Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jwessel/linux-2.6-kgdb

* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jwessel/linux-2.6-kgdb:
  kgdb: Always process the whole breakpoint list on activate or deactivate
  kgdb: continue and warn on signal passing from gdb
  kgdb,x86: do not set kgdb_single_step on x86
  kgdb: allow for cpu switch when single stepping
  kgdb,i386: Fix corner case access to ss with NMI watch dog exception
  kgdb: Replace strstr() by strchr() for single-character needles
  kgdbts: Read buffer overflow
  kgdb: Read buffer overflow
  kgdb,x86: remove redundant test
diff --git a/Documentation/filesystems/nilfs2.txt b/Documentation/filesystems/nilfs2.txt
index 01539f4..4949fca 100644
--- a/Documentation/filesystems/nilfs2.txt
+++ b/Documentation/filesystems/nilfs2.txt
@@ -49,8 +49,7 @@
 NILFS2 supports the following mount options:
 (*) == default
 
-barrier=on(*)		This enables/disables barriers. barrier=off disables
-			it, barrier=on enables it.
+nobarrier		Disables barriers.
 errors=continue(*)	Keep going on a filesystem error.
 errors=remount-ro	Remount the filesystem read-only on an error.
 errors=panic		Panic and halt the machine if an error occurs.
@@ -71,6 +70,10 @@
 			blocks.  That means, it is guaranteed that no
 			overtaking of events occurs in the recovered file
 			system after a crash.
+norecovery		Disable recovery of the filesystem on mount.
+			This disables every write access on the device for
+			read-only mounts or snapshots.  This option will fail
+			for r/w mounts on an unclean volume.
 
 NILFS2 usage
 ============
diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c
index 9a3334a..62619f2 100644
--- a/arch/alpha/kernel/osf_sys.c
+++ b/arch/alpha/kernel/osf_sys.c
@@ -178,25 +178,18 @@
 		unsigned long, prot, unsigned long, flags, unsigned long, fd,
 		unsigned long, off)
 {
-	struct file *file = NULL;
-	unsigned long ret = -EBADF;
+	unsigned long ret = -EINVAL;
 
 #if 0
 	if (flags & (_MAP_HASSEMAPHORE | _MAP_INHERIT | _MAP_UNALIGNED))
 		printk("%s: unimplemented OSF mmap flags %04lx\n", 
 			current->comm, flags);
 #endif
-	if (!(flags & MAP_ANONYMOUS)) {
-		file = fget(fd);
-		if (!file)
-			goto out;
-	}
-	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-	down_write(&current->mm->mmap_sem);
-	ret = do_mmap(file, addr, len, prot, flags, off);
-	up_write(&current->mm->mmap_sem);
-	if (file)
-		fput(file);
+	if ((off + PAGE_ALIGN(len)) < off)
+		goto out;
+	if (off & ~PAGE_MASK)
+		goto out;
+	ret = sys_mmap_pgoff(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
  out:
 	return ret;
 }
diff --git a/arch/arm/include/asm/mman.h b/arch/arm/include/asm/mman.h
index 8eebf89f5..41f99c5 100644
--- a/arch/arm/include/asm/mman.h
+++ b/arch/arm/include/asm/mman.h
@@ -1 +1,4 @@
 #include <asm-generic/mman.h>
+
+#define arch_mmap_check(addr, len, flags) \
+	(((flags) & MAP_FIXED && (addr) < FIRST_USER_ADDRESS) ? -EINVAL : 0)
diff --git a/arch/arm/kernel/calls.S b/arch/arm/kernel/calls.S
index f58c115..9314a2d 100644
--- a/arch/arm/kernel/calls.S
+++ b/arch/arm/kernel/calls.S
@@ -172,7 +172,7 @@
 /* 160 */	CALL(sys_sched_get_priority_min)
 		CALL(sys_sched_rr_get_interval)
 		CALL(sys_nanosleep)
-		CALL(sys_arm_mremap)
+		CALL(sys_mremap)
 		CALL(sys_setresuid16)
 /* 165 */	CALL(sys_getresuid16)
 		CALL(sys_ni_syscall)		/* vm86 */
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
index f0fe95b..2c1db77 100644
--- a/arch/arm/kernel/entry-common.S
+++ b/arch/arm/kernel/entry-common.S
@@ -416,12 +416,12 @@
 		tst	r5, #PGOFF_MASK
 		moveq	r5, r5, lsr #PAGE_SHIFT - 12
 		streq	r5, [sp, #4]
-		beq	do_mmap2
+		beq	sys_mmap_pgoff
 		mov	r0, #-EINVAL
 		mov	pc, lr
 #else
 		str	r5, [sp, #4]
-		b	do_mmap2
+		b	sys_mmap_pgoff
 #endif
 ENDPROC(sys_mmap2)
 
diff --git a/arch/arm/kernel/sys_arm.c b/arch/arm/kernel/sys_arm.c
index 78ecaac..ae4027b 100644
--- a/arch/arm/kernel/sys_arm.c
+++ b/arch/arm/kernel/sys_arm.c
@@ -28,41 +28,6 @@
 #include <linux/ipc.h>
 #include <linux/uaccess.h>
 
-extern unsigned long do_mremap(unsigned long addr, unsigned long old_len,
-			       unsigned long new_len, unsigned long flags,
-			       unsigned long new_addr);
-
-/* common code for old and new mmaps */
-inline long do_mmap2(
-	unsigned long addr, unsigned long len,
-	unsigned long prot, unsigned long flags,
-	unsigned long fd, unsigned long pgoff)
-{
-	int error = -EINVAL;
-	struct file * file = NULL;
-
-	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-
-	if (flags & MAP_FIXED && addr < FIRST_USER_ADDRESS)
-		goto out;
-
-	error = -EBADF;
-	if (!(flags & MAP_ANONYMOUS)) {
-		file = fget(fd);
-		if (!file)
-			goto out;
-	}
-
-	down_write(&current->mm->mmap_sem);
-	error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
-	up_write(&current->mm->mmap_sem);
-
-	if (file)
-		fput(file);
-out:
-	return error;
-}
-
 struct mmap_arg_struct {
 	unsigned long addr;
 	unsigned long len;
@@ -84,29 +49,11 @@
 	if (a.offset & ~PAGE_MASK)
 		goto out;
 
-	error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT);
+	error = sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT);
 out:
 	return error;
 }
 
-asmlinkage unsigned long
-sys_arm_mremap(unsigned long addr, unsigned long old_len,
-	       unsigned long new_len, unsigned long flags,
-	       unsigned long new_addr)
-{
-	unsigned long ret = -EINVAL;
-
-	if (flags & MREMAP_FIXED && new_addr < FIRST_USER_ADDRESS)
-		goto out;
-
-	down_write(&current->mm->mmap_sem);
-	ret = do_mremap(addr, old_len, new_len, flags, new_addr);
-	up_write(&current->mm->mmap_sem);
-
-out:
-	return ret;
-}
-
 /*
  * Perform the select(nd, in, out, ex, tv) and mmap() system
  * calls.
diff --git a/arch/arm/mm/mmap.c b/arch/arm/mm/mmap.c
index 2b79964..f5abc51 100644
--- a/arch/arm/mm/mmap.c
+++ b/arch/arm/mm/mmap.c
@@ -54,7 +54,8 @@
 	 * We enforce the MAP_FIXED case.
 	 */
 	if (flags & MAP_FIXED) {
-		if (aliasing && flags & MAP_SHARED && addr & (SHMLBA - 1))
+		if (aliasing && flags & MAP_SHARED &&
+		    (addr - (pgoff << PAGE_SHIFT)) & (SHMLBA - 1))
 			return -EINVAL;
 		return addr;
 	}
diff --git a/arch/avr32/include/asm/syscalls.h b/arch/avr32/include/asm/syscalls.h
index 483d666..66a1972 100644
--- a/arch/avr32/include/asm/syscalls.h
+++ b/arch/avr32/include/asm/syscalls.h
@@ -29,10 +29,6 @@
 			       struct pt_regs *);
 asmlinkage int sys_rt_sigreturn(struct pt_regs *);
 
-/* kernel/sys_avr32.c */
-asmlinkage long sys_mmap2(unsigned long, unsigned long, unsigned long,
-			  unsigned long, unsigned long, off_t);
-
 /* mm/cache.c */
 asmlinkage int sys_cacheflush(int, void __user *, size_t);
 
diff --git a/arch/avr32/kernel/sys_avr32.c b/arch/avr32/kernel/sys_avr32.c
index 5d2daea..459349b 100644
--- a/arch/avr32/kernel/sys_avr32.c
+++ b/arch/avr32/kernel/sys_avr32.c
@@ -5,39 +5,8 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
-#include <linux/errno.h>
-#include <linux/fs.h>
-#include <linux/file.h>
-#include <linux/mm.h>
 #include <linux/unistd.h>
 
-#include <asm/mman.h>
-#include <asm/uaccess.h>
-#include <asm/syscalls.h>
-
-asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
-			  unsigned long prot, unsigned long flags,
-			  unsigned long fd, off_t offset)
-{
-	int error = -EBADF;
-	struct file *file = NULL;
-
-	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-	if (!(flags & MAP_ANONYMOUS)) {
-		file = fget(fd);
-		if (!file)
-			return error;
-	}
-
-	down_write(&current->mm->mmap_sem);
-	error = do_mmap_pgoff(file, addr, len, prot, flags, offset);
-	up_write(&current->mm->mmap_sem);
-
-	if (file)
-		fput(file);
-	return error;
-}
-
 int kernel_execve(const char *file, char **argv, char **envp)
 {
 	register long scno asm("r8") = __NR_execve;
diff --git a/arch/avr32/kernel/syscall-stubs.S b/arch/avr32/kernel/syscall-stubs.S
index f7244cd..0447a3e 100644
--- a/arch/avr32/kernel/syscall-stubs.S
+++ b/arch/avr32/kernel/syscall-stubs.S
@@ -61,7 +61,7 @@
 __sys_mmap2:
 	pushm	lr
 	st.w	--sp, ARG6
-	call	sys_mmap2
+	call	sys_mmap_pgoff
 	sub	sp, -4
 	popm	pc
 
diff --git a/arch/blackfin/kernel/sys_bfin.c b/arch/blackfin/kernel/sys_bfin.c
index afcef12..2e7f8e1 100644
--- a/arch/blackfin/kernel/sys_bfin.c
+++ b/arch/blackfin/kernel/sys_bfin.c
@@ -22,39 +22,6 @@
 #include <asm/cacheflush.h>
 #include <asm/dma.h>
 
-/* common code for old and new mmaps */
-static inline long
-do_mmap2(unsigned long addr, unsigned long len,
-	 unsigned long prot, unsigned long flags,
-	 unsigned long fd, unsigned long pgoff)
-{
-	int error = -EBADF;
-	struct file *file = NULL;
-
-	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-	if (!(flags & MAP_ANONYMOUS)) {
-		file = fget(fd);
-		if (!file)
-			goto out;
-	}
-
-	down_write(&current->mm->mmap_sem);
-	error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
-	up_write(&current->mm->mmap_sem);
-
-	if (file)
-		fput(file);
- out:
-	return error;
-}
-
-asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
-			  unsigned long prot, unsigned long flags,
-			  unsigned long fd, unsigned long pgoff)
-{
-	return do_mmap2(addr, len, prot, flags, fd, pgoff);
-}
-
 asmlinkage void *sys_sram_alloc(size_t size, unsigned long flags)
 {
 	return sram_alloc_with_lsl(size, flags);
diff --git a/arch/blackfin/mach-common/entry.S b/arch/blackfin/mach-common/entry.S
index a50637a..f3f8bb4 100644
--- a/arch/blackfin/mach-common/entry.S
+++ b/arch/blackfin/mach-common/entry.S
@@ -1422,7 +1422,7 @@
 	.long _sys_ni_syscall	/* streams2 */
 	.long _sys_vfork		/* 190 */
 	.long _sys_getrlimit
-	.long _sys_mmap2
+	.long _sys_mmap_pgoff
 	.long _sys_truncate64
 	.long _sys_ftruncate64
 	.long _sys_stat64	/* 195 */
diff --git a/arch/cris/kernel/sys_cris.c b/arch/cris/kernel/sys_cris.c
index 2ad962c..c2bbb1a 100644
--- a/arch/cris/kernel/sys_cris.c
+++ b/arch/cris/kernel/sys_cris.c
@@ -26,31 +26,6 @@
 #include <asm/uaccess.h>
 #include <asm/segment.h>
 
-/* common code for old and new mmaps */
-static inline long
-do_mmap2(unsigned long addr, unsigned long len, unsigned long prot,
-        unsigned long flags, unsigned long fd, unsigned long pgoff)
-{
-        int error = -EBADF;
-        struct file * file = NULL;
-
-        flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-        if (!(flags & MAP_ANONYMOUS)) {
-                file = fget(fd);
-                if (!file)
-                        goto out;
-        }
-
-        down_write(&current->mm->mmap_sem);
-        error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
-        up_write(&current->mm->mmap_sem);
-
-        if (file)
-                fput(file);
-out:
-        return error;
-}
-
 asmlinkage unsigned long old_mmap(unsigned long __user *args)
 {        
 	unsigned long buffer[6];
@@ -63,7 +38,7 @@
 	if (buffer[5] & ~PAGE_MASK) /* verify that offset is on page boundary */
 		goto out;
 
-	err = do_mmap2(buffer[0], buffer[1], buffer[2], buffer[3],
+	err = sys_mmap_pgoff(buffer[0], buffer[1], buffer[2], buffer[3],
                        buffer[4], buffer[5] >> PAGE_SHIFT);
 out:
 	return err;
@@ -73,7 +48,8 @@
 sys_mmap2(unsigned long addr, unsigned long len, unsigned long prot,
           unsigned long flags, unsigned long fd, unsigned long pgoff)
 {
-        return do_mmap2(addr, len, prot, flags, fd, pgoff);
+	/* bug(?): 8Kb pages here */
+        return sys_mmap_pgoff(addr, len, prot, flags, fd, pgoff);
 }
 
 /*
diff --git a/arch/frv/kernel/sys_frv.c b/arch/frv/kernel/sys_frv.c
index 2b6b528..1d3d4c9 100644
--- a/arch/frv/kernel/sys_frv.c
+++ b/arch/frv/kernel/sys_frv.c
@@ -31,9 +31,6 @@
 			  unsigned long prot, unsigned long flags,
 			  unsigned long fd, unsigned long pgoff)
 {
-	int error = -EBADF;
-	struct file * file = NULL;
-
 	/* As with sparc32, make sure the shift for mmap2 is constant
 	   (12), no matter what PAGE_SIZE we have.... */
 
@@ -41,70 +38,11 @@
 	   trying to map something we can't */
 	if (pgoff & ((1 << (PAGE_SHIFT - 12)) - 1))
 		return -EINVAL;
-	pgoff >>= PAGE_SHIFT - 12;
 
-	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-	if (!(flags & MAP_ANONYMOUS)) {
-		file = fget(fd);
-		if (!file)
-			goto out;
-	}
-
-	down_write(&current->mm->mmap_sem);
-	error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
-	up_write(&current->mm->mmap_sem);
-
-	if (file)
-		fput(file);
-out:
-	return error;
+	return sys_mmap_pgoff(addr, len, prot, flags, fd,
+			      pgoff >> (PAGE_SHIFT - 12));
 }
 
-#if 0 /* DAVIDM - do we want this */
-struct mmap_arg_struct64 {
-	__u32 addr;
-	__u32 len;
-	__u32 prot;
-	__u32 flags;
-	__u64 offset; /* 64 bits */
-	__u32 fd;
-};
-
-asmlinkage long sys_mmap64(struct mmap_arg_struct64 *arg)
-{
-	int error = -EFAULT;
-	struct file * file = NULL;
-	struct mmap_arg_struct64 a;
-	unsigned long pgoff;
-
-	if (copy_from_user(&a, arg, sizeof(a)))
-		return -EFAULT;
-
-	if ((long)a.offset & ~PAGE_MASK)
-		return -EINVAL;
-
-	pgoff = a.offset >> PAGE_SHIFT;
-	if ((a.offset >> PAGE_SHIFT) != pgoff)
-		return -EINVAL;
-
-	if (!(a.flags & MAP_ANONYMOUS)) {
-		error = -EBADF;
-		file = fget(a.fd);
-		if (!file)
-			goto out;
-	}
-	a.flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-
-	down_write(&current->mm->mmap_sem);
-	error = do_mmap_pgoff(file, a.addr, a.len, a.prot, a.flags, pgoff);
-	up_write(&current->mm->mmap_sem);
-	if (file)
-		fput(file);
-out:
-	return error;
-}
-#endif
-
 /*
  * sys_ipc() is the de-multiplexer for the SysV IPC calls..
  *
diff --git a/arch/h8300/kernel/sys_h8300.c b/arch/h8300/kernel/sys_h8300.c
index 8cb5d73..b5969db 100644
--- a/arch/h8300/kernel/sys_h8300.c
+++ b/arch/h8300/kernel/sys_h8300.c
@@ -26,39 +26,6 @@
 #include <asm/traps.h>
 #include <asm/unistd.h>
 
-/* common code for old and new mmaps */
-static inline long do_mmap2(
-	unsigned long addr, unsigned long len,
-	unsigned long prot, unsigned long flags,
-	unsigned long fd, unsigned long pgoff)
-{
-	int error = -EBADF;
-	struct file * file = NULL;
-
-	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-	if (!(flags & MAP_ANONYMOUS)) {
-		file = fget(fd);
-		if (!file)
-			goto out;
-	}
-
-	down_write(&current->mm->mmap_sem);
-	error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
-	up_write(&current->mm->mmap_sem);
-
-	if (file)
-		fput(file);
-out:
-	return error;
-}
-
-asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
-	unsigned long prot, unsigned long flags,
-	unsigned long fd, unsigned long pgoff)
-{
-	return do_mmap2(addr, len, prot, flags, fd, pgoff);
-}
-
 /*
  * Perform the select(nd, in, out, ex, tv) and mmap() system
  * calls. Linux/m68k cloned Linux/i386, which didn't use to be able to
@@ -87,58 +54,12 @@
 	if (a.offset & ~PAGE_MASK)
 		goto out;
 
-	a.flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-
-	error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT);
+	error = sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd,
+			       a.offset >> PAGE_SHIFT);
 out:
 	return error;
 }
 
-#if 0 /* DAVIDM - do we want this */
-struct mmap_arg_struct64 {
-	__u32 addr;
-	__u32 len;
-	__u32 prot;
-	__u32 flags;
-	__u64 offset; /* 64 bits */
-	__u32 fd;
-};
-
-asmlinkage long sys_mmap64(struct mmap_arg_struct64 *arg)
-{
-	int error = -EFAULT;
-	struct file * file = NULL;
-	struct mmap_arg_struct64 a;
-	unsigned long pgoff;
-
-	if (copy_from_user(&a, arg, sizeof(a)))
-		return -EFAULT;
-
-	if ((long)a.offset & ~PAGE_MASK)
-		return -EINVAL;
-
-	pgoff = a.offset >> PAGE_SHIFT;
-	if ((a.offset >> PAGE_SHIFT) != pgoff)
-		return -EINVAL;
-
-	if (!(a.flags & MAP_ANONYMOUS)) {
-		error = -EBADF;
-		file = fget(a.fd);
-		if (!file)
-			goto out;
-	}
-	a.flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-
-	down_write(&current->mm->mmap_sem);
-	error = do_mmap_pgoff(file, a.addr, a.len, a.prot, a.flags, pgoff);
-	up_write(&current->mm->mmap_sem);
-	if (file)
-		fput(file);
-out:
-	return error;
-}
-#endif
-
 struct sel_arg_struct {
 	unsigned long n;
 	fd_set *inp, *outp, *exp;
diff --git a/arch/h8300/kernel/syscalls.S b/arch/h8300/kernel/syscalls.S
index 4eb67fa..2d69881 100644
--- a/arch/h8300/kernel/syscalls.S
+++ b/arch/h8300/kernel/syscalls.S
@@ -206,7 +206,7 @@
 	.long SYMBOL_NAME(sys_ni_syscall)	/* streams2 */
 	.long SYMBOL_NAME(sys_vfork)            /* 190 */
 	.long SYMBOL_NAME(sys_getrlimit)
-	.long SYMBOL_NAME(sys_mmap2)
+	.long SYMBOL_NAME(sys_mmap_pgoff)
 	.long SYMBOL_NAME(sys_truncate64)
 	.long SYMBOL_NAME(sys_ftruncate64)
 	.long SYMBOL_NAME(sys_stat64)		/* 195 */
diff --git a/arch/ia64/ia32/sys_ia32.c b/arch/ia64/ia32/sys_ia32.c
index 429ec96..045b746b 100644
--- a/arch/ia64/ia32/sys_ia32.c
+++ b/arch/ia64/ia32/sys_ia32.c
@@ -858,6 +858,9 @@
 
 	prot = get_prot32(prot);
 
+	if (flags & MAP_HUGETLB)
+		return -ENOMEM;
+
 #if PAGE_SHIFT > IA32_PAGE_SHIFT
 	mutex_lock(&ia32_mmap_mutex);
 	{
diff --git a/arch/ia64/include/asm/xen/hypervisor.h b/arch/ia64/include/asm/xen/hypervisor.h
index 88afb54..67455c2 100644
--- a/arch/ia64/include/asm/xen/hypervisor.h
+++ b/arch/ia64/include/asm/xen/hypervisor.h
@@ -37,35 +37,9 @@
 #include <xen/interface/xen.h>
 #include <xen/interface/version.h>	/* to compile feature.c */
 #include <xen/features.h>		/* to comiple xen-netfront.c */
+#include <xen/xen.h>
 #include <asm/xen/hypercall.h>
 
-/* xen_domain_type is set before executing any C code by early_xen_setup */
-enum xen_domain_type {
-	XEN_NATIVE,	/* running on bare hardware */
-	XEN_PV_DOMAIN,	/* running in a PV domain */
-	XEN_HVM_DOMAIN,	/* running in a Xen hvm domain*/
-};
-
-#ifdef CONFIG_XEN
-extern enum xen_domain_type xen_domain_type;
-#else
-#define xen_domain_type		XEN_NATIVE
-#endif
-
-#define xen_domain()		(xen_domain_type != XEN_NATIVE)
-#define xen_pv_domain()		(xen_domain() &&			\
-				 xen_domain_type == XEN_PV_DOMAIN)
-#define xen_hvm_domain()	(xen_domain() &&			\
-				 xen_domain_type == XEN_HVM_DOMAIN)
-
-#ifdef CONFIG_XEN_DOM0
-#define xen_initial_domain()	(xen_pv_domain() &&			\
-				 (xen_start_info->flags & SIF_INITDOMAIN))
-#else
-#define xen_initial_domain()	(0)
-#endif
-
-
 #ifdef CONFIG_XEN
 extern struct shared_info *HYPERVISOR_shared_info;
 extern struct start_info *xen_start_info;
diff --git a/arch/ia64/kernel/sys_ia64.c b/arch/ia64/kernel/sys_ia64.c
index 92ed83f..609d500 100644
--- a/arch/ia64/kernel/sys_ia64.c
+++ b/arch/ia64/kernel/sys_ia64.c
@@ -100,51 +100,7 @@
 asmlinkage unsigned long
 ia64_brk (unsigned long brk)
 {
-	unsigned long rlim, retval, newbrk, oldbrk;
-	struct mm_struct *mm = current->mm;
-
-	/*
-	 * Most of this replicates the code in sys_brk() except for an additional safety
-	 * check and the clearing of r8.  However, we can't call sys_brk() because we need
-	 * to acquire the mmap_sem before we can do the test...
-	 */
-	down_write(&mm->mmap_sem);
-
-	if (brk < mm->end_code)
-		goto out;
-	newbrk = PAGE_ALIGN(brk);
-	oldbrk = PAGE_ALIGN(mm->brk);
-	if (oldbrk == newbrk)
-		goto set_brk;
-
-	/* Always allow shrinking brk. */
-	if (brk <= mm->brk) {
-		if (!do_munmap(mm, newbrk, oldbrk-newbrk))
-			goto set_brk;
-		goto out;
-	}
-
-	/* Check against unimplemented/unmapped addresses: */
-	if ((newbrk - oldbrk) > RGN_MAP_LIMIT || REGION_OFFSET(newbrk) > RGN_MAP_LIMIT)
-		goto out;
-
-	/* Check against rlimit.. */
-	rlim = current->signal->rlim[RLIMIT_DATA].rlim_cur;
-	if (rlim < RLIM_INFINITY && brk - mm->start_data > rlim)
-		goto out;
-
-	/* Check against existing mmap mappings. */
-	if (find_vma_intersection(mm, oldbrk, newbrk+PAGE_SIZE))
-		goto out;
-
-	/* Ok, looks good - let it rip. */
-	if (do_brk(oldbrk, newbrk-oldbrk) != oldbrk)
-		goto out;
-set_brk:
-	mm->brk = brk;
-out:
-	retval = mm->brk;
-	up_write(&mm->mmap_sem);
+	unsigned long retval = sys_brk(brk);
 	force_successful_syscall_return();
 	return retval;
 }
@@ -185,39 +141,6 @@
 	return 0;
 }
 
-static inline unsigned long
-do_mmap2 (unsigned long addr, unsigned long len, int prot, int flags, int fd, unsigned long pgoff)
-{
-	struct file *file = NULL;
-
-	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-	if (!(flags & MAP_ANONYMOUS)) {
-		file = fget(fd);
-		if (!file)
-			return -EBADF;
-
-		if (!file->f_op || !file->f_op->mmap) {
-			addr = -ENODEV;
-			goto out;
-		}
-	}
-
-	/* Careful about overflows.. */
-	len = PAGE_ALIGN(len);
-	if (!len || len > TASK_SIZE) {
-		addr = -EINVAL;
-		goto out;
-	}
-
-	down_write(&current->mm->mmap_sem);
-	addr = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
-	up_write(&current->mm->mmap_sem);
-
-out:	if (file)
-		fput(file);
-	return addr;
-}
-
 /*
  * mmap2() is like mmap() except that the offset is expressed in units
  * of PAGE_SIZE (instead of bytes).  This allows to mmap2() (pieces
@@ -226,7 +149,7 @@
 asmlinkage unsigned long
 sys_mmap2 (unsigned long addr, unsigned long len, int prot, int flags, int fd, long pgoff)
 {
-	addr = do_mmap2(addr, len, prot, flags, fd, pgoff);
+	addr = sys_mmap_pgoff(addr, len, prot, flags, fd, pgoff);
 	if (!IS_ERR((void *) addr))
 		force_successful_syscall_return();
 	return addr;
@@ -238,7 +161,7 @@
 	if (offset_in_page(off) != 0)
 		return -EINVAL;
 
-	addr = do_mmap2(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
+	addr = sys_mmap_pgoff(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
 	if (!IS_ERR((void *) addr))
 		force_successful_syscall_return();
 	return addr;
diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c
index c0fca2c..df639db7 100644
--- a/arch/ia64/pci/pci.c
+++ b/arch/ia64/pci/pci.c
@@ -131,6 +131,7 @@
 }
 
 struct pci_root_info {
+	struct acpi_device *bridge;
 	struct pci_controller *controller;
 	char *name;
 };
@@ -297,9 +298,20 @@
 	window->offset = offset;
 
 	if (insert_resource(root, &window->resource)) {
-		printk(KERN_ERR "alloc 0x%llx-0x%llx from %s for %s failed\n",
-			window->resource.start, window->resource.end,
-			root->name, info->name);
+		dev_err(&info->bridge->dev,
+			"can't allocate host bridge window %pR\n",
+			&window->resource);
+	} else {
+		if (offset)
+			dev_info(&info->bridge->dev, "host bridge window %pR "
+				 "(PCI address [%#llx-%#llx])\n",
+				 &window->resource,
+				 window->resource.start - offset,
+				 window->resource.end - offset);
+		else
+			dev_info(&info->bridge->dev,
+				 "host bridge window %pR\n",
+				 &window->resource);
 	}
 
 	return AE_OK;
@@ -319,8 +331,9 @@
 		    (res->end - res->start < 16))
 			continue;
 		if (j >= PCI_BUS_NUM_RESOURCES) {
-			printk("Ignoring range [%#llx-%#llx] (%lx)\n",
-					res->start, res->end, res->flags);
+			dev_warn(&bus->dev,
+				 "ignoring host bridge window %pR (no space)\n",
+				 res);
 			continue;
 		}
 		bus->resource[j++] = res;
@@ -364,6 +377,7 @@
 			goto out3;
 
 		sprintf(name, "PCI Bus %04x:%02x", domain, bus);
+		info.bridge = device;
 		info.controller = controller;
 		info.name = name;
 		acpi_walk_resources(device->handle, METHOD_NAME__CRS,
@@ -720,9 +734,6 @@
 	return ret;
 }
 
-/* It's defined in drivers/pci/pci.c */
-extern u8 pci_cache_line_size;
-
 /**
  * set_pci_cacheline_size - determine cacheline size for PCI devices
  *
@@ -731,7 +742,7 @@
  *
  * Code mostly taken from arch/ia64/kernel/palinfo.c:cache_info().
  */
-static void __init set_pci_cacheline_size(void)
+static void __init set_pci_dfl_cacheline_size(void)
 {
 	unsigned long levels, unique_caches;
 	long status;
@@ -751,7 +762,7 @@
 			"(status=%ld)\n", __func__, status);
 		return;
 	}
-	pci_cache_line_size = (1 << cci.pcci_line_size) / 4;
+	pci_dfl_cache_line_size = (1 << cci.pcci_line_size) / 4;
 }
 
 u64 ia64_dma_get_required_mask(struct device *dev)
@@ -782,7 +793,7 @@
 
 static int __init pcibios_init(void)
 {
-	set_pci_cacheline_size();
+	set_pci_dfl_cacheline_size();
 	return 0;
 }
 
diff --git a/arch/m32r/kernel/sys_m32r.c b/arch/m32r/kernel/sys_m32r.c
index 305ac85..d3c865c 100644
--- a/arch/m32r/kernel/sys_m32r.c
+++ b/arch/m32r/kernel/sys_m32r.c
@@ -76,30 +76,6 @@
 	return oldval;
 }
 
-asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
-	unsigned long prot, unsigned long flags,
-	unsigned long fd, unsigned long pgoff)
-{
-	int error = -EBADF;
-	struct file *file = NULL;
-
-	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-	if (!(flags & MAP_ANONYMOUS)) {
-		file = fget(fd);
-		if (!file)
-			goto out;
-	}
-
-	down_write(&current->mm->mmap_sem);
-	error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
-	up_write(&current->mm->mmap_sem);
-
-	if (file)
-		fput(file);
-out:
-	return error;
-}
-
 /*
  * sys_ipc() is the de-multiplexer for the SysV IPC calls..
  *
diff --git a/arch/m32r/kernel/syscall_table.S b/arch/m32r/kernel/syscall_table.S
index aa3bf4c..60536e2 100644
--- a/arch/m32r/kernel/syscall_table.S
+++ b/arch/m32r/kernel/syscall_table.S
@@ -191,7 +191,7 @@
 	.long sys_ni_syscall		/* streams2 */
 	.long sys_vfork			/* 190 */
 	.long sys_getrlimit
-	.long sys_mmap2
+	.long sys_mmap_pgoff
 	.long sys_truncate64
 	.long sys_ftruncate64
 	.long sys_stat64		/* 195 */
diff --git a/arch/m68k/kernel/sys_m68k.c b/arch/m68k/kernel/sys_m68k.c
index 7deb402..218f441 100644
--- a/arch/m68k/kernel/sys_m68k.c
+++ b/arch/m68k/kernel/sys_m68k.c
@@ -29,37 +29,16 @@
 #include <asm/page.h>
 #include <asm/unistd.h>
 
-/* common code for old and new mmaps */
-static inline long do_mmap2(
-	unsigned long addr, unsigned long len,
-	unsigned long prot, unsigned long flags,
-	unsigned long fd, unsigned long pgoff)
-{
-	int error = -EBADF;
-	struct file * file = NULL;
-
-	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-	if (!(flags & MAP_ANONYMOUS)) {
-		file = fget(fd);
-		if (!file)
-			goto out;
-	}
-
-	down_write(&current->mm->mmap_sem);
-	error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
-	up_write(&current->mm->mmap_sem);
-
-	if (file)
-		fput(file);
-out:
-	return error;
-}
-
 asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
 	unsigned long prot, unsigned long flags,
 	unsigned long fd, unsigned long pgoff)
 {
-	return do_mmap2(addr, len, prot, flags, fd, pgoff);
+	/*
+	 * This is wrong for sun3 - there PAGE_SIZE is 8Kb,
+	 * so we need to shift the argument down by 1; m68k mmap64(3)
+	 * (in libc) expects the last argument of mmap2 in 4Kb units.
+	 */
+	return sys_mmap_pgoff(addr, len, prot, flags, fd, pgoff);
 }
 
 /*
@@ -90,58 +69,12 @@
 	if (a.offset & ~PAGE_MASK)
 		goto out;
 
-	a.flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-
-	error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT);
+	error = sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd,
+			       a.offset >> PAGE_SHIFT);
 out:
 	return error;
 }
 
-#if 0
-struct mmap_arg_struct64 {
-	__u32 addr;
-	__u32 len;
-	__u32 prot;
-	__u32 flags;
-	__u64 offset; /* 64 bits */
-	__u32 fd;
-};
-
-asmlinkage long sys_mmap64(struct mmap_arg_struct64 *arg)
-{
-	int error = -EFAULT;
-	struct file * file = NULL;
-	struct mmap_arg_struct64 a;
-	unsigned long pgoff;
-
-	if (copy_from_user(&a, arg, sizeof(a)))
-		return -EFAULT;
-
-	if ((long)a.offset & ~PAGE_MASK)
-		return -EINVAL;
-
-	pgoff = a.offset >> PAGE_SHIFT;
-	if ((a.offset >> PAGE_SHIFT) != pgoff)
-		return -EINVAL;
-
-	if (!(a.flags & MAP_ANONYMOUS)) {
-		error = -EBADF;
-		file = fget(a.fd);
-		if (!file)
-			goto out;
-	}
-	a.flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-
-	down_write(&current->mm->mmap_sem);
-	error = do_mmap_pgoff(file, a.addr, a.len, a.prot, a.flags, pgoff);
-	up_write(&current->mm->mmap_sem);
-	if (file)
-		fput(file);
-out:
-	return error;
-}
-#endif
-
 struct sel_arg_struct {
 	unsigned long n;
 	fd_set __user *inp, *outp, *exp;
diff --git a/arch/m68knommu/kernel/sys_m68k.c b/arch/m68knommu/kernel/sys_m68k.c
index efdd090..b67cbc7 100644
--- a/arch/m68knommu/kernel/sys_m68k.c
+++ b/arch/m68knommu/kernel/sys_m68k.c
@@ -27,39 +27,6 @@
 #include <asm/cacheflush.h>
 #include <asm/unistd.h>
 
-/* common code for old and new mmaps */
-static inline long do_mmap2(
-	unsigned long addr, unsigned long len,
-	unsigned long prot, unsigned long flags,
-	unsigned long fd, unsigned long pgoff)
-{
-	int error = -EBADF;
-	struct file * file = NULL;
-
-	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-	if (!(flags & MAP_ANONYMOUS)) {
-		file = fget(fd);
-		if (!file)
-			goto out;
-	}
-
-	down_write(&current->mm->mmap_sem);
-	error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
-	up_write(&current->mm->mmap_sem);
-
-	if (file)
-		fput(file);
-out:
-	return error;
-}
-
-asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
-	unsigned long prot, unsigned long flags,
-	unsigned long fd, unsigned long pgoff)
-{
-	return do_mmap2(addr, len, prot, flags, fd, pgoff);
-}
-
 /*
  * Perform the select(nd, in, out, ex, tv) and mmap() system
  * calls. Linux/m68k cloned Linux/i386, which didn't use to be able to
@@ -88,9 +55,8 @@
 	if (a.offset & ~PAGE_MASK)
 		goto out;
 
-	a.flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-
-	error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT);
+	error = sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd,
+				a.offset >> PAGE_SHIFT);
 out:
 	return error;
 }
diff --git a/arch/m68knommu/kernel/syscalltable.S b/arch/m68knommu/kernel/syscalltable.S
index 23535cc..486837e 100644
--- a/arch/m68knommu/kernel/syscalltable.S
+++ b/arch/m68knommu/kernel/syscalltable.S
@@ -210,7 +210,7 @@
 	.long sys_ni_syscall	/* streams2 */
 	.long sys_vfork		/* 190 */
 	.long sys_getrlimit
-	.long sys_mmap2
+	.long sys_mmap_pgoff
 	.long sys_truncate64
 	.long sys_ftruncate64
 	.long sys_stat64	/* 195 */
diff --git a/arch/microblaze/kernel/sys_microblaze.c b/arch/microblaze/kernel/sys_microblaze.c
index 07cabed..9f3c205 100644
--- a/arch/microblaze/kernel/sys_microblaze.c
+++ b/arch/microblaze/kernel/sys_microblaze.c
@@ -62,46 +62,14 @@
 	return error;
 }
 
-asmlinkage long
-sys_mmap2(unsigned long addr, unsigned long len,
-	unsigned long prot, unsigned long flags,
-	unsigned long fd, unsigned long pgoff)
-{
-	struct file *file = NULL;
-	int ret = -EBADF;
-
-	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-	if (!(flags & MAP_ANONYMOUS)) {
-		file = fget(fd);
-		if (!file) {
-			printk(KERN_INFO "no fd in mmap\r\n");
-			goto out;
-		}
-	}
-
-	down_write(&current->mm->mmap_sem);
-	ret = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
-	up_write(&current->mm->mmap_sem);
-	if (file)
-		fput(file);
-out:
-	return ret;
-}
-
 asmlinkage long sys_mmap(unsigned long addr, unsigned long len,
 			unsigned long prot, unsigned long flags,
 			unsigned long fd, off_t pgoff)
 {
-	int err = -EINVAL;
+	if (pgoff & ~PAGE_MASK)
+		return -EINVAL;
 
-	if (pgoff & ~PAGE_MASK) {
-		printk(KERN_INFO "no pagemask in mmap\r\n");
-		goto out;
-	}
-
-	err = sys_mmap2(addr, len, prot, flags, fd, pgoff >> PAGE_SHIFT);
-out:
-	return err;
+	return sys_mmap_pgoff(addr, len, prot, flags, fd, pgoff >> PAGE_SHIFT);
 }
 
 /*
diff --git a/arch/microblaze/kernel/syscall_table.S b/arch/microblaze/kernel/syscall_table.S
index c1ab1dc..b96f365 100644
--- a/arch/microblaze/kernel/syscall_table.S
+++ b/arch/microblaze/kernel/syscall_table.S
@@ -196,7 +196,7 @@
 	.long sys_ni_syscall		/* reserved for streams2 */
 	.long sys_vfork		/* 190 */
 	.long sys_getrlimit
-	.long sys_mmap2			/* mmap2 */
+	.long sys_mmap_pgoff		/* mmap2 */
 	.long sys_truncate64
 	.long sys_ftruncate64
 	.long sys_stat64		/* 195 */
diff --git a/arch/mips/kernel/linux32.c b/arch/mips/kernel/linux32.c
index 1a2793e..f042563 100644
--- a/arch/mips/kernel/linux32.c
+++ b/arch/mips/kernel/linux32.c
@@ -67,28 +67,13 @@
 	unsigned long, prot, unsigned long, flags, unsigned long, fd,
 	unsigned long, pgoff)
 {
-	struct file * file = NULL;
 	unsigned long error;
 
 	error = -EINVAL;
 	if (pgoff & (~PAGE_MASK >> 12))
 		goto out;
-	pgoff >>= PAGE_SHIFT-12;
-
-	if (!(flags & MAP_ANONYMOUS)) {
-		error = -EBADF;
-		file = fget(fd);
-		if (!file)
-			goto out;
-	}
-	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-
-	down_write(&current->mm->mmap_sem);
-	error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
-	up_write(&current->mm->mmap_sem);
-	if (file)
-		fput(file);
-
+	error = sys_mmap_pgoff(addr, len, prot, flags, fd,
+			       pgoff >> (PAGE_SHIFT-12));
 out:
 	return error;
 }
diff --git a/arch/mips/kernel/syscall.c b/arch/mips/kernel/syscall.c
index fe0d798..3f7f466 100644
--- a/arch/mips/kernel/syscall.c
+++ b/arch/mips/kernel/syscall.c
@@ -93,7 +93,8 @@
 		 * We do not accept a shared mapping if it would violate
 		 * cache aliasing constraints.
 		 */
-		if ((flags & MAP_SHARED) && (addr & shm_align_mask))
+		if ((flags & MAP_SHARED) &&
+		    ((addr - (pgoff << PAGE_SHIFT)) & shm_align_mask))
 			return -EINVAL;
 		return addr;
 	}
@@ -129,31 +130,6 @@
 	}
 }
 
-/* common code for old and new mmaps */
-static inline unsigned long
-do_mmap2(unsigned long addr, unsigned long len, unsigned long prot,
-        unsigned long flags, unsigned long fd, unsigned long pgoff)
-{
-	unsigned long error = -EBADF;
-	struct file * file = NULL;
-
-	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-	if (!(flags & MAP_ANONYMOUS)) {
-		file = fget(fd);
-		if (!file)
-			goto out;
-	}
-
-	down_write(&current->mm->mmap_sem);
-	error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
-	up_write(&current->mm->mmap_sem);
-
-	if (file)
-		fput(file);
-out:
-	return error;
-}
-
 SYSCALL_DEFINE6(mips_mmap, unsigned long, addr, unsigned long, len,
 	unsigned long, prot, unsigned long, flags, unsigned long,
 	fd, off_t, offset)
@@ -164,7 +140,7 @@
 	if (offset & ~PAGE_MASK)
 		goto out;
 
-	result = do_mmap2(addr, len, prot, flags, fd, offset >> PAGE_SHIFT);
+	result = sys_mmap_pgoff(addr, len, prot, flags, fd, offset >> PAGE_SHIFT);
 
 out:
 	return result;
@@ -177,7 +153,7 @@
 	if (pgoff & (~PAGE_MASK >> 12))
 		return -EINVAL;
 
-	return do_mmap2(addr, len, prot, flags, fd, pgoff >> (PAGE_SHIFT-12));
+	return sys_mmap_pgoff(addr, len, prot, flags, fd, pgoff >> (PAGE_SHIFT-12));
 }
 
 save_static_function(sys_fork);
diff --git a/arch/mn10300/include/asm/mman.h b/arch/mn10300/include/asm/mman.h
index 8eebf89f5..db5c53da 100644
--- a/arch/mn10300/include/asm/mman.h
+++ b/arch/mn10300/include/asm/mman.h
@@ -1 +1,6 @@
 #include <asm-generic/mman.h>
+
+#define MIN_MAP_ADDR	PAGE_SIZE	/* minimum fixed mmap address */
+
+#define arch_mmap_check(addr, len, flags) \
+	(((flags) & MAP_FIXED && (addr) < MIN_MAP_ADDR) ? -EINVAL : 0)
diff --git a/arch/mn10300/kernel/entry.S b/arch/mn10300/kernel/entry.S
index a94e7ea..c9ee6c0 100644
--- a/arch/mn10300/kernel/entry.S
+++ b/arch/mn10300/kernel/entry.S
@@ -578,7 +578,7 @@
 	.long sys_ni_syscall	/* reserved for streams2 */
 	.long sys_vfork		/* 190 */
 	.long sys_getrlimit
-	.long sys_mmap2
+	.long sys_mmap_pgoff
 	.long sys_truncate64
 	.long sys_ftruncate64
 	.long sys_stat64	/* 195 */
diff --git a/arch/mn10300/kernel/sys_mn10300.c b/arch/mn10300/kernel/sys_mn10300.c
index 8ca5af0..17cc6ce 100644
--- a/arch/mn10300/kernel/sys_mn10300.c
+++ b/arch/mn10300/kernel/sys_mn10300.c
@@ -23,47 +23,13 @@
 
 #include <asm/uaccess.h>
 
-#define MIN_MAP_ADDR	PAGE_SIZE	/* minimum fixed mmap address */
-
-/*
- * memory mapping syscall
- */
-asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
-			  unsigned long prot, unsigned long flags,
-			  unsigned long fd, unsigned long pgoff)
-{
-	struct file *file = NULL;
-	long error = -EINVAL;
-
-	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-
-	if (flags & MAP_FIXED && addr < MIN_MAP_ADDR)
-		goto out;
-
-	error = -EBADF;
-	if (!(flags & MAP_ANONYMOUS)) {
-		file = fget(fd);
-		if (!file)
-			goto out;
-	}
-
-	down_write(&current->mm->mmap_sem);
-	error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
-	up_write(&current->mm->mmap_sem);
-
-	if (file)
-		fput(file);
-out:
-	return error;
-}
-
 asmlinkage long old_mmap(unsigned long addr, unsigned long len,
 			 unsigned long prot, unsigned long flags,
 			 unsigned long fd, unsigned long offset)
 {
 	if (offset & ~PAGE_MASK)
 		return -EINVAL;
-	return sys_mmap2(addr, len, prot, flags, fd, offset >> PAGE_SHIFT);
+	return sys_mmap_pgoff(addr, len, prot, flags, fd, offset >> PAGE_SHIFT);
 }
 
 struct sel_arg_struct {
diff --git a/arch/parisc/kernel/sys_parisc.c b/arch/parisc/kernel/sys_parisc.c
index 71b3195..9147391 100644
--- a/arch/parisc/kernel/sys_parisc.c
+++ b/arch/parisc/kernel/sys_parisc.c
@@ -110,37 +110,14 @@
 	return addr;
 }
 
-static unsigned long do_mmap2(unsigned long addr, unsigned long len,
-	unsigned long prot, unsigned long flags, unsigned long fd,
-	unsigned long pgoff)
-{
-	struct file * file = NULL;
-	unsigned long error = -EBADF;
-	if (!(flags & MAP_ANONYMOUS)) {
-		file = fget(fd);
-		if (!file)
-			goto out;
-	}
-
-	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-
-	down_write(&current->mm->mmap_sem);
-	error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
-	up_write(&current->mm->mmap_sem);
-
-	if (file != NULL)
-		fput(file);
-out:
-	return error;
-}
-
 asmlinkage unsigned long sys_mmap2(unsigned long addr, unsigned long len,
 	unsigned long prot, unsigned long flags, unsigned long fd,
 	unsigned long pgoff)
 {
 	/* Make sure the shift for mmap2 is constant (12), no matter what PAGE_SIZE
 	   we have. */
-	return do_mmap2(addr, len, prot, flags, fd, pgoff >> (PAGE_SHIFT - 12));
+	return sys_mmap_pgoff(addr, len, prot, flags, fd,
+			      pgoff >> (PAGE_SHIFT - 12));
 }
 
 asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len,
@@ -148,7 +125,8 @@
 		unsigned long offset)
 {
 	if (!(offset & ~PAGE_MASK)) {
-		return do_mmap2(addr, len, prot, flags, fd, offset >> PAGE_SHIFT);
+		return sys_mmap_pgoff(addr, len, prot, flags, fd,
+					offset >> PAGE_SHIFT);
 	} else {
 		return -EINVAL;
 	}
diff --git a/arch/powerpc/kernel/syscalls.c b/arch/powerpc/kernel/syscalls.c
index c04832c..3370e62 100644
--- a/arch/powerpc/kernel/syscalls.c
+++ b/arch/powerpc/kernel/syscalls.c
@@ -140,7 +140,6 @@
 			unsigned long prot, unsigned long flags,
 			unsigned long fd, unsigned long off, int shift)
 {
-	struct file * file = NULL;
 	unsigned long ret = -EINVAL;
 
 	if (!arch_validate_prot(prot))
@@ -151,20 +150,8 @@
 			goto out;
 		off >>= shift;
 	}
-		
-	ret = -EBADF;
-	if (!(flags & MAP_ANONYMOUS)) {
-		if (!(file = fget(fd)))
-			goto out;
-	}
 
-	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-
-	down_write(&current->mm->mmap_sem);
-	ret = do_mmap_pgoff(file, addr, len, prot, flags, off);
-	up_write(&current->mm->mmap_sem);
-	if (file)
-		fput(file);
+	ret = sys_mmap_pgoff(addr, len, prot, flags, fd, off);
 out:
 	return ret;
 }
diff --git a/arch/s390/kernel/compat_linux.c b/arch/s390/kernel/compat_linux.c
index 25c31d6..22c9e55 100644
--- a/arch/s390/kernel/compat_linux.c
+++ b/arch/s390/kernel/compat_linux.c
@@ -624,38 +624,6 @@
 	u32	offset;
 };
 
-/* common code for old and new mmaps */
-static inline long do_mmap2(
-	unsigned long addr, unsigned long len,
-	unsigned long prot, unsigned long flags,
-	unsigned long fd, unsigned long pgoff)
-{
-	struct file * file = NULL;
-	unsigned long error = -EBADF;
-
-	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-	if (!(flags & MAP_ANONYMOUS)) {
-		file = fget(fd);
-		if (!file)
-			goto out;
-	}
-
-	down_write(&current->mm->mmap_sem);
-	error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
-	if (!IS_ERR((void *) error) && error + len >= 0x80000000ULL) {
-		/* Result is out of bounds.  */
-		do_munmap(current->mm, addr, len);
-		error = -ENOMEM;
-	}
-	up_write(&current->mm->mmap_sem);
-
-	if (file)
-		fput(file);
-out:    
-	return error;
-}
-
-
 asmlinkage unsigned long
 old32_mmap(struct mmap_arg_struct_emu31 __user *arg)
 {
@@ -669,7 +637,8 @@
 	if (a.offset & ~PAGE_MASK)
 		goto out;
 
-	error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT); 
+	error = sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd,
+			       a.offset >> PAGE_SHIFT);
 out:
 	return error;
 }
@@ -682,7 +651,7 @@
 
 	if (copy_from_user(&a, arg, sizeof(a)))
 		goto out;
-	error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset);
+	error = sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd, a.offset);
 out:
 	return error;
 }
diff --git a/arch/s390/kernel/sys_s390.c b/arch/s390/kernel/sys_s390.c
index e9d94f6..86a74c9 100644
--- a/arch/s390/kernel/sys_s390.c
+++ b/arch/s390/kernel/sys_s390.c
@@ -32,32 +32,6 @@
 #include <asm/uaccess.h>
 #include "entry.h"
 
-/* common code for old and new mmaps */
-static inline long do_mmap2(
-	unsigned long addr, unsigned long len,
-	unsigned long prot, unsigned long flags,
-	unsigned long fd, unsigned long pgoff)
-{
-	long error = -EBADF;
-	struct file * file = NULL;
-
-	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-	if (!(flags & MAP_ANONYMOUS)) {
-		file = fget(fd);
-		if (!file)
-			goto out;
-	}
-
-	down_write(&current->mm->mmap_sem);
-	error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
-	up_write(&current->mm->mmap_sem);
-
-	if (file)
-		fput(file);
-out:
-	return error;
-}
-
 /*
  * Perform the select(nd, in, out, ex, tv) and mmap() system
  * calls. Linux for S/390 isn't able to handle more than 5
@@ -81,7 +55,7 @@
 
 	if (copy_from_user(&a, arg, sizeof(a)))
 		goto out;
-	error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset);
+	error = sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd, a.offset);
 out:
 	return error;
 }
@@ -98,7 +72,7 @@
 	if (a.offset & ~PAGE_MASK)
 		goto out;
 
-	error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT);
+	error = sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT);
 out:
 	return error;
 }
diff --git a/arch/score/kernel/sys_score.c b/arch/score/kernel/sys_score.c
index 0012494..856ed68 100644
--- a/arch/score/kernel/sys_score.c
+++ b/arch/score/kernel/sys_score.c
@@ -36,34 +36,16 @@
 sys_mmap2(unsigned long addr, unsigned long len, unsigned long prot,
 	  unsigned long flags, unsigned long fd, unsigned long pgoff)
 {
-	int error = -EBADF;
-	struct file *file = NULL;
-
-	if (pgoff & (~PAGE_MASK >> 12))
-		return -EINVAL;
-
-	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-	if (!(flags & MAP_ANONYMOUS)) {
-		file = fget(fd);
-		if (!file)
-			return error;
-	}
-
-	down_write(&current->mm->mmap_sem);
-	error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
-	up_write(&current->mm->mmap_sem);
-
-	if (file)
-		fput(file);
-
-	return error;
+	return sys_mmap_pgoff(addr, len, prot, flags, fd, pgoff);
 }
 
 asmlinkage long
 sys_mmap(unsigned long addr, unsigned long len, unsigned long prot,
-	unsigned long flags, unsigned long fd, off_t pgoff)
+	unsigned long flags, unsigned long fd, off_t offset)
 {
-	return sys_mmap2(addr, len, prot, flags, fd, pgoff >> PAGE_SHIFT);
+	if (unlikely(offset & ~PAGE_MASK))
+		return -EINVAL;
+	return sys_mmap_pgoff(addr, len, prot, flags, fd, offset >> PAGE_SHIFT);
 }
 
 asmlinkage long
diff --git a/arch/sh/kernel/sys_sh.c b/arch/sh/kernel/sys_sh.c
index 8aa5d1c..71399cd 100644
--- a/arch/sh/kernel/sys_sh.c
+++ b/arch/sh/kernel/sys_sh.c
@@ -28,37 +28,13 @@
 #include <asm/cacheflush.h>
 #include <asm/cachectl.h>
 
-static inline long
-do_mmap2(unsigned long addr, unsigned long len, unsigned long prot,
-	 unsigned long flags, int fd, unsigned long pgoff)
-{
-	int error = -EBADF;
-	struct file *file = NULL;
-
-	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-	if (!(flags & MAP_ANONYMOUS)) {
-		file = fget(fd);
-		if (!file)
-			goto out;
-	}
-
-	down_write(&current->mm->mmap_sem);
-	error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
-	up_write(&current->mm->mmap_sem);
-
-	if (file)
-		fput(file);
-out:
-	return error;
-}
-
 asmlinkage int old_mmap(unsigned long addr, unsigned long len,
 	unsigned long prot, unsigned long flags,
 	int fd, unsigned long off)
 {
 	if (off & ~PAGE_MASK)
 		return -EINVAL;
-	return do_mmap2(addr, len, prot, flags, fd, off>>PAGE_SHIFT);
+	return sys_mmap_pgoff(addr, len, prot, flags, fd, off>>PAGE_SHIFT);
 }
 
 asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
@@ -74,7 +50,7 @@
 
 	pgoff >>= PAGE_SHIFT - 12;
 
-	return do_mmap2(addr, len, prot, flags, fd, pgoff);
+	return sys_mmap_pgoff(addr, len, prot, flags, fd, pgoff);
 }
 
 /*
diff --git a/arch/sh/mm/mmap.c b/arch/sh/mm/mmap.c
index d2984fa..afeb710 100644
--- a/arch/sh/mm/mmap.c
+++ b/arch/sh/mm/mmap.c
@@ -54,7 +54,8 @@
 		/* We do not accept a shared mapping if it would violate
 		 * cache aliasing constraints.
 		 */
-		if ((flags & MAP_SHARED) && (addr & shm_align_mask))
+		if ((flags & MAP_SHARED) &&
+		    ((addr - (pgoff << PAGE_SHIFT)) & shm_align_mask))
 			return -EINVAL;
 		return addr;
 	}
diff --git a/arch/sparc/include/asm/pci_64.h b/arch/sparc/include/asm/pci_64.h
index b63e51c..b0576df 100644
--- a/arch/sparc/include/asm/pci_64.h
+++ b/arch/sparc/include/asm/pci_64.h
@@ -16,8 +16,6 @@
 
 #define PCI_IRQ_NONE		0xffffffff
 
-#define PCI_CACHE_LINE_BYTES	64
-
 static inline void pcibios_set_master(struct pci_dev *dev)
 {
 	/* No special bus mastering setup handling */
diff --git a/arch/sparc/kernel/pci.c b/arch/sparc/kernel/pci.c
index c686486..b85374f 100644
--- a/arch/sparc/kernel/pci.c
+++ b/arch/sparc/kernel/pci.c
@@ -1081,3 +1081,10 @@
 	*start = rp->start - offset;
 	*end = rp->end - offset;
 }
+
+static int __init pcibios_init(void)
+{
+	pci_dfl_cache_line_size = 64 >> 2;
+	return 0;
+}
+subsys_initcall(pcibios_init);
diff --git a/arch/sparc/kernel/sys_sparc32.c b/arch/sparc/kernel/sys_sparc32.c
index 00abe87..dc0ac19 100644
--- a/arch/sparc/kernel/sys_sparc32.c
+++ b/arch/sparc/kernel/sys_sparc32.c
@@ -564,28 +564,6 @@
 	return do_sys_open(AT_FDCWD, filename, flags, mode);
 }
 
-extern unsigned long do_mremap(unsigned long addr,
-	unsigned long old_len, unsigned long new_len,
-	unsigned long flags, unsigned long new_addr);
-                
-asmlinkage unsigned long sys32_mremap(unsigned long addr,
-	unsigned long old_len, unsigned long new_len,
-	unsigned long flags, u32 __new_addr)
-{
-	unsigned long ret = -EINVAL;
-	unsigned long new_addr = __new_addr;
-
-	if (unlikely(sparc_mmap_check(addr, old_len)))
-		goto out;
-	if (unlikely(sparc_mmap_check(new_addr, new_len)))
-		goto out;
-	down_write(&current->mm->mmap_sem);
-	ret = do_mremap(addr, old_len, new_len, flags, new_addr);
-	up_write(&current->mm->mmap_sem);
-out:
-	return ret;       
-}
-
 long sys32_lookup_dcookie(unsigned long cookie_high,
 			  unsigned long cookie_low,
 			  char __user *buf, size_t len)
diff --git a/arch/sparc/kernel/sys_sparc_32.c b/arch/sparc/kernel/sys_sparc_32.c
index 03035c8..3a82e65 100644
--- a/arch/sparc/kernel/sys_sparc_32.c
+++ b/arch/sparc/kernel/sys_sparc_32.c
@@ -45,7 +45,8 @@
 		/* We do not accept a shared mapping if it would violate
 		 * cache aliasing constraints.
 		 */
-		if ((flags & MAP_SHARED) && (addr & (SHMLBA - 1)))
+		if ((flags & MAP_SHARED) &&
+		    ((addr - (pgoff << PAGE_SHIFT)) & (SHMLBA - 1)))
 			return -EINVAL;
 		return addr;
 	}
@@ -79,15 +80,6 @@
 	}
 }
 
-asmlinkage unsigned long sparc_brk(unsigned long brk)
-{
-	if(ARCH_SUN4C) {
-		if ((brk & 0xe0000000) != (current->mm->brk & 0xe0000000))
-			return current->mm->brk;
-	}
-	return sys_brk(brk);
-}
-
 /*
  * sys_pipe() is the normal C calling standard for creating
  * a pipe. It's not the way unix traditionally does this, though.
@@ -234,31 +226,6 @@
 }
 
 /* Linux version of mmap */
-static unsigned long do_mmap2(unsigned long addr, unsigned long len,
-	unsigned long prot, unsigned long flags, unsigned long fd,
-	unsigned long pgoff)
-{
-	struct file * file = NULL;
-	unsigned long retval = -EBADF;
-
-	if (!(flags & MAP_ANONYMOUS)) {
-		file = fget(fd);
-		if (!file)
-			goto out;
-	}
-
-	len = PAGE_ALIGN(len);
-	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-
-	down_write(&current->mm->mmap_sem);
-	retval = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
-	up_write(&current->mm->mmap_sem);
-
-	if (file)
-		fput(file);
-out:
-	return retval;
-}
 
 asmlinkage unsigned long sys_mmap2(unsigned long addr, unsigned long len,
 	unsigned long prot, unsigned long flags, unsigned long fd,
@@ -266,14 +233,16 @@
 {
 	/* Make sure the shift for mmap2 is constant (12), no matter what PAGE_SIZE
 	   we have. */
-	return do_mmap2(addr, len, prot, flags, fd, pgoff >> (PAGE_SHIFT - 12));
+	return sys_mmap_pgoff(addr, len, prot, flags, fd,
+			      pgoff >> (PAGE_SHIFT - 12));
 }
 
 asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len,
 	unsigned long prot, unsigned long flags, unsigned long fd,
 	unsigned long off)
 {
-	return do_mmap2(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
+	/* no alignment check? */
+	return sys_mmap_pgoff(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
 }
 
 long sparc_remap_file_pages(unsigned long start, unsigned long size,
@@ -287,27 +256,6 @@
 				    (pgoff >> (PAGE_SHIFT - 12)), flags);
 }
 
-extern unsigned long do_mremap(unsigned long addr,
-	unsigned long old_len, unsigned long new_len,
-	unsigned long flags, unsigned long new_addr);
-                
-asmlinkage unsigned long sparc_mremap(unsigned long addr,
-	unsigned long old_len, unsigned long new_len,
-	unsigned long flags, unsigned long new_addr)
-{
-	unsigned long ret = -EINVAL;
-
-	if (unlikely(sparc_mmap_check(addr, old_len)))
-		goto out;
-	if (unlikely(sparc_mmap_check(new_addr, new_len)))
-		goto out;
-	down_write(&current->mm->mmap_sem);
-	ret = do_mremap(addr, old_len, new_len, flags, new_addr);
-	up_write(&current->mm->mmap_sem);
-out:
-	return ret;       
-}
-
 /* we come to here via sys_nis_syscall so it can setup the regs argument */
 asmlinkage unsigned long
 c_sys_nis_syscall (struct pt_regs *regs)
diff --git a/arch/sparc/kernel/sys_sparc_64.c b/arch/sparc/kernel/sys_sparc_64.c
index e2d1024..cfa0e19 100644
--- a/arch/sparc/kernel/sys_sparc_64.c
+++ b/arch/sparc/kernel/sys_sparc_64.c
@@ -317,10 +317,14 @@
 unsigned long get_fb_unmapped_area(struct file *filp, unsigned long orig_addr, unsigned long len, unsigned long pgoff, unsigned long flags)
 {
 	unsigned long align_goal, addr = -ENOMEM;
+	unsigned long (*get_area)(struct file *, unsigned long,
+				  unsigned long, unsigned long, unsigned long);
+
+	get_area = current->mm->get_unmapped_area;
 
 	if (flags & MAP_FIXED) {
 		/* Ok, don't mess with it. */
-		return get_unmapped_area(NULL, orig_addr, len, pgoff, flags);
+		return get_area(NULL, orig_addr, len, pgoff, flags);
 	}
 	flags &= ~MAP_SHARED;
 
@@ -333,7 +337,7 @@
 		align_goal = (64UL * 1024);
 
 	do {
-		addr = get_unmapped_area(NULL, orig_addr, len + (align_goal - PAGE_SIZE), pgoff, flags);
+		addr = get_area(NULL, orig_addr, len + (align_goal - PAGE_SIZE), pgoff, flags);
 		if (!(addr & ~PAGE_MASK)) {
 			addr = (addr + (align_goal - 1UL)) & ~(align_goal - 1UL);
 			break;
@@ -351,7 +355,7 @@
 	 * be obtained.
 	 */
 	if (addr & ~PAGE_MASK)
-		addr = get_unmapped_area(NULL, orig_addr, len, pgoff, flags);
+		addr = get_area(NULL, orig_addr, len, pgoff, flags);
 
 	return addr;
 }
@@ -399,18 +403,6 @@
 	}
 }
 
-SYSCALL_DEFINE1(sparc_brk, unsigned long, brk)
-{
-	/* People could try to be nasty and use ta 0x6d in 32bit programs */
-	if (test_thread_flag(TIF_32BIT) && brk >= STACK_TOP32)
-		return current->mm->brk;
-
-	if (unlikely(straddles_64bit_va_hole(current->mm->brk, brk)))
-		return current->mm->brk;
-
-	return sys_brk(brk);
-}
-                                                                
 /*
  * sys_pipe() is the normal C calling standard for creating
  * a pipe. It's not the way unix traditionally does this, though.
@@ -568,23 +560,13 @@
 		unsigned long, prot, unsigned long, flags, unsigned long, fd,
 		unsigned long, off)
 {
-	struct file * file = NULL;
-	unsigned long retval = -EBADF;
+	unsigned long retval = -EINVAL;
 
-	if (!(flags & MAP_ANONYMOUS)) {
-		file = fget(fd);
-		if (!file)
-			goto out;
-	}
-	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-	len = PAGE_ALIGN(len);
-
-	down_write(&current->mm->mmap_sem);
-	retval = do_mmap(file, addr, len, prot, flags, off);
-	up_write(&current->mm->mmap_sem);
-
-	if (file)
-		fput(file);
+	if ((off + PAGE_ALIGN(len)) < off)
+		goto out;
+	if (off & ~PAGE_MASK)
+		goto out;
+	retval = sys_mmap_pgoff(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
 out:
 	return retval;
 }
@@ -614,12 +596,6 @@
 
 	if (test_thread_flag(TIF_32BIT))
 		goto out;
-	if (unlikely(new_len >= VA_EXCLUDE_START))
-		goto out;
-	if (unlikely(sparc_mmap_check(addr, old_len)))
-		goto out;
-	if (unlikely(sparc_mmap_check(new_addr, new_len)))
-		goto out;
 
 	down_write(&current->mm->mmap_sem);
 	ret = do_mremap(addr, old_len, new_len, flags, new_addr);
diff --git a/arch/sparc/kernel/systbls.h b/arch/sparc/kernel/systbls.h
index a63c5d2..d2f999a 100644
--- a/arch/sparc/kernel/systbls.h
+++ b/arch/sparc/kernel/systbls.h
@@ -9,7 +9,6 @@
 struct new_utsname;
 
 extern asmlinkage unsigned long sys_getpagesize(void);
-extern asmlinkage unsigned long sparc_brk(unsigned long brk);
 extern asmlinkage long sparc_pipe(struct pt_regs *regs);
 extern asmlinkage long sys_ipc(unsigned int call, int first,
 			       unsigned long second,
diff --git a/arch/sparc/kernel/systbls_32.S b/arch/sparc/kernel/systbls_32.S
index ceb1530..801fc8e 100644
--- a/arch/sparc/kernel/systbls_32.S
+++ b/arch/sparc/kernel/systbls_32.S
@@ -19,7 +19,7 @@
 /*0*/	.long sys_restart_syscall, sys_exit, sys_fork, sys_read, sys_write
 /*5*/	.long sys_open, sys_close, sys_wait4, sys_creat, sys_link
 /*10*/  .long sys_unlink, sunos_execv, sys_chdir, sys_chown16, sys_mknod
-/*15*/	.long sys_chmod, sys_lchown16, sparc_brk, sys_nis_syscall, sys_lseek
+/*15*/	.long sys_chmod, sys_lchown16, sys_brk, sys_nis_syscall, sys_lseek
 /*20*/	.long sys_getpid, sys_capget, sys_capset, sys_setuid16, sys_getuid16
 /*25*/	.long sys_vmsplice, sys_ptrace, sys_alarm, sys_sigaltstack, sys_pause
 /*30*/	.long sys_utime, sys_lchown, sys_fchown, sys_access, sys_nice
@@ -67,7 +67,7 @@
 /*235*/	.long sys_fstatfs64, sys_llseek, sys_mlock, sys_munlock, sys_mlockall
 /*240*/	.long sys_munlockall, sys_sched_setparam, sys_sched_getparam, sys_sched_setscheduler, sys_sched_getscheduler
 /*245*/	.long sys_sched_yield, sys_sched_get_priority_max, sys_sched_get_priority_min, sys_sched_rr_get_interval, sys_nanosleep
-/*250*/	.long sparc_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys_nfsservctl
+/*250*/	.long sys_mremap, sys_sysctl, sys_getsid, sys_fdatasync, sys_nfsservctl
 /*255*/	.long sys_sync_file_range, sys_clock_settime, sys_clock_gettime, sys_clock_getres, sys_clock_nanosleep
 /*260*/	.long sys_sched_getaffinity, sys_sched_setaffinity, sys_timer_settime, sys_timer_gettime, sys_timer_getoverrun
 /*265*/	.long sys_timer_delete, sys_timer_create, sys_nis_syscall, sys_io_setup, sys_io_destroy
diff --git a/arch/sparc/kernel/systbls_64.S b/arch/sparc/kernel/systbls_64.S
index cc8e786..e575b46 100644
--- a/arch/sparc/kernel/systbls_64.S
+++ b/arch/sparc/kernel/systbls_64.S
@@ -21,7 +21,7 @@
 /*0*/	.word sys_restart_syscall, sys32_exit, sys_fork, sys_read, sys_write
 /*5*/	.word sys32_open, sys_close, sys32_wait4, sys32_creat, sys_link
 /*10*/  .word sys_unlink, sunos_execv, sys_chdir, sys_chown16, sys32_mknod
-/*15*/	.word sys_chmod, sys_lchown16, sys_sparc_brk, sys32_perfctr, sys32_lseek
+/*15*/	.word sys_chmod, sys_lchown16, sys_brk, sys32_perfctr, sys32_lseek
 /*20*/	.word sys_getpid, sys_capget, sys_capset, sys_setuid16, sys_getuid16
 /*25*/	.word sys32_vmsplice, compat_sys_ptrace, sys_alarm, sys32_sigaltstack, sys_pause
 /*30*/	.word compat_sys_utime, sys_lchown, sys_fchown, sys32_access, sys32_nice
@@ -68,7 +68,7 @@
 	.word compat_sys_fstatfs64, sys_llseek, sys_mlock, sys_munlock, sys32_mlockall
 /*240*/	.word sys_munlockall, sys32_sched_setparam, sys32_sched_getparam, sys32_sched_setscheduler, sys32_sched_getscheduler
 	.word sys_sched_yield, sys32_sched_get_priority_max, sys32_sched_get_priority_min, sys32_sched_rr_get_interval, compat_sys_nanosleep
-/*250*/	.word sys32_mremap, compat_sys_sysctl, sys32_getsid, sys_fdatasync, sys32_nfsservctl
+/*250*/	.word sys_mremap, compat_sys_sysctl, sys32_getsid, sys_fdatasync, sys32_nfsservctl
 	.word sys32_sync_file_range, compat_sys_clock_settime, compat_sys_clock_gettime, compat_sys_clock_getres, sys32_clock_nanosleep
 /*260*/	.word compat_sys_sched_getaffinity, compat_sys_sched_setaffinity, sys32_timer_settime, compat_sys_timer_gettime, sys_timer_getoverrun
 	.word sys_timer_delete, compat_sys_timer_create, sys_ni_syscall, compat_sys_io_setup, sys_io_destroy
@@ -96,7 +96,7 @@
 /*0*/	.word sys_restart_syscall, sparc_exit, sys_fork, sys_read, sys_write
 /*5*/	.word sys_open, sys_close, sys_wait4, sys_creat, sys_link
 /*10*/  .word sys_unlink, sys_nis_syscall, sys_chdir, sys_chown, sys_mknod
-/*15*/	.word sys_chmod, sys_lchown, sys_sparc_brk, sys_perfctr, sys_lseek
+/*15*/	.word sys_chmod, sys_lchown, sys_brk, sys_perfctr, sys_lseek
 /*20*/	.word sys_getpid, sys_capget, sys_capset, sys_setuid, sys_getuid
 /*25*/	.word sys_vmsplice, sys_ptrace, sys_alarm, sys_sigaltstack, sys_nis_syscall
 /*30*/	.word sys_utime, sys_nis_syscall, sys_nis_syscall, sys_access, sys_nice
diff --git a/arch/um/kernel/syscall.c b/arch/um/kernel/syscall.c
index a4625c7..cccab85 100644
--- a/arch/um/kernel/syscall.c
+++ b/arch/um/kernel/syscall.c
@@ -8,6 +8,7 @@
 #include "linux/mm.h"
 #include "linux/sched.h"
 #include "linux/utsname.h"
+#include "linux/syscalls.h"
 #include "asm/current.h"
 #include "asm/mman.h"
 #include "asm/uaccess.h"
@@ -37,31 +38,6 @@
 	return ret;
 }
 
-/* common code for old and new mmaps */
-long sys_mmap2(unsigned long addr, unsigned long len,
-	       unsigned long prot, unsigned long flags,
-	       unsigned long fd, unsigned long pgoff)
-{
-	long error = -EBADF;
-	struct file * file = NULL;
-
-	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-	if (!(flags & MAP_ANONYMOUS)) {
-		file = fget(fd);
-		if (!file)
-			goto out;
-	}
-
-	down_write(&current->mm->mmap_sem);
-	error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
-	up_write(&current->mm->mmap_sem);
-
-	if (file)
-		fput(file);
- out:
-	return error;
-}
-
 long old_mmap(unsigned long addr, unsigned long len,
 	      unsigned long prot, unsigned long flags,
 	      unsigned long fd, unsigned long offset)
@@ -70,7 +46,7 @@
 	if (offset & ~PAGE_MASK)
 		goto out;
 
-	err = sys_mmap2(addr, len, prot, flags, fd, offset >> PAGE_SHIFT);
+	err = sys_mmap_pgoff(addr, len, prot, flags, fd, offset >> PAGE_SHIFT);
  out:
 	return err;
 }
diff --git a/arch/um/sys-i386/shared/sysdep/syscalls.h b/arch/um/sys-i386/shared/sysdep/syscalls.h
index 9056981..e778767 100644
--- a/arch/um/sys-i386/shared/sysdep/syscalls.h
+++ b/arch/um/sys-i386/shared/sysdep/syscalls.h
@@ -20,7 +20,3 @@
 #define EXECUTE_SYSCALL(syscall, regs) \
 	((long (*)(struct syscall_args)) \
 	 (*sys_call_table[syscall]))(SYSCALL_ARGS(&regs->regs))
-
-extern long sys_mmap2(unsigned long addr, unsigned long len,
-		      unsigned long prot, unsigned long flags,
-		      unsigned long fd, unsigned long pgoff);
diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S
index 4eefdca..53147ad 100644
--- a/arch/x86/ia32/ia32entry.S
+++ b/arch/x86/ia32/ia32entry.S
@@ -696,7 +696,7 @@
 	.quad quiet_ni_syscall		/* streams2 */
 	.quad stub32_vfork            /* 190 */
 	.quad compat_sys_getrlimit
-	.quad sys32_mmap2
+	.quad sys_mmap_pgoff
 	.quad sys32_truncate64
 	.quad sys32_ftruncate64
 	.quad sys32_stat64		/* 195 */
diff --git a/arch/x86/ia32/sys_ia32.c b/arch/x86/ia32/sys_ia32.c
index df82c0e..422572c 100644
--- a/arch/x86/ia32/sys_ia32.c
+++ b/arch/x86/ia32/sys_ia32.c
@@ -155,9 +155,6 @@
 asmlinkage long sys32_mmap(struct mmap_arg_struct __user *arg)
 {
 	struct mmap_arg_struct a;
-	struct file *file = NULL;
-	unsigned long retval;
-	struct mm_struct *mm ;
 
 	if (copy_from_user(&a, arg, sizeof(a)))
 		return -EFAULT;
@@ -165,22 +162,8 @@
 	if (a.offset & ~PAGE_MASK)
 		return -EINVAL;
 
-	if (!(a.flags & MAP_ANONYMOUS)) {
-		file = fget(a.fd);
-		if (!file)
-			return -EBADF;
-	}
-
-	mm = current->mm;
-	down_write(&mm->mmap_sem);
-	retval = do_mmap_pgoff(file, a.addr, a.len, a.prot, a.flags,
+	return sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd,
 			       a.offset>>PAGE_SHIFT);
-	if (file)
-		fput(file);
-
-	up_write(&mm->mmap_sem);
-
-	return retval;
 }
 
 asmlinkage long sys32_mprotect(unsigned long start, size_t len,
@@ -483,30 +466,6 @@
 	return ret;
 }
 
-asmlinkage long sys32_mmap2(unsigned long addr, unsigned long len,
-			    unsigned long prot, unsigned long flags,
-			    unsigned long fd, unsigned long pgoff)
-{
-	struct mm_struct *mm = current->mm;
-	unsigned long error;
-	struct file *file = NULL;
-
-	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-	if (!(flags & MAP_ANONYMOUS)) {
-		file = fget(fd);
-		if (!file)
-			return -EBADF;
-	}
-
-	down_write(&mm->mmap_sem);
-	error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
-	up_write(&mm->mmap_sem);
-
-	if (file)
-		fput(file);
-	return error;
-}
-
 asmlinkage long sys32_olduname(struct oldold_utsname __user *name)
 {
 	char *arch = "x86_64";
diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h
index b399988..b4bf9a9 100644
--- a/arch/x86/include/asm/pci_x86.h
+++ b/arch/x86/include/asm/pci_x86.h
@@ -118,11 +118,27 @@
 
 /* pci-mmconfig.c */
 
+/* "PCI MMCONFIG %04x [bus %02x-%02x]" */
+#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2)
+
+struct pci_mmcfg_region {
+	struct list_head list;
+	struct resource res;
+	u64 address;
+	char __iomem *virt;
+	u16 segment;
+	u8 start_bus;
+	u8 end_bus;
+	char name[PCI_MMCFG_RESOURCE_NAME_LEN];
+};
+
 extern int __init pci_mmcfg_arch_init(void);
 extern void __init pci_mmcfg_arch_free(void);
+extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus);
 
-extern struct acpi_mcfg_allocation *pci_mmcfg_config;
-extern int pci_mmcfg_config_num;
+extern struct list_head pci_mmcfg_list;
+
+#define PCI_MMCFG_BUS_OFFSET(bus)      ((bus) << 20)
 
 /*
  * AMD Fam10h CPUs are buggy, and cannot access MMIO config space
diff --git a/arch/x86/include/asm/sys_ia32.h b/arch/x86/include/asm/sys_ia32.h
index 9af9dec..4a5a089e 100644
--- a/arch/x86/include/asm/sys_ia32.h
+++ b/arch/x86/include/asm/sys_ia32.h
@@ -57,9 +57,6 @@
 asmlinkage long sys32_personality(unsigned long);
 asmlinkage long sys32_sendfile(int, int, compat_off_t __user *, s32);
 
-asmlinkage long sys32_mmap2(unsigned long, unsigned long, unsigned long,
-			    unsigned long, unsigned long, unsigned long);
-
 struct oldold_utsname;
 struct old_utsname;
 asmlinkage long sys32_olduname(struct oldold_utsname __user *);
diff --git a/arch/x86/include/asm/syscalls.h b/arch/x86/include/asm/syscalls.h
index 372b76e..1bb6e39 100644
--- a/arch/x86/include/asm/syscalls.h
+++ b/arch/x86/include/asm/syscalls.h
@@ -55,8 +55,6 @@
 struct oldold_utsname;
 struct old_utsname;
 
-asmlinkage long sys_mmap2(unsigned long, unsigned long, unsigned long,
-			  unsigned long, unsigned long, unsigned long);
 asmlinkage int old_mmap(struct mmap_arg_struct __user *);
 asmlinkage int old_select(struct sel_arg_struct __user *);
 asmlinkage int sys_ipc(uint, int, int, int, void __user *, long);
diff --git a/arch/x86/include/asm/xen/hypervisor.h b/arch/x86/include/asm/xen/hypervisor.h
index d5b7e90..396ff4c 100644
--- a/arch/x86/include/asm/xen/hypervisor.h
+++ b/arch/x86/include/asm/xen/hypervisor.h
@@ -37,31 +37,4 @@
 extern struct shared_info *HYPERVISOR_shared_info;
 extern struct start_info *xen_start_info;
 
-enum xen_domain_type {
-	XEN_NATIVE,		/* running on bare hardware    */
-	XEN_PV_DOMAIN,		/* running in a PV domain      */
-	XEN_HVM_DOMAIN,		/* running in a Xen hvm domain */
-};
-
-#ifdef CONFIG_XEN
-extern enum xen_domain_type xen_domain_type;
-#else
-#define xen_domain_type		XEN_NATIVE
-#endif
-
-#define xen_domain()		(xen_domain_type != XEN_NATIVE)
-#define xen_pv_domain()		(xen_domain() &&			\
-				 xen_domain_type == XEN_PV_DOMAIN)
-#define xen_hvm_domain()	(xen_domain() &&			\
-				 xen_domain_type == XEN_HVM_DOMAIN)
-
-#ifdef CONFIG_XEN_DOM0
-#include <xen/interface/xen.h>
-
-#define xen_initial_domain()	(xen_pv_domain() && \
-				 xen_start_info->flags & SIF_INITDOMAIN)
-#else  /* !CONFIG_XEN_DOM0 */
-#define xen_initial_domain()	(0)
-#endif	/* CONFIG_XEN_DOM0 */
-
 #endif /* _ASM_X86_XEN_HYPERVISOR_H */
diff --git a/arch/x86/kernel/amd_iommu_init.c b/arch/x86/kernel/amd_iommu_init.c
index 7ffc399..9c4a6f7 100644
--- a/arch/x86/kernel/amd_iommu_init.c
+++ b/arch/x86/kernel/amd_iommu_init.c
@@ -1336,6 +1336,9 @@
 		iommu_detected = 1;
 		amd_iommu_detected = 1;
 		x86_init.iommu.iommu_init = amd_iommu_init;
+
+		/* Make sure ACS will be enabled */
+		pci_request_acs();
 	}
 }
 
diff --git a/arch/x86/kernel/sys_i386_32.c b/arch/x86/kernel/sys_i386_32.c
index 1884a8d..dee1ff7 100644
--- a/arch/x86/kernel/sys_i386_32.c
+++ b/arch/x86/kernel/sys_i386_32.c
@@ -24,31 +24,6 @@
 
 #include <asm/syscalls.h>
 
-asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
-			  unsigned long prot, unsigned long flags,
-			  unsigned long fd, unsigned long pgoff)
-{
-	int error = -EBADF;
-	struct file *file = NULL;
-	struct mm_struct *mm = current->mm;
-
-	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-	if (!(flags & MAP_ANONYMOUS)) {
-		file = fget(fd);
-		if (!file)
-			goto out;
-	}
-
-	down_write(&mm->mmap_sem);
-	error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
-	up_write(&mm->mmap_sem);
-
-	if (file)
-		fput(file);
-out:
-	return error;
-}
-
 /*
  * Perform the select(nd, in, out, ex, tv) and mmap() system
  * calls. Linux/i386 didn't use to be able to handle more than
@@ -77,7 +52,7 @@
 	if (a.offset & ~PAGE_MASK)
 		goto out;
 
-	err = sys_mmap2(a.addr, a.len, a.prot, a.flags,
+	err = sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags,
 			a.fd, a.offset >> PAGE_SHIFT);
 out:
 	return err;
diff --git a/arch/x86/kernel/sys_x86_64.c b/arch/x86/kernel/sys_x86_64.c
index 45e00eb..8aa2057 100644
--- a/arch/x86/kernel/sys_x86_64.c
+++ b/arch/x86/kernel/sys_x86_64.c
@@ -23,26 +23,11 @@
 		unsigned long, fd, unsigned long, off)
 {
 	long error;
-	struct file *file;
-
 	error = -EINVAL;
 	if (off & ~PAGE_MASK)
 		goto out;
 
-	error = -EBADF;
-	file = NULL;
-	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-	if (!(flags & MAP_ANONYMOUS)) {
-		file = fget(fd);
-		if (!file)
-			goto out;
-	}
-	down_write(&current->mm->mmap_sem);
-	error = do_mmap_pgoff(file, addr, len, prot, flags, off >> PAGE_SHIFT);
-	up_write(&current->mm->mmap_sem);
-
-	if (file)
-		fput(file);
+	error = sys_mmap_pgoff(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
 out:
 	return error;
 }
diff --git a/arch/x86/kernel/syscall_table_32.S b/arch/x86/kernel/syscall_table_32.S
index 70c2125..15228b5 100644
--- a/arch/x86/kernel/syscall_table_32.S
+++ b/arch/x86/kernel/syscall_table_32.S
@@ -191,7 +191,7 @@
 	.long sys_ni_syscall	/* reserved for streams2 */
 	.long ptregs_vfork	/* 190 */
 	.long sys_getrlimit
-	.long sys_mmap2
+	.long sys_mmap_pgoff
 	.long sys_truncate64
 	.long sys_ftruncate64
 	.long sys_stat64	/* 195 */
diff --git a/arch/x86/pci/Makefile b/arch/x86/pci/Makefile
index d49202e..564b008 100644
--- a/arch/x86/pci/Makefile
+++ b/arch/x86/pci/Makefile
@@ -15,3 +15,8 @@
 
 obj-y				+= common.o early.o
 obj-y				+= amd_bus.o
+obj-$(CONFIG_X86_64)		+= bus_numa.o intel_bus.o
+
+ifeq ($(CONFIG_PCI_DEBUG),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c
index 1014eb4..959e548 100644
--- a/arch/x86/pci/acpi.c
+++ b/arch/x86/pci/acpi.c
@@ -7,6 +7,7 @@
 #include <asm/pci_x86.h>
 
 struct pci_root_info {
+	struct acpi_device *bridge;
 	char *name;
 	unsigned int res_num;
 	struct resource *res;
@@ -58,6 +59,30 @@
 	return false;
 }
 
+static void
+align_resource(struct acpi_device *bridge, struct resource *res)
+{
+	int align = (res->flags & IORESOURCE_MEM) ? 16 : 4;
+
+	/*
+	 * Host bridge windows are not BARs, but the decoders on the PCI side
+	 * that claim this address space have starting alignment and length
+	 * constraints, so fix any obvious BIOS goofs.
+	 */
+	if (!IS_ALIGNED(res->start, align)) {
+		dev_printk(KERN_DEBUG, &bridge->dev,
+			   "host bridge window %pR invalid; "
+			   "aligning start to %d-byte boundary\n", res, align);
+		res->start &= ~(align - 1);
+	}
+	if (!IS_ALIGNED(res->end + 1, align)) {
+		dev_printk(KERN_DEBUG, &bridge->dev,
+			   "host bridge window %pR invalid; "
+			   "aligning end to %d-byte boundary\n", res, align);
+		res->end = ALIGN(res->end, align) - 1;
+	}
+}
+
 static acpi_status
 setup_resource(struct acpi_resource *acpi_res, void *data)
 {
@@ -91,11 +116,12 @@
 	start = addr.minimum + addr.translation_offset;
 	end = start + addr.address_length - 1;
 	if (info->res_num >= max_root_bus_resources) {
-		printk(KERN_WARNING "PCI: Failed to allocate 0x%lx-0x%lx "
-			"from %s for %s due to _CRS returning more than "
-			"%d resource descriptors\n", (unsigned long) start,
-			(unsigned long) end, root->name, info->name,
-			max_root_bus_resources);
+		if (pci_probe & PCI_USE__CRS)
+			printk(KERN_WARNING "PCI: Failed to allocate "
+			       "0x%lx-0x%lx from %s for %s due to _CRS "
+			       "returning more than %d resource descriptors\n",
+			       (unsigned long) start, (unsigned long) end,
+			       root->name, info->name, max_root_bus_resources);
 		return AE_OK;
 	}
 
@@ -105,14 +131,28 @@
 	res->start = start;
 	res->end = end;
 	res->child = NULL;
+	align_resource(info->bridge, res);
+
+	if (!(pci_probe & PCI_USE__CRS)) {
+		dev_printk(KERN_DEBUG, &info->bridge->dev,
+			   "host bridge window %pR (ignored)\n", res);
+		return AE_OK;
+	}
 
 	if (insert_resource(root, res)) {
-		printk(KERN_ERR "PCI: Failed to allocate 0x%lx-0x%lx "
-			"from %s for %s\n", (unsigned long) res->start,
-			(unsigned long) res->end, root->name, info->name);
+		dev_err(&info->bridge->dev,
+			"can't allocate host bridge window %pR\n", res);
 	} else {
 		info->bus->resource[info->res_num] = res;
 		info->res_num++;
+		if (addr.translation_offset)
+			dev_info(&info->bridge->dev, "host bridge window %pR "
+				 "(PCI address [%#llx-%#llx])\n",
+				 res, res->start - addr.translation_offset,
+				 res->end - addr.translation_offset);
+		else
+			dev_info(&info->bridge->dev,
+				 "host bridge window %pR\n", res);
 	}
 	return AE_OK;
 }
@@ -124,6 +164,12 @@
 	struct pci_root_info info;
 	size_t size;
 
+	if (!(pci_probe & PCI_USE__CRS))
+		dev_info(&device->dev,
+			 "ignoring host bridge windows from ACPI; "
+			 "boot with \"pci=use_crs\" to use them\n");
+
+	info.bridge = device;
 	info.bus = bus;
 	info.res_num = 0;
 	acpi_walk_resources(device->handle, METHOD_NAME__CRS, count_resource,
@@ -163,8 +209,9 @@
 #endif
 
 	if (domain && !pci_domains_supported) {
-		printk(KERN_WARNING "PCI: Multiple domains not supported "
-		       "(dom %d, bus %d)\n", domain, busnum);
+		printk(KERN_WARNING "pci_bus %04x:%02x: "
+		       "ignored (multiple domains not supported)\n",
+		       domain, busnum);
 		return NULL;
 	}
 
@@ -188,7 +235,8 @@
 	 */
 	sd = kzalloc(sizeof(*sd), GFP_KERNEL);
 	if (!sd) {
-		printk(KERN_ERR "PCI: OOM, not probing PCI bus %02x\n", busnum);
+		printk(KERN_WARNING "pci_bus %04x:%02x: "
+		       "ignored (out of memory)\n", domain, busnum);
 		return NULL;
 	}
 
@@ -209,9 +257,7 @@
 	} else {
 		bus = pci_create_bus(NULL, busnum, &pci_root_ops, sd);
 		if (bus) {
-			if (pci_probe & PCI_USE__CRS)
-				get_current_resources(device, busnum, domain,
-							bus);
+			get_current_resources(device, busnum, domain, bus);
 			bus->subordinate = pci_scan_child_bus(bus);
 		}
 	}
diff --git a/arch/x86/pci/amd_bus.c b/arch/x86/pci/amd_bus.c
index 572ee97..95ecbd4 100644
--- a/arch/x86/pci/amd_bus.c
+++ b/arch/x86/pci/amd_bus.c
@@ -6,10 +6,10 @@
 
 #ifdef CONFIG_X86_64
 #include <asm/pci-direct.h>
-#include <asm/mpspec.h>
-#include <linux/cpumask.h>
 #endif
 
+#include "bus_numa.h"
+
 /*
  * This discovers the pcibus <-> node mapping on AMD K8.
  * also get peer root bus resource for io,mmio
@@ -17,67 +17,6 @@
 
 #ifdef CONFIG_X86_64
 
-/*
- * sub bus (transparent) will use entres from 3 to store extra from root,
- * so need to make sure have enought slot there, increase PCI_BUS_NUM_RESOURCES?
- */
-#define RES_NUM 16
-struct pci_root_info {
-	char name[12];
-	unsigned int res_num;
-	struct resource res[RES_NUM];
-	int bus_min;
-	int bus_max;
-	int node;
-	int link;
-};
-
-/* 4 at this time, it may become to 32 */
-#define PCI_ROOT_NR 4
-static int pci_root_num;
-static struct pci_root_info pci_root_info[PCI_ROOT_NR];
-
-void x86_pci_root_bus_res_quirks(struct pci_bus *b)
-{
-	int i;
-	int j;
-	struct pci_root_info *info;
-
-	/* don't go for it if _CRS is used already */
-	if (b->resource[0] != &ioport_resource ||
-	    b->resource[1] != &iomem_resource)
-		return;
-
-	/* if only one root bus, don't need to anything */
-	if (pci_root_num < 2)
-		return;
-
-	for (i = 0; i < pci_root_num; i++) {
-		if (pci_root_info[i].bus_min == b->number)
-			break;
-	}
-
-	if (i == pci_root_num)
-		return;
-
-	printk(KERN_DEBUG "PCI: peer root bus %02x res updated from pci conf\n",
-			b->number);
-
-	info = &pci_root_info[i];
-	for (j = 0; j < info->res_num; j++) {
-		struct resource *res;
-		struct resource *root;
-
-		res = &info->res[j];
-		b->resource[j] = res;
-		if (res->flags & IORESOURCE_IO)
-			root = &ioport_resource;
-		else
-			root = &iomem_resource;
-		insert_resource(root, res);
-	}
-}
-
 #define RANGE_NUM 16
 
 struct res_range {
@@ -130,52 +69,6 @@
 	}
 }
 
-static void __init update_res(struct pci_root_info *info, size_t start,
-			      size_t end, unsigned long flags, int merge)
-{
-	int i;
-	struct resource *res;
-
-	if (!merge)
-		goto addit;
-
-	/* try to merge it with old one */
-	for (i = 0; i < info->res_num; i++) {
-		size_t final_start, final_end;
-		size_t common_start, common_end;
-
-		res = &info->res[i];
-		if (res->flags != flags)
-			continue;
-
-		common_start = max((size_t)res->start, start);
-		common_end = min((size_t)res->end, end);
-		if (common_start > common_end + 1)
-			continue;
-
-		final_start = min((size_t)res->start, start);
-		final_end = max((size_t)res->end, end);
-
-		res->start = final_start;
-		res->end = final_end;
-		return;
-	}
-
-addit:
-
-	/* need to add that */
-	if (info->res_num >= RES_NUM)
-		return;
-
-	res = &info->res[info->res_num];
-	res->name = info->name;
-	res->flags = flags;
-	res->start = start;
-	res->end = end;
-	res->child = NULL;
-	info->res_num++;
-}
-
 struct pci_hostbridge_probe {
 	u32 bus;
 	u32 slot;
@@ -230,7 +123,6 @@
 	int j;
 	unsigned bus;
 	unsigned slot;
-	int found;
 	int node;
 	int link;
 	int def_node;
@@ -247,7 +139,7 @@
 	if (!early_pci_allowed())
 		return -1;
 
-	found = 0;
+	found_all_numa_early = 0;
 	for (i = 0; i < ARRAY_SIZE(pci_probes); i++) {
 		u32 id;
 		u16 device;
@@ -261,12 +153,12 @@
 		device = (id>>16) & 0xffff;
 		if (pci_probes[i].vendor == vendor &&
 		    pci_probes[i].device == device) {
-			found = 1;
+			found_all_numa_early = 1;
 			break;
 		}
 	}
 
-	if (!found)
+	if (!found_all_numa_early)
 		return 0;
 
 	pci_root_num = 0;
@@ -488,7 +380,7 @@
 		info = &pci_root_info[i];
 		res_num = info->res_num;
 		busnum = info->bus_min;
-		printk(KERN_DEBUG "bus: [%02x,%02x] on node %x link %x\n",
+		printk(KERN_DEBUG "bus: [%02x, %02x] on node %x link %x\n",
 		       info->bus_min, info->bus_max, info->node, info->link);
 		for (j = 0; j < res_num; j++) {
 			res = &info->res[j];
diff --git a/arch/x86/pci/bus_numa.c b/arch/x86/pci/bus_numa.c
new file mode 100644
index 0000000..145df00
--- /dev/null
+++ b/arch/x86/pci/bus_numa.c
@@ -0,0 +1,101 @@
+#include <linux/init.h>
+#include <linux/pci.h>
+
+#include "bus_numa.h"
+
+int pci_root_num;
+struct pci_root_info pci_root_info[PCI_ROOT_NR];
+int found_all_numa_early;
+
+void x86_pci_root_bus_res_quirks(struct pci_bus *b)
+{
+	int i;
+	int j;
+	struct pci_root_info *info;
+
+	/* don't go for it if _CRS is used already */
+	if (b->resource[0] != &ioport_resource ||
+	    b->resource[1] != &iomem_resource)
+		return;
+
+	if (!pci_root_num)
+		return;
+
+	/* for amd, if only one root bus, don't need to do anything */
+	if (pci_root_num < 2 && found_all_numa_early)
+		return;
+
+	for (i = 0; i < pci_root_num; i++) {
+		if (pci_root_info[i].bus_min == b->number)
+			break;
+	}
+
+	if (i == pci_root_num)
+		return;
+
+	printk(KERN_DEBUG "PCI: peer root bus %02x res updated from pci conf\n",
+			b->number);
+
+	info = &pci_root_info[i];
+	for (j = 0; j < info->res_num; j++) {
+		struct resource *res;
+		struct resource *root;
+
+		res = &info->res[j];
+		b->resource[j] = res;
+		if (res->flags & IORESOURCE_IO)
+			root = &ioport_resource;
+		else
+			root = &iomem_resource;
+		insert_resource(root, res);
+	}
+}
+
+void __init update_res(struct pci_root_info *info, size_t start,
+			      size_t end, unsigned long flags, int merge)
+{
+	int i;
+	struct resource *res;
+
+	if (start > end)
+		return;
+
+	if (!merge)
+		goto addit;
+
+	/* try to merge it with old one */
+	for (i = 0; i < info->res_num; i++) {
+		size_t final_start, final_end;
+		size_t common_start, common_end;
+
+		res = &info->res[i];
+		if (res->flags != flags)
+			continue;
+
+		common_start = max((size_t)res->start, start);
+		common_end = min((size_t)res->end, end);
+		if (common_start > common_end + 1)
+			continue;
+
+		final_start = min((size_t)res->start, start);
+		final_end = max((size_t)res->end, end);
+
+		res->start = final_start;
+		res->end = final_end;
+		return;
+	}
+
+addit:
+
+	/* need to add that */
+	if (info->res_num >= RES_NUM)
+		return;
+
+	res = &info->res[info->res_num];
+	res->name = info->name;
+	res->flags = flags;
+	res->start = start;
+	res->end = end;
+	res->child = NULL;
+	info->res_num++;
+}
diff --git a/arch/x86/pci/bus_numa.h b/arch/x86/pci/bus_numa.h
new file mode 100644
index 0000000..adbc23f
--- /dev/null
+++ b/arch/x86/pci/bus_numa.h
@@ -0,0 +1,27 @@
+#ifdef CONFIG_X86_64
+
+/*
+ * sub bus (transparent) will use entres from 3 to store extra from
+ * root, so need to make sure we have enough slot there, Should we
+ * increase PCI_BUS_NUM_RESOURCES?
+ */
+#define RES_NUM 16
+struct pci_root_info {
+	char name[12];
+	unsigned int res_num;
+	struct resource res[RES_NUM];
+	int bus_min;
+	int bus_max;
+	int node;
+	int link;
+};
+
+/* 4 at this time, it may become to 32 */
+#define PCI_ROOT_NR 4
+extern int pci_root_num;
+extern struct pci_root_info pci_root_info[PCI_ROOT_NR];
+extern int found_all_numa_early;
+
+extern void update_res(struct pci_root_info *info, size_t start,
+			      size_t end, unsigned long flags, int merge);
+#endif
diff --git a/arch/x86/pci/common.c b/arch/x86/pci/common.c
index 1331fcf..d2552c6 100644
--- a/arch/x86/pci/common.c
+++ b/arch/x86/pci/common.c
@@ -410,8 +410,6 @@
 	return bus;
 }
 
-extern u8 pci_cache_line_size;
-
 int __init pcibios_init(void)
 {
 	struct cpuinfo_x86 *c = &boot_cpu_data;
@@ -422,15 +420,19 @@
 	}
 
 	/*
-	 * Assume PCI cacheline size of 32 bytes for all x86s except K7/K8
-	 * and P4. It's also good for 386/486s (which actually have 16)
+	 * Set PCI cacheline size to that of the CPU if the CPU has reported it.
+	 * (For older CPUs that don't support cpuid, we se it to 32 bytes
+	 * It's also good for 386/486s (which actually have 16)
 	 * as quite a few PCI devices do not support smaller values.
 	 */
-	pci_cache_line_size = 32 >> 2;
-	if (c->x86 >= 6 && c->x86_vendor == X86_VENDOR_AMD)
-		pci_cache_line_size = 64 >> 2;	/* K7 & K8 */
-	else if (c->x86 > 6 && c->x86_vendor == X86_VENDOR_INTEL)
-		pci_cache_line_size = 128 >> 2;	/* P4 */
+	if (c->x86_clflush_size > 0) {
+		pci_dfl_cache_line_size = c->x86_clflush_size >> 2;
+		printk(KERN_DEBUG "PCI: pci_cache_line_size set to %d bytes\n",
+			pci_dfl_cache_line_size << 2);
+	} else {
+ 		pci_dfl_cache_line_size = 32 >> 2;
+		printk(KERN_DEBUG "PCI: Unknown cacheline size. Setting to 32 bytes\n");
+	}
 
 	pcibios_resource_survey();
 
diff --git a/arch/x86/pci/early.c b/arch/x86/pci/early.c
index aaf26ae..d1067d5 100644
--- a/arch/x86/pci/early.c
+++ b/arch/x86/pci/early.c
@@ -12,8 +12,6 @@
 	u32 v;
 	outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
 	v = inl(0xcfc);
-	if (v != 0xffffffff)
-		pr_debug("%x reading 4 from %x: %x\n", slot, offset, v);
 	return v;
 }
 
@@ -22,7 +20,6 @@
 	u8 v;
 	outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
 	v = inb(0xcfc + (offset&3));
-	pr_debug("%x reading 1 from %x: %x\n", slot, offset, v);
 	return v;
 }
 
@@ -31,28 +28,24 @@
 	u16 v;
 	outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
 	v = inw(0xcfc + (offset&2));
-	pr_debug("%x reading 2 from %x: %x\n", slot, offset, v);
 	return v;
 }
 
 void write_pci_config(u8 bus, u8 slot, u8 func, u8 offset,
 				    u32 val)
 {
-	pr_debug("%x writing to %x: %x\n", slot, offset, val);
 	outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
 	outl(val, 0xcfc);
 }
 
 void write_pci_config_byte(u8 bus, u8 slot, u8 func, u8 offset, u8 val)
 {
-	pr_debug("%x writing to %x: %x\n", slot, offset, val);
 	outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
 	outb(val, 0xcfc + (offset&3));
 }
 
 void write_pci_config_16(u8 bus, u8 slot, u8 func, u8 offset, u16 val)
 {
-	pr_debug("%x writing to %x: %x\n", slot, offset, val);
 	outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
 	outw(val, 0xcfc + (offset&2));
 }
diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c
index b22d13b..5dc9e8c 100644
--- a/arch/x86/pci/i386.c
+++ b/arch/x86/pci/i386.c
@@ -129,7 +129,9 @@
 					continue;
 				if (!r->start ||
 				    pci_claim_resource(dev, idx) < 0) {
-					dev_info(&dev->dev, "BAR %d: can't allocate resource\n", idx);
+					dev_info(&dev->dev,
+						 "can't reserve window %pR\n",
+						 r);
 					/*
 					 * Something is wrong with the region.
 					 * Invalidate the resource to prevent
@@ -144,16 +146,29 @@
 	}
 }
 
+struct pci_check_idx_range {
+	int start;
+	int end;
+};
+
 static void __init pcibios_allocate_resources(int pass)
 {
 	struct pci_dev *dev = NULL;
-	int idx, disabled;
+	int idx, disabled, i;
 	u16 command;
 	struct resource *r;
 
+	struct pci_check_idx_range idx_range[] = {
+		{ PCI_STD_RESOURCES, PCI_STD_RESOURCE_END },
+#ifdef CONFIG_PCI_IOV
+		{ PCI_IOV_RESOURCES, PCI_IOV_RESOURCE_END },
+#endif
+	};
+
 	for_each_pci_dev(dev) {
 		pci_read_config_word(dev, PCI_COMMAND, &command);
-		for (idx = 0; idx < PCI_ROM_RESOURCE; idx++) {
+		for (i = 0; i < ARRAY_SIZE(idx_range); i++)
+		for (idx = idx_range[i].start; idx <= idx_range[i].end; idx++) {
 			r = &dev->resource[idx];
 			if (r->parent)		/* Already allocated */
 				continue;
@@ -164,12 +179,12 @@
 			else
 				disabled = !(command & PCI_COMMAND_MEMORY);
 			if (pass == disabled) {
-				dev_dbg(&dev->dev, "resource %#08llx-%#08llx (f=%lx, d=%d, p=%d)\n",
-					(unsigned long long) r->start,
-					(unsigned long long) r->end,
-					r->flags, disabled, pass);
+				dev_dbg(&dev->dev,
+					"BAR %d: reserving %pr (d=%d, p=%d)\n",
+					idx, r, disabled, pass);
 				if (pci_claim_resource(dev, idx) < 0) {
-					dev_info(&dev->dev, "BAR %d: can't allocate resource\n", idx);
+					dev_info(&dev->dev,
+						 "can't reserve %pR\n", r);
 					/* We'll assign a new address later */
 					r->end -= r->start;
 					r->start = 0;
@@ -182,7 +197,7 @@
 				/* Turn the ROM off, leave the resource region,
 				 * but keep it unregistered. */
 				u32 reg;
-				dev_dbg(&dev->dev, "disabling ROM\n");
+				dev_dbg(&dev->dev, "disabling ROM %pR\n", r);
 				r->flags &= ~IORESOURCE_ROM_ENABLE;
 				pci_read_config_dword(dev,
 						dev->rom_base_reg, &reg);
@@ -282,6 +297,15 @@
 		return -EINVAL;
 
 	prot = pgprot_val(vma->vm_page_prot);
+
+	/*
+ 	 * Return error if pat is not enabled and write_combine is requested.
+ 	 * Caller can followup with UC MINUS request and add a WC mtrr if there
+ 	 * is a free mtrr slot.
+ 	 */
+	if (!pat_enabled && write_combine)
+		return -EINVAL;
+
 	if (pat_enabled && write_combine)
 		prot |= _PAGE_CACHE_WC;
 	else if (pat_enabled || boot_cpu_data.x86 > 3)
diff --git a/arch/x86/pci/intel_bus.c b/arch/x86/pci/intel_bus.c
new file mode 100644
index 0000000..b7a55dc
--- /dev/null
+++ b/arch/x86/pci/intel_bus.c
@@ -0,0 +1,90 @@
+/*
+ * to read io range from IOH pci conf, need to do it after mmconfig is there
+ */
+
+#include <linux/delay.h>
+#include <linux/dmi.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <asm/pci_x86.h>
+
+#include "bus_numa.h"
+
+static inline void print_ioh_resources(struct pci_root_info *info)
+{
+	int res_num;
+	int busnum;
+	int i;
+
+	printk(KERN_DEBUG "IOH bus: [%02x, %02x]\n",
+			info->bus_min, info->bus_max);
+	res_num = info->res_num;
+	busnum = info->bus_min;
+	for (i = 0; i < res_num; i++) {
+		struct resource *res;
+
+		res = &info->res[i];
+		printk(KERN_DEBUG "IOH bus: %02x index %x %s: [%llx, %llx]\n",
+			busnum, i,
+			(res->flags & IORESOURCE_IO) ? "io port" :
+							"mmio",
+			res->start, res->end);
+	}
+}
+
+#define IOH_LIO			0x108
+#define IOH_LMMIOL		0x10c
+#define IOH_LMMIOH		0x110
+#define IOH_LMMIOH_BASEU	0x114
+#define IOH_LMMIOH_LIMITU	0x118
+#define IOH_LCFGBUS		0x11c
+
+static void __devinit pci_root_bus_res(struct pci_dev *dev)
+{
+	u16 word;
+	u32 dword;
+	struct pci_root_info *info;
+	u16 io_base, io_end;
+	u32 mmiol_base, mmiol_end;
+	u64 mmioh_base, mmioh_end;
+	int bus_base, bus_end;
+
+	if (pci_root_num >= PCI_ROOT_NR) {
+		printk(KERN_DEBUG "intel_bus.c: PCI_ROOT_NR is too small\n");
+		return;
+	}
+
+	info = &pci_root_info[pci_root_num];
+	pci_root_num++;
+
+	pci_read_config_word(dev, IOH_LCFGBUS, &word);
+	bus_base = (word & 0xff);
+	bus_end = (word & 0xff00) >> 8;
+	sprintf(info->name, "PCI Bus #%02x", bus_base);
+	info->bus_min = bus_base;
+	info->bus_max = bus_end;
+
+	pci_read_config_word(dev, IOH_LIO, &word);
+	io_base = (word & 0xf0) << (12 - 4);
+	io_end = (word & 0xf000) | 0xfff;
+	update_res(info, io_base, io_end, IORESOURCE_IO, 0);
+
+	pci_read_config_dword(dev, IOH_LMMIOL, &dword);
+	mmiol_base = (dword & 0xff00) << (24 - 8);
+	mmiol_end = (dword & 0xff000000) | 0xffffff;
+	update_res(info, mmiol_base, mmiol_end, IORESOURCE_MEM, 0);
+
+	pci_read_config_dword(dev, IOH_LMMIOH, &dword);
+	mmioh_base = ((u64)(dword & 0xfc00)) << (26 - 10);
+	mmioh_end = ((u64)(dword & 0xfc000000) | 0x3ffffff);
+	pci_read_config_dword(dev, IOH_LMMIOH_BASEU, &dword);
+	mmioh_base |= ((u64)(dword & 0x7ffff)) << 32;
+	pci_read_config_dword(dev, IOH_LMMIOH_LIMITU, &dword);
+	mmioh_end |= ((u64)(dword & 0x7ffff)) << 32;
+	update_res(info, mmioh_base, mmioh_end, IORESOURCE_MEM, 0);
+
+	print_ioh_resources(info);
+}
+
+/* intel IOH */
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x342e, pci_root_bus_res);
diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c
index 602c172d..b19d1e5 100644
--- a/arch/x86/pci/mmconfig-shared.c
+++ b/arch/x86/pci/mmconfig-shared.c
@@ -15,48 +15,98 @@
 #include <linux/acpi.h>
 #include <linux/sfi_acpi.h>
 #include <linux/bitmap.h>
-#include <linux/sort.h>
+#include <linux/dmi.h>
 #include <asm/e820.h>
 #include <asm/pci_x86.h>
 #include <asm/acpi.h>
 
 #define PREFIX "PCI: "
 
-/* aperture is up to 256MB but BIOS may reserve less */
-#define MMCONFIG_APER_MIN	(2 * 1024*1024)
-#define MMCONFIG_APER_MAX	(256 * 1024*1024)
-
 /* Indicate if the mmcfg resources have been placed into the resource table. */
 static int __initdata pci_mmcfg_resources_inserted;
 
-static __init int extend_mmcfg(int num)
+LIST_HEAD(pci_mmcfg_list);
+
+static __init void pci_mmconfig_remove(struct pci_mmcfg_region *cfg)
 {
-	struct acpi_mcfg_allocation *new;
-	int new_num = pci_mmcfg_config_num + num;
-
-	new = kzalloc(sizeof(pci_mmcfg_config[0]) * new_num, GFP_KERNEL);
-	if (!new)
-		return -1;
-
-	if (pci_mmcfg_config) {
-		memcpy(new, pci_mmcfg_config,
-			 sizeof(pci_mmcfg_config[0]) * new_num);
-		kfree(pci_mmcfg_config);
-	}
-	pci_mmcfg_config = new;
-
-	return 0;
+	if (cfg->res.parent)
+		release_resource(&cfg->res);
+	list_del(&cfg->list);
+	kfree(cfg);
 }
 
-static __init void fill_one_mmcfg(u64 addr, int segment, int start, int end)
+static __init void free_all_mmcfg(void)
 {
-	int i = pci_mmcfg_config_num;
+	struct pci_mmcfg_region *cfg, *tmp;
 
-	pci_mmcfg_config_num++;
-	pci_mmcfg_config[i].address = addr;
-	pci_mmcfg_config[i].pci_segment = segment;
-	pci_mmcfg_config[i].start_bus_number = start;
-	pci_mmcfg_config[i].end_bus_number = end;
+	pci_mmcfg_arch_free();
+	list_for_each_entry_safe(cfg, tmp, &pci_mmcfg_list, list)
+		pci_mmconfig_remove(cfg);
+}
+
+static __init void list_add_sorted(struct pci_mmcfg_region *new)
+{
+	struct pci_mmcfg_region *cfg;
+
+	/* keep list sorted by segment and starting bus number */
+	list_for_each_entry(cfg, &pci_mmcfg_list, list) {
+		if (cfg->segment > new->segment ||
+		    (cfg->segment == new->segment &&
+		     cfg->start_bus >= new->start_bus)) {
+			list_add_tail(&new->list, &cfg->list);
+			return;
+		}
+	}
+	list_add_tail(&new->list, &pci_mmcfg_list);
+}
+
+static __init struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start,
+							int end, u64 addr)
+{
+	struct pci_mmcfg_region *new;
+	int num_buses;
+	struct resource *res;
+
+	if (addr == 0)
+		return NULL;
+
+	new = kzalloc(sizeof(*new), GFP_KERNEL);
+	if (!new)
+		return NULL;
+
+	new->address = addr;
+	new->segment = segment;
+	new->start_bus = start;
+	new->end_bus = end;
+
+	list_add_sorted(new);
+
+	num_buses = end - start + 1;
+	res = &new->res;
+	res->start = addr + PCI_MMCFG_BUS_OFFSET(start);
+	res->end = addr + PCI_MMCFG_BUS_OFFSET(num_buses) - 1;
+	res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+	snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN,
+		 "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end);
+	res->name = new->name;
+
+	printk(KERN_INFO PREFIX "MMCONFIG for domain %04x [bus %02x-%02x] at "
+	       "%pR (base %#lx)\n", segment, start, end, &new->res,
+	       (unsigned long) addr);
+
+	return new;
+}
+
+struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus)
+{
+	struct pci_mmcfg_region *cfg;
+
+	list_for_each_entry(cfg, &pci_mmcfg_list, list)
+		if (cfg->segment == segment &&
+		    cfg->start_bus <= bus && bus <= cfg->end_bus)
+			return cfg;
+
+	return NULL;
 }
 
 static const char __init *pci_mmcfg_e7520(void)
@@ -68,11 +118,9 @@
 	if (win == 0x0000 || win == 0xf000)
 		return NULL;
 
-	if (extend_mmcfg(1) == -1)
+	if (pci_mmconfig_add(0, 0, 255, win << 16) == NULL)
 		return NULL;
 
-	fill_one_mmcfg(win << 16, 0, 0, 255);
-
 	return "Intel Corporation E7520 Memory Controller Hub";
 }
 
@@ -114,11 +162,9 @@
 	if ((pciexbar & mask) >= 0xf0000000U)
 		return NULL;
 
-	if (extend_mmcfg(1) == -1)
+	if (pci_mmconfig_add(0, 0, (len >> 20) - 1, pciexbar & mask) == NULL)
 		return NULL;
 
-	fill_one_mmcfg(pciexbar & mask, 0, 0, (len >> 20) - 1);
-
 	return "Intel Corporation 945G/GZ/P/PL Express Memory Controller Hub";
 }
 
@@ -127,7 +173,7 @@
 	u32 low, high, address;
 	u64 base, msr;
 	int i;
-	unsigned segnbits = 0, busnbits;
+	unsigned segnbits = 0, busnbits, end_bus;
 
 	if (!(pci_probe & PCI_CHECK_ENABLE_AMD_MMCONF))
 		return NULL;
@@ -161,11 +207,13 @@
 		busnbits = 8;
 	}
 
-	if (extend_mmcfg(1 << segnbits) == -1)
-		return NULL;
-
+	end_bus = (1 << busnbits) - 1;
 	for (i = 0; i < (1 << segnbits); i++)
-		fill_one_mmcfg(base + (1<<28) * i, i, 0, (1 << busnbits) - 1);
+		if (pci_mmconfig_add(i, 0, end_bus,
+				     base + (1<<28) * i) == NULL) {
+			free_all_mmcfg();
+			return NULL;
+		}
 
 	return "AMD Family 10h NB";
 }
@@ -190,7 +238,7 @@
 	/*
 	 * do check if amd fam10h already took over
 	 */
-	if (!acpi_disabled || pci_mmcfg_config_num || mcp55_checked)
+	if (!acpi_disabled || !list_empty(&pci_mmcfg_list) || mcp55_checked)
 		return NULL;
 
 	mcp55_checked = true;
@@ -213,16 +261,14 @@
 		if (!(extcfg & extcfg_enable_mask))
 			continue;
 
-		if (extend_mmcfg(1) == -1)
-			continue;
-
 		size_index = (extcfg & extcfg_size_mask) >> extcfg_size_shift;
 		base = extcfg & extcfg_base_mask[size_index];
 		/* base could > 4G */
 		base <<= extcfg_base_lshift;
 		start = (extcfg & extcfg_start_mask) >> extcfg_start_shift;
 		end = start + extcfg_sizebus[size_index] - 1;
-		fill_one_mmcfg(base, 0, start, end);
+		if (pci_mmconfig_add(0, start, end, base) == NULL)
+			continue;
 		mcp55_mmconf_found++;
 	}
 
@@ -253,45 +299,27 @@
 	  0x0369, pci_mmcfg_nvidia_mcp55 },
 };
 
-static int __init cmp_mmcfg(const void *x1, const void *x2)
-{
-	const typeof(pci_mmcfg_config[0]) *m1 = x1;
-	const typeof(pci_mmcfg_config[0]) *m2 = x2;
-	int start1, start2;
-
-	start1 = m1->start_bus_number;
-	start2 = m2->start_bus_number;
-
-	return start1 - start2;
-}
-
 static void __init pci_mmcfg_check_end_bus_number(void)
 {
-	int i;
-	typeof(pci_mmcfg_config[0]) *cfg, *cfgx;
-
-	/* sort them at first */
-	sort(pci_mmcfg_config, pci_mmcfg_config_num,
-		 sizeof(pci_mmcfg_config[0]), cmp_mmcfg, NULL);
+	struct pci_mmcfg_region *cfg, *cfgx;
 
 	/* last one*/
-	if (pci_mmcfg_config_num > 0) {
-		i = pci_mmcfg_config_num - 1;
-		cfg = &pci_mmcfg_config[i];
-		if (cfg->end_bus_number < cfg->start_bus_number)
-			cfg->end_bus_number = 255;
-	}
+	cfg = list_entry(pci_mmcfg_list.prev, typeof(*cfg), list);
+	if (cfg)
+		if (cfg->end_bus < cfg->start_bus)
+			cfg->end_bus = 255;
+
+	if (list_is_singular(&pci_mmcfg_list))
+		return;
 
 	/* don't overlap please */
-	for (i = 0; i < pci_mmcfg_config_num - 1; i++) {
-		cfg = &pci_mmcfg_config[i];
-		cfgx = &pci_mmcfg_config[i+1];
+	list_for_each_entry(cfg, &pci_mmcfg_list, list) {
+		if (cfg->end_bus < cfg->start_bus)
+			cfg->end_bus = 255;
 
-		if (cfg->end_bus_number < cfg->start_bus_number)
-			cfg->end_bus_number = 255;
-
-		if (cfg->end_bus_number >= cfgx->start_bus_number)
-			cfg->end_bus_number = cfgx->start_bus_number - 1;
+		cfgx = list_entry(cfg->list.next, typeof(*cfg), list);
+		if (cfg != cfgx && cfg->end_bus >= cfgx->start_bus)
+			cfg->end_bus = cfgx->start_bus - 1;
 	}
 }
 
@@ -306,8 +334,7 @@
 	if (!raw_pci_ops)
 		return 0;
 
-	pci_mmcfg_config_num = 0;
-	pci_mmcfg_config = NULL;
+	free_all_mmcfg();
 
 	for (i = 0; i < ARRAY_SIZE(pci_mmcfg_probes); i++) {
 		bus =  pci_mmcfg_probes[i].bus;
@@ -322,45 +349,22 @@
 			name = pci_mmcfg_probes[i].probe();
 
 		if (name)
-			printk(KERN_INFO "PCI: Found %s with MMCONFIG support.\n",
+			printk(KERN_INFO PREFIX "%s with MMCONFIG support\n",
 			       name);
 	}
 
 	/* some end_bus_number is crazy, fix it */
 	pci_mmcfg_check_end_bus_number();
 
-	return pci_mmcfg_config_num != 0;
+	return !list_empty(&pci_mmcfg_list);
 }
 
 static void __init pci_mmcfg_insert_resources(void)
 {
-#define PCI_MMCFG_RESOURCE_NAME_LEN 24
-	int i;
-	struct resource *res;
-	char *names;
-	unsigned num_buses;
+	struct pci_mmcfg_region *cfg;
 
-	res = kcalloc(PCI_MMCFG_RESOURCE_NAME_LEN + sizeof(*res),
-			pci_mmcfg_config_num, GFP_KERNEL);
-	if (!res) {
-		printk(KERN_ERR "PCI: Unable to allocate MMCONFIG resources\n");
-		return;
-	}
-
-	names = (void *)&res[pci_mmcfg_config_num];
-	for (i = 0; i < pci_mmcfg_config_num; i++, res++) {
-		struct acpi_mcfg_allocation *cfg = &pci_mmcfg_config[i];
-		num_buses = cfg->end_bus_number - cfg->start_bus_number + 1;
-		res->name = names;
-		snprintf(names, PCI_MMCFG_RESOURCE_NAME_LEN,
-			 "PCI MMCONFIG %u [%02x-%02x]", cfg->pci_segment,
-			 cfg->start_bus_number, cfg->end_bus_number);
-		res->start = cfg->address + (cfg->start_bus_number << 20);
-		res->end = res->start + (num_buses << 20) - 1;
-		res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
-		insert_resource(&iomem_resource, res);
-		names += PCI_MMCFG_RESOURCE_NAME_LEN;
-	}
+	list_for_each_entry(cfg, &pci_mmcfg_list, list)
+		insert_resource(&iomem_resource, &cfg->res);
 
 	/* Mark that the resources have been inserted. */
 	pci_mmcfg_resources_inserted = 1;
@@ -437,11 +441,12 @@
 typedef int (*check_reserved_t)(u64 start, u64 end, unsigned type);
 
 static int __init is_mmconf_reserved(check_reserved_t is_reserved,
-		u64 addr, u64 size, int i,
-		typeof(pci_mmcfg_config[0]) *cfg, int with_e820)
+				    struct pci_mmcfg_region *cfg, int with_e820)
 {
+	u64 addr = cfg->res.start;
+	u64 size = resource_size(&cfg->res);
 	u64 old_size = size;
-	int valid = 0;
+	int valid = 0, num_buses;
 
 	while (!is_reserved(addr, addr + size, E820_RESERVED)) {
 		size >>= 1;
@@ -450,19 +455,25 @@
 	}
 
 	if (size >= (16UL<<20) || size == old_size) {
-		printk(KERN_NOTICE
-		       "PCI: MCFG area at %Lx reserved in %s\n",
-			addr, with_e820?"E820":"ACPI motherboard resources");
+		printk(KERN_INFO PREFIX "MMCONFIG at %pR reserved in %s\n",
+		       &cfg->res,
+		       with_e820 ? "E820" : "ACPI motherboard resources");
 		valid = 1;
 
 		if (old_size != size) {
-			/* update end_bus_number */
-			cfg->end_bus_number = cfg->start_bus_number + ((size>>20) - 1);
-			printk(KERN_NOTICE "PCI: updated MCFG configuration %d: base %lx "
-			       "segment %hu buses %u - %u\n",
-			       i, (unsigned long)cfg->address, cfg->pci_segment,
-			       (unsigned int)cfg->start_bus_number,
-			       (unsigned int)cfg->end_bus_number);
+			/* update end_bus */
+			cfg->end_bus = cfg->start_bus + ((size>>20) - 1);
+			num_buses = cfg->end_bus - cfg->start_bus + 1;
+			cfg->res.end = cfg->res.start +
+			    PCI_MMCFG_BUS_OFFSET(num_buses) - 1;
+			snprintf(cfg->name, PCI_MMCFG_RESOURCE_NAME_LEN,
+				 "PCI MMCONFIG %04x [bus %02x-%02x]",
+				 cfg->segment, cfg->start_bus, cfg->end_bus);
+			printk(KERN_INFO PREFIX
+			       "MMCONFIG for %04x [bus%02x-%02x] "
+			       "at %pR (base %#lx) (size reduced!)\n",
+			       cfg->segment, cfg->start_bus, cfg->end_bus,
+			       &cfg->res, (unsigned long) cfg->address);
 		}
 	}
 
@@ -471,45 +482,26 @@
 
 static void __init pci_mmcfg_reject_broken(int early)
 {
-	typeof(pci_mmcfg_config[0]) *cfg;
-	int i;
+	struct pci_mmcfg_region *cfg;
 
-	if ((pci_mmcfg_config_num == 0) ||
-	    (pci_mmcfg_config == NULL) ||
-	    (pci_mmcfg_config[0].address == 0))
-		return;
-
-	for (i = 0; i < pci_mmcfg_config_num; i++) {
+	list_for_each_entry(cfg, &pci_mmcfg_list, list) {
 		int valid = 0;
-		u64 addr, size;
-
-		cfg = &pci_mmcfg_config[i];
-		addr = cfg->start_bus_number;
-		addr <<= 20;
-		addr += cfg->address;
-		size = cfg->end_bus_number + 1 - cfg->start_bus_number;
-		size <<= 20;
-		printk(KERN_NOTICE "PCI: MCFG configuration %d: base %lx "
-		       "segment %hu buses %u - %u\n",
-		       i, (unsigned long)cfg->address, cfg->pci_segment,
-		       (unsigned int)cfg->start_bus_number,
-		       (unsigned int)cfg->end_bus_number);
 
 		if (!early && !acpi_disabled)
-			valid = is_mmconf_reserved(is_acpi_reserved, addr, size, i, cfg, 0);
+			valid = is_mmconf_reserved(is_acpi_reserved, cfg, 0);
 
 		if (valid)
 			continue;
 
 		if (!early)
-			printk(KERN_ERR "PCI: BIOS Bug: MCFG area at %Lx is not"
-			       " reserved in ACPI motherboard resources\n",
-			       cfg->address);
+			printk(KERN_ERR FW_BUG PREFIX
+			       "MMCONFIG at %pR not reserved in "
+			       "ACPI motherboard resources\n", &cfg->res);
 
 		/* Don't try to do this check unless configuration
 		   type 1 is available. how about type 2 ?*/
 		if (raw_pci_ops)
-			valid = is_mmconf_reserved(e820_all_mapped, addr, size, i, cfg, 1);
+			valid = is_mmconf_reserved(e820_all_mapped, cfg, 1);
 
 		if (!valid)
 			goto reject;
@@ -518,34 +510,41 @@
 	return;
 
 reject:
-	printk(KERN_INFO "PCI: Not using MMCONFIG.\n");
-	pci_mmcfg_arch_free();
-	kfree(pci_mmcfg_config);
-	pci_mmcfg_config = NULL;
-	pci_mmcfg_config_num = 0;
+	printk(KERN_INFO PREFIX "not using MMCONFIG\n");
+	free_all_mmcfg();
 }
 
 static int __initdata known_bridge;
 
-static int acpi_mcfg_64bit_base_addr __initdata = FALSE;
-
-/* The physical address of the MMCONFIG aperture.  Set from ACPI tables. */
-struct acpi_mcfg_allocation *pci_mmcfg_config;
-int pci_mmcfg_config_num;
-
-static int __init acpi_mcfg_oem_check(struct acpi_table_mcfg *mcfg)
+static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
+					struct acpi_mcfg_allocation *cfg)
 {
-	if (!strcmp(mcfg->header.oem_id, "SGI"))
-		acpi_mcfg_64bit_base_addr = TRUE;
+	int year;
 
-	return 0;
+	if (cfg->address < 0xFFFFFFFF)
+		return 0;
+
+	if (!strcmp(mcfg->header.oem_id, "SGI"))
+		return 0;
+
+	if (mcfg->header.revision >= 1) {
+		if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) &&
+		    year >= 2010)
+			return 0;
+	}
+
+	printk(KERN_ERR PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx "
+	       "is above 4GB, ignored\n", cfg->pci_segment,
+	       cfg->start_bus_number, cfg->end_bus_number, cfg->address);
+	return -EINVAL;
 }
 
 static int __init pci_parse_mcfg(struct acpi_table_header *header)
 {
 	struct acpi_table_mcfg *mcfg;
+	struct acpi_mcfg_allocation *cfg_table, *cfg;
 	unsigned long i;
-	int config_size;
+	int entries;
 
 	if (!header)
 		return -EINVAL;
@@ -553,38 +552,33 @@
 	mcfg = (struct acpi_table_mcfg *)header;
 
 	/* how many config structures do we have */
-	pci_mmcfg_config_num = 0;
+	free_all_mmcfg();
+	entries = 0;
 	i = header->length - sizeof(struct acpi_table_mcfg);
 	while (i >= sizeof(struct acpi_mcfg_allocation)) {
-		++pci_mmcfg_config_num;
+		entries++;
 		i -= sizeof(struct acpi_mcfg_allocation);
 	};
-	if (pci_mmcfg_config_num == 0) {
+	if (entries == 0) {
 		printk(KERN_ERR PREFIX "MMCONFIG has no entries\n");
 		return -ENODEV;
 	}
 
-	config_size = pci_mmcfg_config_num * sizeof(*pci_mmcfg_config);
-	pci_mmcfg_config = kmalloc(config_size, GFP_KERNEL);
-	if (!pci_mmcfg_config) {
-		printk(KERN_WARNING PREFIX
-		       "No memory for MCFG config tables\n");
-		return -ENOMEM;
-	}
-
-	memcpy(pci_mmcfg_config, &mcfg[1], config_size);
-
-	acpi_mcfg_oem_check(mcfg);
-
-	for (i = 0; i < pci_mmcfg_config_num; ++i) {
-		if ((pci_mmcfg_config[i].address > 0xFFFFFFFF) &&
-		    !acpi_mcfg_64bit_base_addr) {
-			printk(KERN_ERR PREFIX
-			       "MMCONFIG not in low 4GB of memory\n");
-			kfree(pci_mmcfg_config);
-			pci_mmcfg_config_num = 0;
+	cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1];
+	for (i = 0; i < entries; i++) {
+		cfg = &cfg_table[i];
+		if (acpi_mcfg_check_entry(mcfg, cfg)) {
+			free_all_mmcfg();
 			return -ENODEV;
 		}
+
+		if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number,
+				   cfg->end_bus_number, cfg->address) == NULL) {
+			printk(KERN_WARNING PREFIX
+			       "no memory for MCFG entries\n");
+			free_all_mmcfg();
+			return -ENOMEM;
+		}
 	}
 
 	return 0;
@@ -614,9 +608,7 @@
 
 	pci_mmcfg_reject_broken(early);
 
-	if ((pci_mmcfg_config_num == 0) ||
-	    (pci_mmcfg_config == NULL) ||
-	    (pci_mmcfg_config[0].address == 0))
+	if (list_empty(&pci_mmcfg_list))
 		return;
 
 	if (pci_mmcfg_arch_init())
@@ -648,9 +640,7 @@
 	 */
 	if ((pci_mmcfg_resources_inserted == 1) ||
 	    (pci_probe & PCI_PROBE_MMCONF) == 0 ||
-	    (pci_mmcfg_config_num == 0) ||
-	    (pci_mmcfg_config == NULL) ||
-	    (pci_mmcfg_config[0].address == 0))
+	    list_empty(&pci_mmcfg_list))
 		return 1;
 
 	/*
diff --git a/arch/x86/pci/mmconfig_32.c b/arch/x86/pci/mmconfig_32.c
index f10a7e9..90d5fd47 100644
--- a/arch/x86/pci/mmconfig_32.c
+++ b/arch/x86/pci/mmconfig_32.c
@@ -27,18 +27,10 @@
  */
 static u32 get_base_addr(unsigned int seg, int bus, unsigned devfn)
 {
-	struct acpi_mcfg_allocation *cfg;
-	int cfg_num;
+	struct pci_mmcfg_region *cfg = pci_mmconfig_lookup(seg, bus);
 
-	for (cfg_num = 0; cfg_num < pci_mmcfg_config_num; cfg_num++) {
-		cfg = &pci_mmcfg_config[cfg_num];
-		if (cfg->pci_segment == seg &&
-		    (cfg->start_bus_number <= bus) &&
-		    (cfg->end_bus_number >= bus))
-			return cfg->address;
-	}
-
-	/* Fall back to type 0 */
+	if (cfg)
+		return cfg->address;
 	return 0;
 }
 
@@ -47,7 +39,7 @@
  */
 static void pci_exp_set_dev_base(unsigned int base, int bus, int devfn)
 {
-	u32 dev_base = base | (bus << 20) | (devfn << 12);
+	u32 dev_base = base | PCI_MMCFG_BUS_OFFSET(bus) | (devfn << 12);
 	int cpu = smp_processor_id();
 	if (dev_base != mmcfg_last_accessed_device ||
 	    cpu != mmcfg_last_accessed_cpu) {
diff --git a/arch/x86/pci/mmconfig_64.c b/arch/x86/pci/mmconfig_64.c
index 94349f8..e783841 100644
--- a/arch/x86/pci/mmconfig_64.c
+++ b/arch/x86/pci/mmconfig_64.c
@@ -12,38 +12,15 @@
 #include <asm/e820.h>
 #include <asm/pci_x86.h>
 
-/* Static virtual mapping of the MMCONFIG aperture */
-struct mmcfg_virt {
-	struct acpi_mcfg_allocation *cfg;
-	char __iomem *virt;
-};
-static struct mmcfg_virt *pci_mmcfg_virt;
-
-static char __iomem *get_virt(unsigned int seg, unsigned bus)
-{
-	struct acpi_mcfg_allocation *cfg;
-	int cfg_num;
-
-	for (cfg_num = 0; cfg_num < pci_mmcfg_config_num; cfg_num++) {
-		cfg = pci_mmcfg_virt[cfg_num].cfg;
-		if (cfg->pci_segment == seg &&
-		    (cfg->start_bus_number <= bus) &&
-		    (cfg->end_bus_number >= bus))
-			return pci_mmcfg_virt[cfg_num].virt;
-	}
-
-	/* Fall back to type 0 */
-	return NULL;
-}
+#define PREFIX "PCI: "
 
 static char __iomem *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn)
 {
-	char __iomem *addr;
+	struct pci_mmcfg_region *cfg = pci_mmconfig_lookup(seg, bus);
 
-	addr = get_virt(seg, bus);
-	if (!addr)
-		return NULL;
- 	return addr + ((bus << 20) | (devfn << 12));
+	if (cfg && cfg->virt)
+		return cfg->virt + (PCI_MMCFG_BUS_OFFSET(bus) | (devfn << 12));
+	return NULL;
 }
 
 static int pci_mmcfg_read(unsigned int seg, unsigned int bus,
@@ -109,42 +86,30 @@
 	.write =	pci_mmcfg_write,
 };
 
-static void __iomem * __init mcfg_ioremap(struct acpi_mcfg_allocation *cfg)
+static void __iomem * __init mcfg_ioremap(struct pci_mmcfg_region *cfg)
 {
 	void __iomem *addr;
 	u64 start, size;
+	int num_buses;
 
-	start = cfg->start_bus_number;
-	start <<= 20;
-	start += cfg->address;
-	size = cfg->end_bus_number + 1 - cfg->start_bus_number;
-	size <<= 20;
+	start = cfg->address + PCI_MMCFG_BUS_OFFSET(cfg->start_bus);
+	num_buses = cfg->end_bus - cfg->start_bus + 1;
+	size = PCI_MMCFG_BUS_OFFSET(num_buses);
 	addr = ioremap_nocache(start, size);
-	if (addr) {
-		printk(KERN_INFO "PCI: Using MMCONFIG at %Lx - %Lx\n",
-		       start, start + size - 1);
-		addr -= cfg->start_bus_number << 20;
-	}
+	if (addr)
+		addr -= PCI_MMCFG_BUS_OFFSET(cfg->start_bus);
 	return addr;
 }
 
 int __init pci_mmcfg_arch_init(void)
 {
-	int i;
-	pci_mmcfg_virt = kzalloc(sizeof(*pci_mmcfg_virt) *
-				 pci_mmcfg_config_num, GFP_KERNEL);
-	if (pci_mmcfg_virt == NULL) {
-		printk(KERN_ERR "PCI: Can not allocate memory for mmconfig structures\n");
-		return 0;
-	}
+	struct pci_mmcfg_region *cfg;
 
-	for (i = 0; i < pci_mmcfg_config_num; ++i) {
-		pci_mmcfg_virt[i].cfg = &pci_mmcfg_config[i];
-		pci_mmcfg_virt[i].virt = mcfg_ioremap(&pci_mmcfg_config[i]);
-		if (!pci_mmcfg_virt[i].virt) {
-			printk(KERN_ERR "PCI: Cannot map mmconfig aperture for "
-					"segment %d\n",
-				pci_mmcfg_config[i].pci_segment);
+	list_for_each_entry(cfg, &pci_mmcfg_list, list) {
+		cfg->virt = mcfg_ioremap(cfg);
+		if (!cfg->virt) {
+			printk(KERN_ERR PREFIX "can't map MMCONFIG at %pR\n",
+			       &cfg->res);
 			pci_mmcfg_arch_free();
 			return 0;
 		}
@@ -155,19 +120,12 @@
 
 void __init pci_mmcfg_arch_free(void)
 {
-	int i;
+	struct pci_mmcfg_region *cfg;
 
-	if (pci_mmcfg_virt == NULL)
-		return;
-
-	for (i = 0; i < pci_mmcfg_config_num; ++i) {
-		if (pci_mmcfg_virt[i].virt) {
-			iounmap(pci_mmcfg_virt[i].virt + (pci_mmcfg_virt[i].cfg->start_bus_number << 20));
-			pci_mmcfg_virt[i].virt = NULL;
-			pci_mmcfg_virt[i].cfg = NULL;
+	list_for_each_entry(cfg, &pci_mmcfg_list, list) {
+		if (cfg->virt) {
+			iounmap(cfg->virt + PCI_MMCFG_BUS_OFFSET(cfg->start_bus));
+			cfg->virt = NULL;
 		}
 	}
-
-	kfree(pci_mmcfg_virt);
-	pci_mmcfg_virt = NULL;
 }
diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c
index b8e45f1..2b26dd5 100644
--- a/arch/x86/xen/enlighten.c
+++ b/arch/x86/xen/enlighten.c
@@ -27,7 +27,9 @@
 #include <linux/page-flags.h>
 #include <linux/highmem.h>
 #include <linux/console.h>
+#include <linux/pci.h>
 
+#include <xen/xen.h>
 #include <xen/interface/xen.h>
 #include <xen/interface/version.h>
 #include <xen/interface/physdev.h>
@@ -1175,7 +1177,11 @@
 		add_preferred_console("xenboot", 0, NULL);
 		add_preferred_console("tty", 0, NULL);
 		add_preferred_console("hvc", 0, NULL);
+	} else {
+		/* Make sure ACS will be enabled */
+		pci_request_acs();
 	}
+		
 
 	xen_raw_console_write("about to get started...\n");
 
diff --git a/arch/xtensa/include/asm/syscall.h b/arch/xtensa/include/asm/syscall.h
index 05cebf8..4352dbe 100644
--- a/arch/xtensa/include/asm/syscall.h
+++ b/arch/xtensa/include/asm/syscall.h
@@ -13,8 +13,6 @@
 asmlinkage long xtensa_execve(char*, char**, char**, struct pt_regs*);
 asmlinkage long xtensa_clone(unsigned long, unsigned long, struct pt_regs*);
 asmlinkage long xtensa_pipe(int __user *);
-asmlinkage long xtensa_mmap2(unsigned long, unsigned long, unsigned long,
-    			     unsigned long, unsigned long, unsigned long);
 asmlinkage long xtensa_ptrace(long, long, long, long);
 asmlinkage long xtensa_sigreturn(struct pt_regs*);
 asmlinkage long xtensa_rt_sigreturn(struct pt_regs*);
diff --git a/arch/xtensa/include/asm/unistd.h b/arch/xtensa/include/asm/unistd.h
index 4e55dc7..fbf318b 100644
--- a/arch/xtensa/include/asm/unistd.h
+++ b/arch/xtensa/include/asm/unistd.h
@@ -189,7 +189,7 @@
 /* File Map / Shared Memory Operations */
 
 #define __NR_mmap2 				 80
-__SYSCALL( 80, xtensa_mmap2, 6)
+__SYSCALL( 80, sys_mmap_pgoff, 6)
 #define __NR_munmap 				 81
 __SYSCALL( 81, sys_munmap, 2)
 #define __NR_mprotect 				 82
diff --git a/arch/xtensa/kernel/syscall.c b/arch/xtensa/kernel/syscall.c
index ac15ecb..1e67bab 100644
--- a/arch/xtensa/kernel/syscall.c
+++ b/arch/xtensa/kernel/syscall.c
@@ -57,31 +57,6 @@
 	return error;
 }
 
-
-asmlinkage long xtensa_mmap2(unsigned long addr, unsigned long len,
-   			     unsigned long prot, unsigned long flags,
-			     unsigned long fd, unsigned long pgoff)
-{
-	int error = -EBADF;
-	struct file * file = NULL;
-
-	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
-	if (!(flags & MAP_ANONYMOUS)) {
-		file = fget(fd);
-		if (!file)
-			goto out;
-	}
-
-	down_write(&current->mm->mmap_sem);
-	error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
-	up_write(&current->mm->mmap_sem);
-
-	if (file)
-		fput(file);
-out:
-	return error;
-}
-
 asmlinkage long xtensa_shmat(int shmid, char __user *shmaddr, int shmflg)
 {
 	unsigned long ret;
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 7702118..c7b10b4 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -19,6 +19,7 @@
 
 # All the builtin files are in the "acpi." module_param namespace.
 acpi-y				+= osl.o utils.o reboot.o
+acpi-y				+= hest.o
 
 # sleep related files
 acpi-y				+= wakeup.o
diff --git a/drivers/acpi/hest.c b/drivers/acpi/hest.c
new file mode 100644
index 0000000..4bb18c9
--- /dev/null
+++ b/drivers/acpi/hest.c
@@ -0,0 +1,135 @@
+#include <linux/acpi.h>
+#include <linux/pci.h>
+
+#define PREFIX "ACPI: "
+
+static inline unsigned long parse_acpi_hest_ia_machine_check(struct acpi_hest_ia_machine_check *p)
+{
+	return sizeof(*p) +
+		(sizeof(struct acpi_hest_ia_error_bank) * p->num_hardware_banks);
+}
+
+static inline unsigned long parse_acpi_hest_ia_corrected(struct acpi_hest_ia_corrected *p)
+{
+	return sizeof(*p) +
+		(sizeof(struct acpi_hest_ia_error_bank) * p->num_hardware_banks);
+}
+
+static inline unsigned long parse_acpi_hest_ia_nmi(struct acpi_hest_ia_nmi *p)
+{
+	return sizeof(*p);
+}
+
+static inline unsigned long parse_acpi_hest_generic(struct acpi_hest_generic *p)
+{
+	return sizeof(*p);
+}
+
+static inline unsigned int hest_match_pci(struct acpi_hest_aer_common *p, struct pci_dev *pci)
+{
+	return	(0           == pci_domain_nr(pci->bus) &&
+		 p->bus      == pci->bus->number &&
+		 p->device   == PCI_SLOT(pci->devfn) &&
+		 p->function == PCI_FUNC(pci->devfn));
+}
+
+static unsigned long parse_acpi_hest_aer(void *hdr, int type, struct pci_dev *pci, int *firmware_first)
+{
+	struct acpi_hest_aer_common *p = hdr + sizeof(struct acpi_hest_header);
+	unsigned long rc=0;
+	u8 pcie_type = 0;
+	u8 bridge = 0;
+	switch (type) {
+	case ACPI_HEST_TYPE_AER_ROOT_PORT:
+		rc = sizeof(struct acpi_hest_aer_root);
+		pcie_type = PCI_EXP_TYPE_ROOT_PORT;
+		break;
+	case ACPI_HEST_TYPE_AER_ENDPOINT:
+		rc = sizeof(struct acpi_hest_aer);
+		pcie_type = PCI_EXP_TYPE_ENDPOINT;
+		break;
+	case ACPI_HEST_TYPE_AER_BRIDGE:
+		rc = sizeof(struct acpi_hest_aer_bridge);
+		if ((pci->class >> 16) == PCI_BASE_CLASS_BRIDGE)
+			bridge = 1;
+		break;
+	}
+
+	if (p->flags & ACPI_HEST_GLOBAL) {
+		if ((pci->is_pcie && (pci->pcie_type == pcie_type)) || bridge)
+			*firmware_first = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST);
+	}
+	else
+		if (hest_match_pci(p, pci))
+			*firmware_first = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST);
+	return rc;
+}
+
+static int acpi_hest_firmware_first(struct acpi_table_header *stdheader, struct pci_dev *pci)
+{
+	struct acpi_table_hest *hest = (struct acpi_table_hest *)stdheader;
+	void *p = (void *)hest + sizeof(*hest); /* defined by the ACPI 4.0 spec */
+	struct acpi_hest_header *hdr = p;
+
+	int i;
+	int firmware_first = 0;
+	static unsigned char printed_unused = 0;
+	static unsigned char printed_reserved = 0;
+
+	for (i=0, hdr=p; p < (((void *)hest) + hest->header.length) && i < hest->error_source_count; i++) {
+		switch (hdr->type) {
+		case ACPI_HEST_TYPE_IA32_CHECK:
+			p += parse_acpi_hest_ia_machine_check(p);
+			break;
+		case ACPI_HEST_TYPE_IA32_CORRECTED_CHECK:
+			p += parse_acpi_hest_ia_corrected(p);
+			break;
+		case ACPI_HEST_TYPE_IA32_NMI:
+			p += parse_acpi_hest_ia_nmi(p);
+			break;
+		/* These three should never appear */
+		case ACPI_HEST_TYPE_NOT_USED3:
+		case ACPI_HEST_TYPE_NOT_USED4:
+		case ACPI_HEST_TYPE_NOT_USED5:
+			if (!printed_unused) {
+				printk(KERN_DEBUG PREFIX
+				       "HEST Error Source list contains an obsolete type (%d).\n", hdr->type);
+				printed_unused = 1;
+			}
+			break;
+		case ACPI_HEST_TYPE_AER_ROOT_PORT:
+		case ACPI_HEST_TYPE_AER_ENDPOINT:
+		case ACPI_HEST_TYPE_AER_BRIDGE:
+			p += parse_acpi_hest_aer(p, hdr->type, pci, &firmware_first);
+			break;
+		case ACPI_HEST_TYPE_GENERIC_ERROR:
+			p += parse_acpi_hest_generic(p);
+			break;
+		/* These should never appear either */
+		case ACPI_HEST_TYPE_RESERVED:
+		default:
+			if (!printed_reserved) {
+				printk(KERN_DEBUG PREFIX
+				       "HEST Error Source list contains a reserved type (%d).\n", hdr->type);
+				printed_reserved = 1;
+			}
+			break;
+		}
+	}
+	return firmware_first;
+}
+
+int acpi_hest_firmware_first_pci(struct pci_dev *pci)
+{
+	acpi_status status = AE_NOT_FOUND;
+	struct acpi_table_header *hest = NULL;
+	status = acpi_get_table(ACPI_SIG_HEST, 1, &hest);
+
+	if (ACPI_SUCCESS(status)) {
+		if (acpi_hest_firmware_first(hest, pci)) {
+			return 1;
+		}
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_hest_firmware_first_pci);
diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c
index b8578bb..05a31e5 100644
--- a/drivers/block/xen-blkfront.c
+++ b/drivers/block/xen-blkfront.c
@@ -42,6 +42,7 @@
 #include <linux/module.h>
 #include <linux/scatterlist.h>
 
+#include <xen/xen.h>
 #include <xen/xenbus.h>
 #include <xen/grant_table.h>
 #include <xen/events.h>
diff --git a/drivers/char/hvc_xen.c b/drivers/char/hvc_xen.c
index a6ee32b..b1a7163 100644
--- a/drivers/char/hvc_xen.c
+++ b/drivers/char/hvc_xen.c
@@ -25,6 +25,8 @@
 #include <linux/types.h>
 
 #include <asm/xen/hypervisor.h>
+
+#include <xen/xen.h>
 #include <xen/page.h>
 #include <xen/events.h>
 #include <xen/interface/io/console.h>
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 91567ac..470ef67 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -31,3 +31,5 @@
 obj-$(CONFIG_DRM_SIS)   += sis/
 obj-$(CONFIG_DRM_SAVAGE)+= savage/
 obj-$(CONFIG_DRM_VIA)	+=via/
+obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
+obj-y			+= i2c/
diff --git a/drivers/gpu/drm/i2c/Makefile b/drivers/gpu/drm/i2c/Makefile
new file mode 100644
index 0000000..6d2abaf
--- /dev/null
+++ b/drivers/gpu/drm/i2c/Makefile
@@ -0,0 +1,4 @@
+ccflags-y := -Iinclude/drm
+
+ch7006-y := ch7006_drv.o ch7006_mode.o
+obj-$(CONFIG_DRM_I2C_CH7006) += ch7006.o
diff --git a/drivers/gpu/drm/i2c/ch7006_drv.c b/drivers/gpu/drm/i2c/ch7006_drv.c
new file mode 100644
index 0000000..9422a74
--- /dev/null
+++ b/drivers/gpu/drm/i2c/ch7006_drv.c
@@ -0,0 +1,531 @@
+/*
+ * Copyright (C) 2009 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "ch7006_priv.h"
+
+/* DRM encoder functions */
+
+static void ch7006_encoder_set_config(struct drm_encoder *encoder,
+				      void *params)
+{
+	struct ch7006_priv *priv = to_ch7006_priv(encoder);
+
+	priv->params = params;
+}
+
+static void ch7006_encoder_destroy(struct drm_encoder *encoder)
+{
+	struct ch7006_priv *priv = to_ch7006_priv(encoder);
+
+	drm_property_destroy(encoder->dev, priv->scale_property);
+
+	kfree(priv);
+	to_encoder_slave(encoder)->slave_priv = NULL;
+
+	drm_i2c_encoder_destroy(encoder);
+}
+
+static void  ch7006_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+	struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+	struct ch7006_priv *priv = to_ch7006_priv(encoder);
+	struct ch7006_state *state = &priv->state;
+
+	ch7006_dbg(client, "\n");
+
+	if (mode == priv->last_dpms)
+		return;
+	priv->last_dpms = mode;
+
+	ch7006_setup_power_state(encoder);
+
+	ch7006_load_reg(client, state, CH7006_POWER);
+}
+
+static void ch7006_encoder_save(struct drm_encoder *encoder)
+{
+	struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+	struct ch7006_priv *priv = to_ch7006_priv(encoder);
+
+	ch7006_dbg(client, "\n");
+
+	ch7006_state_save(client, &priv->saved_state);
+}
+
+static void ch7006_encoder_restore(struct drm_encoder *encoder)
+{
+	struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+	struct ch7006_priv *priv = to_ch7006_priv(encoder);
+
+	ch7006_dbg(client, "\n");
+
+	ch7006_state_load(client, &priv->saved_state);
+}
+
+static bool ch7006_encoder_mode_fixup(struct drm_encoder *encoder,
+				      struct drm_display_mode *mode,
+				      struct drm_display_mode *adjusted_mode)
+{
+	struct ch7006_priv *priv = to_ch7006_priv(encoder);
+
+	/* The ch7006 is painfully picky with the input timings so no
+	 * custom modes for now... */
+
+	priv->mode = ch7006_lookup_mode(encoder, mode);
+
+	return !!priv->mode;
+}
+
+static int ch7006_encoder_mode_valid(struct drm_encoder *encoder,
+				     struct drm_display_mode *mode)
+{
+	if (ch7006_lookup_mode(encoder, mode))
+		return MODE_OK;
+	else
+		return MODE_BAD;
+}
+
+static void ch7006_encoder_mode_set(struct drm_encoder *encoder,
+				     struct drm_display_mode *drm_mode,
+				     struct drm_display_mode *adjusted_mode)
+{
+	struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+	struct ch7006_priv *priv = to_ch7006_priv(encoder);
+	struct ch7006_encoder_params *params = priv->params;
+	struct ch7006_state *state = &priv->state;
+	uint8_t *regs = state->regs;
+	struct ch7006_mode *mode = priv->mode;
+	struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
+	int start_active;
+
+	ch7006_dbg(client, "\n");
+
+	regs[CH7006_DISPMODE] = norm->dispmode | mode->dispmode;
+	regs[CH7006_BWIDTH] = 0;
+	regs[CH7006_INPUT_FORMAT] = bitf(CH7006_INPUT_FORMAT_FORMAT,
+					 params->input_format);
+
+	regs[CH7006_CLKMODE] = CH7006_CLKMODE_SUBC_LOCK
+		| bitf(CH7006_CLKMODE_XCM, params->xcm)
+		| bitf(CH7006_CLKMODE_PCM, params->pcm);
+	if (params->clock_mode)
+		regs[CH7006_CLKMODE] |= CH7006_CLKMODE_MASTER;
+	if (params->clock_edge)
+		regs[CH7006_CLKMODE] |= CH7006_CLKMODE_POS_EDGE;
+
+	start_active = (drm_mode->htotal & ~0x7) - (drm_mode->hsync_start & ~0x7);
+	regs[CH7006_POV] = bitf(CH7006_POV_START_ACTIVE_8, start_active);
+	regs[CH7006_START_ACTIVE] = bitf(CH7006_START_ACTIVE_0, start_active);
+
+	regs[CH7006_INPUT_SYNC] = 0;
+	if (params->sync_direction)
+		regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_OUTPUT;
+	if (params->sync_encoding)
+		regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_EMBEDDED;
+	if (drm_mode->flags & DRM_MODE_FLAG_PVSYNC)
+		regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_PVSYNC;
+	if (drm_mode->flags & DRM_MODE_FLAG_PHSYNC)
+		regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_PHSYNC;
+
+	regs[CH7006_DETECT] = 0;
+	regs[CH7006_BCLKOUT] = 0;
+
+	regs[CH7006_SUBC_INC3] = 0;
+	if (params->pout_level)
+		regs[CH7006_SUBC_INC3] |= CH7006_SUBC_INC3_POUT_3_3V;
+
+	regs[CH7006_SUBC_INC4] = 0;
+	if (params->active_detect)
+		regs[CH7006_SUBC_INC4] |= CH7006_SUBC_INC4_DS_INPUT;
+
+	regs[CH7006_PLL_CONTROL] = priv->saved_state.regs[CH7006_PLL_CONTROL];
+
+	ch7006_setup_levels(encoder);
+	ch7006_setup_subcarrier(encoder);
+	ch7006_setup_pll(encoder);
+	ch7006_setup_power_state(encoder);
+	ch7006_setup_properties(encoder);
+
+	ch7006_state_load(client, state);
+}
+
+static enum drm_connector_status ch7006_encoder_detect(struct drm_encoder *encoder,
+						       struct drm_connector *connector)
+{
+	struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+	struct ch7006_priv *priv = to_ch7006_priv(encoder);
+	struct ch7006_state *state = &priv->state;
+	int det;
+
+	ch7006_dbg(client, "\n");
+
+	ch7006_save_reg(client, state, CH7006_DETECT);
+	ch7006_save_reg(client, state, CH7006_POWER);
+	ch7006_save_reg(client, state, CH7006_CLKMODE);
+
+	ch7006_write(client, CH7006_POWER, CH7006_POWER_RESET |
+					   bitfs(CH7006_POWER_LEVEL, NORMAL));
+	ch7006_write(client, CH7006_CLKMODE, CH7006_CLKMODE_MASTER);
+
+	ch7006_write(client, CH7006_DETECT, CH7006_DETECT_SENSE);
+
+	ch7006_write(client, CH7006_DETECT, 0);
+
+	det = ch7006_read(client, CH7006_DETECT);
+
+	ch7006_load_reg(client, state, CH7006_CLKMODE);
+	ch7006_load_reg(client, state, CH7006_POWER);
+	ch7006_load_reg(client, state, CH7006_DETECT);
+
+	if ((det & (CH7006_DETECT_SVIDEO_Y_TEST|
+		    CH7006_DETECT_SVIDEO_C_TEST|
+		    CH7006_DETECT_CVBS_TEST)) == 0)
+		priv->subconnector = DRM_MODE_SUBCONNECTOR_SCART;
+	else if ((det & (CH7006_DETECT_SVIDEO_Y_TEST|
+			 CH7006_DETECT_SVIDEO_C_TEST)) == 0)
+		priv->subconnector = DRM_MODE_SUBCONNECTOR_SVIDEO;
+	else if ((det & CH7006_DETECT_CVBS_TEST) == 0)
+		priv->subconnector = DRM_MODE_SUBCONNECTOR_Composite;
+	else
+		priv->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
+
+	drm_connector_property_set_value(connector,
+			encoder->dev->mode_config.tv_subconnector_property,
+							priv->subconnector);
+
+	return priv->subconnector ? connector_status_connected :
+					connector_status_disconnected;
+}
+
+static int ch7006_encoder_get_modes(struct drm_encoder *encoder,
+				    struct drm_connector *connector)
+{
+	struct ch7006_priv *priv = to_ch7006_priv(encoder);
+	struct ch7006_mode *mode;
+	int n = 0;
+
+	for (mode = ch7006_modes; mode->mode.clock; mode++) {
+		if (~mode->valid_scales & 1<<priv->scale ||
+		    ~mode->valid_norms & 1<<priv->norm)
+			continue;
+
+		drm_mode_probed_add(connector,
+				drm_mode_duplicate(encoder->dev, &mode->mode));
+
+		n++;
+	}
+
+	return n;
+}
+
+static int ch7006_encoder_create_resources(struct drm_encoder *encoder,
+					   struct drm_connector *connector)
+{
+	struct ch7006_priv *priv = to_ch7006_priv(encoder);
+	struct drm_device *dev = encoder->dev;
+	struct drm_mode_config *conf = &dev->mode_config;
+
+	drm_mode_create_tv_properties(dev, NUM_TV_NORMS, ch7006_tv_norm_names);
+
+	priv->scale_property = drm_property_create(dev, DRM_MODE_PROP_RANGE,
+						   "scale", 2);
+	priv->scale_property->values[0] = 0;
+	priv->scale_property->values[1] = 2;
+
+	drm_connector_attach_property(connector, conf->tv_select_subconnector_property,
+				      priv->select_subconnector);
+	drm_connector_attach_property(connector, conf->tv_subconnector_property,
+				      priv->subconnector);
+	drm_connector_attach_property(connector, conf->tv_left_margin_property,
+				      priv->hmargin);
+	drm_connector_attach_property(connector, conf->tv_bottom_margin_property,
+				      priv->vmargin);
+	drm_connector_attach_property(connector, conf->tv_mode_property,
+				      priv->norm);
+	drm_connector_attach_property(connector, conf->tv_brightness_property,
+				      priv->brightness);
+	drm_connector_attach_property(connector, conf->tv_contrast_property,
+				      priv->contrast);
+	drm_connector_attach_property(connector, conf->tv_flicker_reduction_property,
+				      priv->flicker);
+	drm_connector_attach_property(connector, priv->scale_property,
+				      priv->scale);
+
+	return 0;
+}
+
+static int ch7006_encoder_set_property(struct drm_encoder *encoder,
+				       struct drm_connector *connector,
+				       struct drm_property *property,
+				       uint64_t val)
+{
+	struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+	struct ch7006_priv *priv = to_ch7006_priv(encoder);
+	struct ch7006_state *state = &priv->state;
+	struct drm_mode_config *conf = &encoder->dev->mode_config;
+	struct drm_crtc *crtc = encoder->crtc;
+	bool modes_changed = false;
+
+	ch7006_dbg(client, "\n");
+
+	if (property == conf->tv_select_subconnector_property) {
+		priv->select_subconnector = val;
+
+		ch7006_setup_power_state(encoder);
+
+		ch7006_load_reg(client, state, CH7006_POWER);
+
+	} else if (property == conf->tv_left_margin_property) {
+		priv->hmargin = val;
+
+		ch7006_setup_properties(encoder);
+
+		ch7006_load_reg(client, state, CH7006_POV);
+		ch7006_load_reg(client, state, CH7006_HPOS);
+
+	} else if (property == conf->tv_bottom_margin_property) {
+		priv->vmargin = val;
+
+		ch7006_setup_properties(encoder);
+
+		ch7006_load_reg(client, state, CH7006_POV);
+		ch7006_load_reg(client, state, CH7006_VPOS);
+
+	} else if (property == conf->tv_mode_property) {
+		if (connector->dpms != DRM_MODE_DPMS_OFF)
+			return -EINVAL;
+
+		priv->norm = val;
+
+		modes_changed = true;
+
+	} else if (property == conf->tv_brightness_property) {
+		priv->brightness = val;
+
+		ch7006_setup_levels(encoder);
+
+		ch7006_load_reg(client, state, CH7006_BLACK_LEVEL);
+
+	} else if (property == conf->tv_contrast_property) {
+		priv->contrast = val;
+
+		ch7006_setup_properties(encoder);
+
+		ch7006_load_reg(client, state, CH7006_CONTRAST);
+
+	} else if (property == conf->tv_flicker_reduction_property) {
+		priv->flicker = val;
+
+		ch7006_setup_properties(encoder);
+
+		ch7006_load_reg(client, state, CH7006_FFILTER);
+
+	} else if (property == priv->scale_property) {
+		if (connector->dpms != DRM_MODE_DPMS_OFF)
+			return -EINVAL;
+
+		priv->scale = val;
+
+		modes_changed = true;
+
+	} else {
+		return -EINVAL;
+	}
+
+	if (modes_changed) {
+		drm_helper_probe_single_connector_modes(connector, 0, 0);
+
+		/* Disable the crtc to ensure a full modeset is
+		 * performed whenever it's turned on again. */
+		if (crtc) {
+			struct drm_mode_set modeset = {
+				.crtc = crtc,
+			};
+
+			crtc->funcs->set_config(&modeset);
+		}
+	}
+
+	return 0;
+}
+
+static struct drm_encoder_slave_funcs ch7006_encoder_funcs = {
+	.set_config = ch7006_encoder_set_config,
+	.destroy = ch7006_encoder_destroy,
+	.dpms = ch7006_encoder_dpms,
+	.save = ch7006_encoder_save,
+	.restore = ch7006_encoder_restore,
+	.mode_fixup = ch7006_encoder_mode_fixup,
+	.mode_valid = ch7006_encoder_mode_valid,
+	.mode_set = ch7006_encoder_mode_set,
+	.detect = ch7006_encoder_detect,
+	.get_modes = ch7006_encoder_get_modes,
+	.create_resources = ch7006_encoder_create_resources,
+	.set_property = ch7006_encoder_set_property,
+};
+
+
+/* I2C driver functions */
+
+static int ch7006_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+	uint8_t addr = CH7006_VERSION_ID;
+	uint8_t val;
+	int ret;
+
+	ch7006_dbg(client, "\n");
+
+	ret = i2c_master_send(client, &addr, sizeof(addr));
+	if (ret < 0)
+		goto fail;
+
+	ret = i2c_master_recv(client, &val, sizeof(val));
+	if (ret < 0)
+		goto fail;
+
+	ch7006_info(client, "Detected version ID: %x\n", val);
+
+	return 0;
+
+fail:
+	ch7006_err(client, "Error %d reading version ID\n", ret);
+
+	return -ENODEV;
+}
+
+static int ch7006_remove(struct i2c_client *client)
+{
+	ch7006_dbg(client, "\n");
+
+	return 0;
+}
+
+static int ch7006_encoder_init(struct i2c_client *client,
+			       struct drm_device *dev,
+			       struct drm_encoder_slave *encoder)
+{
+	struct ch7006_priv *priv;
+	int i;
+
+	ch7006_dbg(client, "\n");
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	encoder->slave_priv = priv;
+	encoder->slave_funcs = &ch7006_encoder_funcs;
+
+	priv->norm = TV_NORM_PAL;
+	priv->select_subconnector = DRM_MODE_SUBCONNECTOR_Automatic;
+	priv->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
+	priv->scale = 1;
+	priv->contrast = 50;
+	priv->brightness = 50;
+	priv->flicker = 50;
+	priv->hmargin = 50;
+	priv->vmargin = 50;
+	priv->last_dpms = -1;
+
+	if (ch7006_tv_norm) {
+		for (i = 0; i < NUM_TV_NORMS; i++) {
+			if (!strcmp(ch7006_tv_norm_names[i], ch7006_tv_norm)) {
+				priv->norm = i;
+				break;
+			}
+		}
+
+		if (i == NUM_TV_NORMS)
+			ch7006_err(client, "Invalid TV norm setting \"%s\".\n",
+				   ch7006_tv_norm);
+	}
+
+	if (ch7006_scale >= 0 && ch7006_scale <= 2)
+		priv->scale = ch7006_scale;
+	else
+		ch7006_err(client, "Invalid scale setting \"%d\".\n",
+			   ch7006_scale);
+
+	return 0;
+}
+
+static struct i2c_device_id ch7006_ids[] = {
+	{ "ch7006", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ch7006_ids);
+
+static struct drm_i2c_encoder_driver ch7006_driver = {
+	.i2c_driver = {
+		.probe = ch7006_probe,
+		.remove = ch7006_remove,
+
+		.driver = {
+			.name = "ch7006",
+		},
+
+		.id_table = ch7006_ids,
+	},
+
+	.encoder_init = ch7006_encoder_init,
+};
+
+
+/* Module initialization */
+
+static int __init ch7006_init(void)
+{
+	return drm_i2c_encoder_register(THIS_MODULE, &ch7006_driver);
+}
+
+static void __exit ch7006_exit(void)
+{
+	drm_i2c_encoder_unregister(&ch7006_driver);
+}
+
+int ch7006_debug;
+module_param_named(debug, ch7006_debug, int, 0600);
+MODULE_PARM_DESC(debug, "Enable debug output.");
+
+char *ch7006_tv_norm;
+module_param_named(tv_norm, ch7006_tv_norm, charp, 0600);
+MODULE_PARM_DESC(tv_norm, "Default TV norm.\n"
+		 "\t\tSupported: PAL, PAL-M, PAL-N, PAL-Nc, PAL-60, NTSC-M, NTSC-J.\n"
+		 "\t\tDefault: PAL");
+
+int ch7006_scale = 1;
+module_param_named(scale, ch7006_scale, int, 0600);
+MODULE_PARM_DESC(scale, "Default scale.\n"
+		 "\t\tSupported: 0 -> Select video modes with a higher blanking ratio.\n"
+		 "\t\t\t1 -> Select default video modes.\n"
+		 "\t\t\t2 -> Select video modes with a lower blanking ratio.");
+
+MODULE_AUTHOR("Francisco Jerez <currojerez@riseup.net>");
+MODULE_DESCRIPTION("Chrontel ch7006 TV encoder driver");
+MODULE_LICENSE("GPL and additional rights");
+
+module_init(ch7006_init);
+module_exit(ch7006_exit);
diff --git a/drivers/gpu/drm/i2c/ch7006_mode.c b/drivers/gpu/drm/i2c/ch7006_mode.c
new file mode 100644
index 0000000..87f5445
--- /dev/null
+++ b/drivers/gpu/drm/i2c/ch7006_mode.c
@@ -0,0 +1,473 @@
+/*
+ * Copyright (C) 2009 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "ch7006_priv.h"
+
+char *ch7006_tv_norm_names[] = {
+	[TV_NORM_PAL] = "PAL",
+	[TV_NORM_PAL_M] = "PAL-M",
+	[TV_NORM_PAL_N] = "PAL-N",
+	[TV_NORM_PAL_NC] = "PAL-Nc",
+	[TV_NORM_PAL_60] = "PAL-60",
+	[TV_NORM_NTSC_M] = "NTSC-M",
+	[TV_NORM_NTSC_J] = "NTSC-J",
+};
+
+#define NTSC_LIKE_TIMINGS .vrefresh = 60 * fixed1/1.001,		\
+		.vdisplay = 480,					\
+		.vtotal = 525,						\
+		.hvirtual = 660
+
+#define PAL_LIKE_TIMINGS .vrefresh = 50 * fixed1,		\
+		.vdisplay = 576,				\
+		.vtotal = 625,					\
+		.hvirtual = 810
+
+struct ch7006_tv_norm_info ch7006_tv_norms[] = {
+	[TV_NORM_NTSC_M] = {
+		NTSC_LIKE_TIMINGS,
+		.black_level = 0.339 * fixed1,
+		.subc_freq = 3579545 * fixed1,
+		.dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, NTSC),
+		.voffset = 0,
+	},
+	[TV_NORM_NTSC_J] = {
+		NTSC_LIKE_TIMINGS,
+		.black_level = 0.286 * fixed1,
+		.subc_freq = 3579545 * fixed1,
+		.dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, NTSC_J),
+		.voffset = 0,
+	},
+	[TV_NORM_PAL] = {
+		PAL_LIKE_TIMINGS,
+		.black_level = 0.3 * fixed1,
+		.subc_freq = 4433618.75 * fixed1,
+		.dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL),
+		.voffset = 0,
+	},
+	[TV_NORM_PAL_M] = {
+		NTSC_LIKE_TIMINGS,
+		.black_level = 0.339 * fixed1,
+		.subc_freq = 3575611.433 * fixed1,
+		.dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL_M),
+		.voffset = 16,
+	},
+
+	/* The following modes seem to work right but they're
+	 * undocumented */
+
+	[TV_NORM_PAL_N] = {
+		PAL_LIKE_TIMINGS,
+		.black_level = 0.339 * fixed1,
+		.subc_freq = 4433618.75 * fixed1,
+		.dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL),
+		.voffset = 0,
+	},
+	[TV_NORM_PAL_NC] = {
+		PAL_LIKE_TIMINGS,
+		.black_level = 0.3 * fixed1,
+		.subc_freq = 3582056.25 * fixed1,
+		.dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL),
+		.voffset = 0,
+	},
+	[TV_NORM_PAL_60] = {
+		NTSC_LIKE_TIMINGS,
+		.black_level = 0.3 * fixed1,
+		.subc_freq = 4433618.75 * fixed1,
+		.dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL_M),
+		.voffset = 16,
+	},
+};
+
+#define __MODE(f, hd, vd, ht, vt, hsynp, vsynp,				\
+	       subc, scale, scale_mask, norm_mask, e_hd, e_vd) {	\
+		.mode = {						\
+			.name = #hd "x" #vd,				\
+			.status = 0,					\
+			.type = DRM_MODE_TYPE_DRIVER,			\
+			.clock = f,					\
+			.hdisplay = hd,					\
+			.hsync_start = e_hd + 16,			\
+			.hsync_end = e_hd + 80,				\
+			.htotal = ht,					\
+			.hskew = 0,					\
+			.vdisplay = vd,					\
+			.vsync_start = vd + 10,				\
+			.vsync_end = vd + 26,				\
+			.vtotal = vt,					\
+			.vscan = 0,					\
+			.flags = DRM_MODE_FLAG_##hsynp##HSYNC |		\
+				DRM_MODE_FLAG_##vsynp##VSYNC,		\
+			.vrefresh = 0,					\
+		},							\
+		.enc_hdisp = e_hd,					\
+		.enc_vdisp = e_vd,					\
+		.subc_coeff = subc * fixed1,				\
+		.dispmode = bitfs(CH7006_DISPMODE_SCALING_RATIO, scale) | \
+			    bitfs(CH7006_DISPMODE_INPUT_RES, e_hd##x##e_vd), \
+		.valid_scales = scale_mask,				\
+		.valid_norms = norm_mask				\
+	 }
+
+#define MODE(f, hd, vd, ht, vt, hsynp, vsynp,				\
+	     subc, scale, scale_mask, norm_mask)			\
+	__MODE(f, hd, vd, ht, vt, hsynp, vsynp, subc, scale,		\
+	       scale_mask, norm_mask, hd, vd)
+
+#define NTSC_LIKE (1 << TV_NORM_NTSC_M | 1 << TV_NORM_NTSC_J |		\
+		   1 << TV_NORM_PAL_M | 1 << TV_NORM_PAL_60)
+
+#define PAL_LIKE (1 << TV_NORM_PAL | 1 << TV_NORM_PAL_N | 1 << TV_NORM_PAL_NC)
+
+struct ch7006_mode ch7006_modes[] = {
+	MODE(21000, 512, 384, 840, 500, N, N, 181.797557582, 5_4, 0x6, PAL_LIKE),
+	MODE(26250, 512, 384, 840, 625, N, N, 145.438046066, 1_1, 0x1, PAL_LIKE),
+	MODE(20140, 512, 384, 800, 420, N, N, 213.257083791, 5_4, 0x4, NTSC_LIKE),
+	MODE(24671, 512, 384, 784, 525, N, N, 174.0874153, 1_1, 0x3, NTSC_LIKE),
+	MODE(28125, 720, 400, 1125, 500, N, N, 135.742176298, 5_4, 0x6, PAL_LIKE),
+	MODE(34875, 720, 400, 1116, 625, N, N, 109.469496898, 1_1, 0x1, PAL_LIKE),
+	MODE(23790, 720, 400, 945, 420, N, N, 160.475642016, 5_4, 0x4, NTSC_LIKE),
+	MODE(29455, 720, 400, 936, 525, N, N, 129.614941843, 1_1, 0x3, NTSC_LIKE),
+	MODE(25000, 640, 400, 1000, 500, N, N, 152.709948279, 5_4, 0x6, PAL_LIKE),
+	MODE(31500, 640, 400, 1008, 625, N, N, 121.198371646, 1_1, 0x1, PAL_LIKE),
+	MODE(21147, 640, 400, 840, 420, N, N, 180.535097338, 5_4, 0x4, NTSC_LIKE),
+	MODE(26434, 640, 400, 840, 525, N, N, 144.42807787, 1_1, 0x2, NTSC_LIKE),
+	MODE(30210, 640, 400, 840, 600, N, N, 126.374568276, 7_8, 0x1, NTSC_LIKE),
+	MODE(21000, 640, 480, 840, 500, N, N, 181.797557582, 5_4, 0x4, PAL_LIKE),
+	MODE(26250, 640, 480, 840, 625, N, N, 145.438046066, 1_1, 0x2, PAL_LIKE),
+	MODE(31500, 640, 480, 840, 750, N, N, 121.198371646, 5_6, 0x1, PAL_LIKE),
+	MODE(24671, 640, 480, 784, 525, N, N, 174.0874153, 1_1, 0x4, NTSC_LIKE),
+	MODE(28196, 640, 480, 784, 600, N, N, 152.326488422, 7_8, 0x2, NTSC_LIKE),
+	MODE(30210, 640, 480, 800, 630, N, N, 142.171389101, 5_6, 0x1, NTSC_LIKE),
+	__MODE(29500, 720, 576, 944, 625, P, P, 145.592111636, 1_1, 0x7, PAL_LIKE, 800, 600),
+	MODE(36000, 800, 600, 960, 750, P, P, 119.304647022, 5_6, 0x6, PAL_LIKE),
+	MODE(39000, 800, 600, 936, 836, P, P, 110.127366499, 3_4, 0x1, PAL_LIKE),
+	MODE(39273, 800, 600, 1040, 630, P, P, 145.816809399, 5_6, 0x4, NTSC_LIKE),
+	MODE(43636, 800, 600, 1040, 700, P, P, 131.235128487, 3_4, 0x2, NTSC_LIKE),
+	MODE(47832, 800, 600, 1064, 750, P, P, 119.723275165, 7_10, 0x1, NTSC_LIKE),
+	{}
+};
+
+struct ch7006_mode *ch7006_lookup_mode(struct drm_encoder *encoder,
+				       struct drm_display_mode *drm_mode)
+{
+	struct ch7006_priv *priv = to_ch7006_priv(encoder);
+	struct ch7006_mode *mode;
+
+	for (mode = ch7006_modes; mode->mode.clock; mode++) {
+
+		if (~mode->valid_norms & 1<<priv->norm)
+			continue;
+
+		if (mode->mode.hdisplay != drm_mode->hdisplay ||
+		    mode->mode.vdisplay != drm_mode->vdisplay ||
+		    mode->mode.vtotal != drm_mode->vtotal ||
+		    mode->mode.htotal != drm_mode->htotal ||
+		    mode->mode.clock != drm_mode->clock)
+			continue;
+
+		return mode;
+	}
+
+	return NULL;
+}
+
+/* Some common HW state calculation code */
+
+void ch7006_setup_levels(struct drm_encoder *encoder)
+{
+	struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+	struct ch7006_priv *priv = to_ch7006_priv(encoder);
+	uint8_t *regs = priv->state.regs;
+	struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
+	int gain;
+	int black_level;
+
+	/* Set DAC_GAIN if the voltage drop between white and black is
+	 * high enough. */
+	if (norm->black_level < 339*fixed1/1000) {
+		gain = 76;
+
+		regs[CH7006_INPUT_FORMAT] |= CH7006_INPUT_FORMAT_DAC_GAIN;
+	} else {
+		gain = 71;
+
+		regs[CH7006_INPUT_FORMAT] &= ~CH7006_INPUT_FORMAT_DAC_GAIN;
+	}
+
+	black_level = round_fixed(norm->black_level*26625)/gain;
+
+	/* Correct it with the specified brightness. */
+	black_level = interpolate(90, black_level, 208, priv->brightness);
+
+	regs[CH7006_BLACK_LEVEL] = bitf(CH7006_BLACK_LEVEL_0, black_level);
+
+	ch7006_dbg(client, "black level: %d\n", black_level);
+}
+
+void ch7006_setup_subcarrier(struct drm_encoder *encoder)
+{
+	struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+	struct ch7006_priv *priv = to_ch7006_priv(encoder);
+	struct ch7006_state *state = &priv->state;
+	struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
+	struct ch7006_mode *mode = priv->mode;
+	uint32_t subc_inc;
+
+	subc_inc = round_fixed((mode->subc_coeff >> 8)
+			       * (norm->subc_freq >> 24));
+
+	setbitf(state, CH7006_SUBC_INC0, 28, subc_inc);
+	setbitf(state, CH7006_SUBC_INC1, 24, subc_inc);
+	setbitf(state, CH7006_SUBC_INC2, 20, subc_inc);
+	setbitf(state, CH7006_SUBC_INC3, 16, subc_inc);
+	setbitf(state, CH7006_SUBC_INC4, 12, subc_inc);
+	setbitf(state, CH7006_SUBC_INC5, 8, subc_inc);
+	setbitf(state, CH7006_SUBC_INC6, 4, subc_inc);
+	setbitf(state, CH7006_SUBC_INC7, 0, subc_inc);
+
+	ch7006_dbg(client, "subcarrier inc: %u\n", subc_inc);
+}
+
+void ch7006_setup_pll(struct drm_encoder *encoder)
+{
+	struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+	struct ch7006_priv *priv = to_ch7006_priv(encoder);
+	uint8_t *regs = priv->state.regs;
+	struct ch7006_mode *mode = priv->mode;
+	int n, best_n = 0;
+	int m, best_m = 0;
+	int freq, best_freq = 0;
+
+	for (n = 0; n < CH7006_MAXN; n++) {
+		for (m = 0; m < CH7006_MAXM; m++) {
+			freq = CH7006_FREQ0*(n+2)/(m+2);
+
+			if (abs(freq - mode->mode.clock) <
+			    abs(best_freq - mode->mode.clock)) {
+				best_freq = freq;
+				best_n = n;
+				best_m = m;
+			}
+		}
+	}
+
+	regs[CH7006_PLLOV] = bitf(CH7006_PLLOV_N_8, best_n) |
+		bitf(CH7006_PLLOV_M_8, best_m);
+
+	regs[CH7006_PLLM] = bitf(CH7006_PLLM_0, best_m);
+	regs[CH7006_PLLN] = bitf(CH7006_PLLN_0, best_n);
+
+	if (best_n < 108)
+		regs[CH7006_PLL_CONTROL] |= CH7006_PLL_CONTROL_CAPACITOR;
+	else
+		regs[CH7006_PLL_CONTROL] &= ~CH7006_PLL_CONTROL_CAPACITOR;
+
+	ch7006_dbg(client, "n=%d m=%d f=%d c=%d\n",
+		   best_n, best_m, best_freq, best_n < 108);
+}
+
+void ch7006_setup_power_state(struct drm_encoder *encoder)
+{
+	struct ch7006_priv *priv = to_ch7006_priv(encoder);
+	uint8_t *power = &priv->state.regs[CH7006_POWER];
+	int subconnector;
+
+	subconnector = priv->select_subconnector ? priv->select_subconnector :
+							priv->subconnector;
+
+	*power = CH7006_POWER_RESET;
+
+	if (priv->last_dpms == DRM_MODE_DPMS_ON) {
+		switch (subconnector) {
+		case DRM_MODE_SUBCONNECTOR_SVIDEO:
+			*power |= bitfs(CH7006_POWER_LEVEL, CVBS_OFF);
+			break;
+		case DRM_MODE_SUBCONNECTOR_Composite:
+			*power |= bitfs(CH7006_POWER_LEVEL, SVIDEO_OFF);
+			break;
+		case DRM_MODE_SUBCONNECTOR_SCART:
+			*power |= bitfs(CH7006_POWER_LEVEL, NORMAL) |
+				CH7006_POWER_SCART;
+			break;
+		}
+
+	} else {
+		*power |= bitfs(CH7006_POWER_LEVEL, FULL_POWER_OFF);
+	}
+}
+
+void ch7006_setup_properties(struct drm_encoder *encoder)
+{
+	struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+	struct ch7006_priv *priv = to_ch7006_priv(encoder);
+	struct ch7006_state *state = &priv->state;
+	struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
+	struct ch7006_mode *ch_mode = priv->mode;
+	struct drm_display_mode *mode = &ch_mode->mode;
+	uint8_t *regs = state->regs;
+	int flicker, contrast, hpos, vpos;
+	uint64_t scale, aspect;
+
+	flicker = interpolate(0, 2, 3, priv->flicker);
+	regs[CH7006_FFILTER] = bitf(CH7006_FFILTER_TEXT, flicker) |
+		bitf(CH7006_FFILTER_LUMA, flicker) |
+		bitf(CH7006_FFILTER_CHROMA, 1);
+
+	contrast = interpolate(0, 5, 7, priv->contrast);
+	regs[CH7006_CONTRAST] = bitf(CH7006_CONTRAST_0, contrast);
+
+	scale = norm->vtotal*fixed1;
+	do_div(scale, mode->vtotal);
+
+	aspect = ch_mode->enc_hdisp*fixed1;
+	do_div(aspect, ch_mode->enc_vdisp);
+
+	hpos = round_fixed((norm->hvirtual * aspect - mode->hdisplay * scale)
+			   * priv->hmargin * mode->vtotal) / norm->vtotal / 100 / 4;
+
+	setbitf(state, CH7006_POV, HPOS_8, hpos);
+	setbitf(state, CH7006_HPOS, 0, hpos);
+
+	vpos = max(0, norm->vdisplay - round_fixed(mode->vdisplay*scale)
+		   + norm->voffset) * priv->vmargin / 100 / 2;
+
+	setbitf(state, CH7006_POV, VPOS_8, vpos);
+	setbitf(state, CH7006_VPOS, 0, vpos);
+
+	ch7006_dbg(client, "hpos: %d, vpos: %d\n", hpos, vpos);
+}
+
+/* HW access functions */
+
+void ch7006_write(struct i2c_client *client, uint8_t addr, uint8_t val)
+{
+	uint8_t buf[] = {addr, val};
+	int ret;
+
+	ret = i2c_master_send(client, buf, ARRAY_SIZE(buf));
+	if (ret < 0)
+		ch7006_err(client, "Error %d writing to subaddress 0x%x\n",
+			   ret, addr);
+}
+
+uint8_t ch7006_read(struct i2c_client *client, uint8_t addr)
+{
+	uint8_t val;
+	int ret;
+
+	ret = i2c_master_send(client, &addr, sizeof(addr));
+	if (ret < 0)
+		goto fail;
+
+	ret = i2c_master_recv(client, &val, sizeof(val));
+	if (ret < 0)
+		goto fail;
+
+	return val;
+
+fail:
+	ch7006_err(client, "Error %d reading from subaddress 0x%x\n",
+		   ret, addr);
+	return 0;
+}
+
+void ch7006_state_load(struct i2c_client *client,
+		       struct ch7006_state *state)
+{
+	ch7006_load_reg(client, state, CH7006_POWER);
+
+	ch7006_load_reg(client, state, CH7006_DISPMODE);
+	ch7006_load_reg(client, state, CH7006_FFILTER);
+	ch7006_load_reg(client, state, CH7006_BWIDTH);
+	ch7006_load_reg(client, state, CH7006_INPUT_FORMAT);
+	ch7006_load_reg(client, state, CH7006_CLKMODE);
+	ch7006_load_reg(client, state, CH7006_START_ACTIVE);
+	ch7006_load_reg(client, state, CH7006_POV);
+	ch7006_load_reg(client, state, CH7006_BLACK_LEVEL);
+	ch7006_load_reg(client, state, CH7006_HPOS);
+	ch7006_load_reg(client, state, CH7006_VPOS);
+	ch7006_load_reg(client, state, CH7006_INPUT_SYNC);
+	ch7006_load_reg(client, state, CH7006_DETECT);
+	ch7006_load_reg(client, state, CH7006_CONTRAST);
+	ch7006_load_reg(client, state, CH7006_PLLOV);
+	ch7006_load_reg(client, state, CH7006_PLLM);
+	ch7006_load_reg(client, state, CH7006_PLLN);
+	ch7006_load_reg(client, state, CH7006_BCLKOUT);
+	ch7006_load_reg(client, state, CH7006_SUBC_INC0);
+	ch7006_load_reg(client, state, CH7006_SUBC_INC1);
+	ch7006_load_reg(client, state, CH7006_SUBC_INC2);
+	ch7006_load_reg(client, state, CH7006_SUBC_INC3);
+	ch7006_load_reg(client, state, CH7006_SUBC_INC4);
+	ch7006_load_reg(client, state, CH7006_SUBC_INC5);
+	ch7006_load_reg(client, state, CH7006_SUBC_INC6);
+	ch7006_load_reg(client, state, CH7006_SUBC_INC7);
+	ch7006_load_reg(client, state, CH7006_PLL_CONTROL);
+	ch7006_load_reg(client, state, CH7006_CALC_SUBC_INC0);
+
+	/* I don't know what this is for, but otherwise I get no
+	 * signal.
+	 */
+	ch7006_write(client, 0x3d, 0x0);
+}
+
+void ch7006_state_save(struct i2c_client *client,
+		       struct ch7006_state *state)
+{
+	ch7006_save_reg(client, state, CH7006_POWER);
+
+	ch7006_save_reg(client, state, CH7006_DISPMODE);
+	ch7006_save_reg(client, state, CH7006_FFILTER);
+	ch7006_save_reg(client, state, CH7006_BWIDTH);
+	ch7006_save_reg(client, state, CH7006_INPUT_FORMAT);
+	ch7006_save_reg(client, state, CH7006_CLKMODE);
+	ch7006_save_reg(client, state, CH7006_START_ACTIVE);
+	ch7006_save_reg(client, state, CH7006_POV);
+	ch7006_save_reg(client, state, CH7006_BLACK_LEVEL);
+	ch7006_save_reg(client, state, CH7006_HPOS);
+	ch7006_save_reg(client, state, CH7006_VPOS);
+	ch7006_save_reg(client, state, CH7006_INPUT_SYNC);
+	ch7006_save_reg(client, state, CH7006_DETECT);
+	ch7006_save_reg(client, state, CH7006_CONTRAST);
+	ch7006_save_reg(client, state, CH7006_PLLOV);
+	ch7006_save_reg(client, state, CH7006_PLLM);
+	ch7006_save_reg(client, state, CH7006_PLLN);
+	ch7006_save_reg(client, state, CH7006_BCLKOUT);
+	ch7006_save_reg(client, state, CH7006_SUBC_INC0);
+	ch7006_save_reg(client, state, CH7006_SUBC_INC1);
+	ch7006_save_reg(client, state, CH7006_SUBC_INC2);
+	ch7006_save_reg(client, state, CH7006_SUBC_INC3);
+	ch7006_save_reg(client, state, CH7006_SUBC_INC4);
+	ch7006_save_reg(client, state, CH7006_SUBC_INC5);
+	ch7006_save_reg(client, state, CH7006_SUBC_INC6);
+	ch7006_save_reg(client, state, CH7006_SUBC_INC7);
+	ch7006_save_reg(client, state, CH7006_PLL_CONTROL);
+	ch7006_save_reg(client, state, CH7006_CALC_SUBC_INC0);
+
+	state->regs[CH7006_FFILTER] = (state->regs[CH7006_FFILTER] & 0xf0) |
+		(state->regs[CH7006_FFILTER] & 0x0c) >> 2 |
+		(state->regs[CH7006_FFILTER] & 0x03) << 2;
+}
diff --git a/drivers/gpu/drm/i2c/ch7006_priv.h b/drivers/gpu/drm/i2c/ch7006_priv.h
new file mode 100644
index 0000000..b06d3d9
--- /dev/null
+++ b/drivers/gpu/drm/i2c/ch7006_priv.h
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2009 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef __DRM_I2C_CH7006_PRIV_H__
+#define __DRM_I2C_CH7006_PRIV_H__
+
+#include "drmP.h"
+#include "drm_crtc_helper.h"
+#include "drm_encoder_slave.h"
+#include "i2c/ch7006.h"
+
+typedef int64_t fixed;
+#define fixed1 (1LL << 32)
+
+enum ch7006_tv_norm {
+	TV_NORM_PAL,
+	TV_NORM_PAL_M,
+	TV_NORM_PAL_N,
+	TV_NORM_PAL_NC,
+	TV_NORM_PAL_60,
+	TV_NORM_NTSC_M,
+	TV_NORM_NTSC_J,
+	NUM_TV_NORMS
+};
+
+struct ch7006_tv_norm_info {
+	fixed vrefresh;
+	int vdisplay;
+	int vtotal;
+	int hvirtual;
+
+	fixed subc_freq;
+	fixed black_level;
+
+	uint32_t dispmode;
+	int voffset;
+};
+
+struct ch7006_mode {
+	struct drm_display_mode mode;
+
+	int enc_hdisp;
+	int enc_vdisp;
+
+	fixed subc_coeff;
+	uint32_t dispmode;
+
+	uint32_t valid_scales;
+	uint32_t valid_norms;
+};
+
+struct ch7006_state {
+	uint8_t regs[0x26];
+};
+
+struct ch7006_priv {
+	struct ch7006_encoder_params *params;
+	struct ch7006_mode *mode;
+
+	struct ch7006_state state;
+	struct ch7006_state saved_state;
+
+	struct drm_property *scale_property;
+
+	int select_subconnector;
+	int subconnector;
+	int hmargin;
+	int vmargin;
+	enum ch7006_tv_norm norm;
+	int brightness;
+	int contrast;
+	int flicker;
+	int scale;
+
+	int last_dpms;
+};
+
+#define to_ch7006_priv(x) \
+	((struct ch7006_priv *)to_encoder_slave(x)->slave_priv)
+
+extern int ch7006_debug;
+extern char *ch7006_tv_norm;
+extern int ch7006_scale;
+
+extern char *ch7006_tv_norm_names[];
+extern struct ch7006_tv_norm_info ch7006_tv_norms[];
+extern struct ch7006_mode ch7006_modes[];
+
+struct ch7006_mode *ch7006_lookup_mode(struct drm_encoder *encoder,
+				       struct drm_display_mode *drm_mode);
+
+void ch7006_setup_levels(struct drm_encoder *encoder);
+void ch7006_setup_subcarrier(struct drm_encoder *encoder);
+void ch7006_setup_pll(struct drm_encoder *encoder);
+void ch7006_setup_power_state(struct drm_encoder *encoder);
+void ch7006_setup_properties(struct drm_encoder *encoder);
+
+void ch7006_write(struct i2c_client *client, uint8_t addr, uint8_t val);
+uint8_t ch7006_read(struct i2c_client *client, uint8_t addr);
+
+void ch7006_state_load(struct i2c_client *client,
+		       struct ch7006_state *state);
+void ch7006_state_save(struct i2c_client *client,
+		       struct ch7006_state *state);
+
+/* Some helper macros */
+
+#define ch7006_dbg(client, format, ...) do {				\
+		if (ch7006_debug)					\
+			dev_printk(KERN_DEBUG, &client->dev,		\
+				   "%s: " format, __func__, ## __VA_ARGS__); \
+	} while (0)
+#define ch7006_info(client, format, ...) \
+				dev_info(&client->dev, format, __VA_ARGS__)
+#define ch7006_err(client, format, ...) \
+				dev_err(&client->dev, format, __VA_ARGS__)
+
+#define __mask(src, bitfield) \
+		(((2 << (1 ? bitfield)) - 1) & ~((1 << (0 ? bitfield)) - 1))
+#define mask(bitfield) __mask(bitfield)
+
+#define __bitf(src, bitfield, x) \
+		(((x) >> (src) << (0 ? bitfield)) &  __mask(src, bitfield))
+#define bitf(bitfield, x) __bitf(bitfield, x)
+#define bitfs(bitfield, s) __bitf(bitfield, bitfield##_##s)
+#define setbitf(state, reg, bitfield, x)				\
+	state->regs[reg] = (state->regs[reg] & ~mask(reg##_##bitfield))	\
+		| bitf(reg##_##bitfield, x)
+
+#define __unbitf(src, bitfield, x) \
+		((x & __mask(src, bitfield)) >> (0 ? bitfield) << (src))
+#define unbitf(bitfield, x) __unbitf(bitfield, x)
+
+static inline int interpolate(int y0, int y1, int y2, int x)
+{
+	return y1 + (x < 50 ? y1 - y0 : y2 - y1) * (x - 50) / 50;
+}
+
+static inline int32_t round_fixed(fixed x)
+{
+	return (x + fixed1/2) >> 32;
+}
+
+#define ch7006_load_reg(client, state, reg) ch7006_write(client, reg, state->regs[reg])
+#define ch7006_save_reg(client, state, reg) state->regs[reg] = ch7006_read(client, reg)
+
+/* Fixed hardware specs */
+
+#define CH7006_FREQ0				14318
+#define CH7006_MAXN				650
+#define CH7006_MAXM				315
+
+/* Register definitions */
+
+#define CH7006_DISPMODE				0x00
+#define CH7006_DISPMODE_INPUT_RES		0, 7:5
+#define CH7006_DISPMODE_INPUT_RES_512x384	0x0
+#define CH7006_DISPMODE_INPUT_RES_720x400	0x1
+#define CH7006_DISPMODE_INPUT_RES_640x400	0x2
+#define CH7006_DISPMODE_INPUT_RES_640x480	0x3
+#define CH7006_DISPMODE_INPUT_RES_800x600	0x4
+#define CH7006_DISPMODE_INPUT_RES_NATIVE	0x5
+#define CH7006_DISPMODE_OUTPUT_STD		0, 4:3
+#define CH7006_DISPMODE_OUTPUT_STD_PAL		0x0
+#define CH7006_DISPMODE_OUTPUT_STD_NTSC		0x1
+#define CH7006_DISPMODE_OUTPUT_STD_PAL_M	0x2
+#define CH7006_DISPMODE_OUTPUT_STD_NTSC_J	0x3
+#define CH7006_DISPMODE_SCALING_RATIO		0, 2:0
+#define CH7006_DISPMODE_SCALING_RATIO_5_4	0x0
+#define CH7006_DISPMODE_SCALING_RATIO_1_1	0x1
+#define CH7006_DISPMODE_SCALING_RATIO_7_8	0x2
+#define CH7006_DISPMODE_SCALING_RATIO_5_6	0x3
+#define CH7006_DISPMODE_SCALING_RATIO_3_4	0x4
+#define CH7006_DISPMODE_SCALING_RATIO_7_10	0x5
+
+#define CH7006_FFILTER				0x01
+#define CH7006_FFILTER_TEXT			0, 5:4
+#define CH7006_FFILTER_LUMA			0, 3:2
+#define CH7006_FFILTER_CHROMA			0, 1:0
+#define CH7006_FFILTER_CHROMA_NO_DCRAWL		0x3
+
+#define CH7006_BWIDTH				0x03
+#define CH7006_BWIDTH_5L_FFILER			(1 << 7)
+#define CH7006_BWIDTH_CVBS_NO_CHROMA		(1 << 6)
+#define CH7006_BWIDTH_CHROMA			0, 5:4
+#define CH7006_BWIDTH_SVIDEO_YPEAK		(1 << 3)
+#define CH7006_BWIDTH_SVIDEO_LUMA		0, 2:1
+#define CH7006_BWIDTH_CVBS_LUMA			0, 0:0
+
+#define CH7006_INPUT_FORMAT			0x04
+#define CH7006_INPUT_FORMAT_DAC_GAIN		(1 << 6)
+#define CH7006_INPUT_FORMAT_RGB_PASS_THROUGH	(1 << 5)
+#define CH7006_INPUT_FORMAT_FORMAT		0, 3:0
+#define CH7006_INPUT_FORMAT_FORMAT_RGB16	0x0
+#define CH7006_INPUT_FORMAT_FORMAT_YCrCb24m16	0x1
+#define CH7006_INPUT_FORMAT_FORMAT_RGB24m16	0x2
+#define CH7006_INPUT_FORMAT_FORMAT_RGB15	0x3
+#define CH7006_INPUT_FORMAT_FORMAT_RGB24m12C	0x4
+#define CH7006_INPUT_FORMAT_FORMAT_RGB24m12I	0x5
+#define CH7006_INPUT_FORMAT_FORMAT_RGB24m8	0x6
+#define CH7006_INPUT_FORMAT_FORMAT_RGB16m8	0x7
+#define CH7006_INPUT_FORMAT_FORMAT_RGB15m8	0x8
+#define CH7006_INPUT_FORMAT_FORMAT_YCrCb24m8	0x9
+
+#define CH7006_CLKMODE				0x06
+#define CH7006_CLKMODE_SUBC_LOCK		(1 << 7)
+#define CH7006_CLKMODE_MASTER			(1 << 6)
+#define CH7006_CLKMODE_POS_EDGE			(1 << 4)
+#define CH7006_CLKMODE_XCM			0, 3:2
+#define CH7006_CLKMODE_PCM			0, 1:0
+
+#define CH7006_START_ACTIVE			0x07
+#define CH7006_START_ACTIVE_0			0, 7:0
+
+#define CH7006_POV				0x08
+#define CH7006_POV_START_ACTIVE_8		8, 2:2
+#define CH7006_POV_HPOS_8			8, 1:1
+#define CH7006_POV_VPOS_8			8, 0:0
+
+#define CH7006_BLACK_LEVEL			0x09
+#define CH7006_BLACK_LEVEL_0			0, 7:0
+
+#define CH7006_HPOS				0x0a
+#define CH7006_HPOS_0				0, 7:0
+
+#define CH7006_VPOS				0x0b
+#define CH7006_VPOS_0				0, 7:0
+
+#define CH7006_INPUT_SYNC			0x0d
+#define CH7006_INPUT_SYNC_EMBEDDED		(1 << 3)
+#define CH7006_INPUT_SYNC_OUTPUT		(1 << 2)
+#define CH7006_INPUT_SYNC_PVSYNC		(1 << 1)
+#define CH7006_INPUT_SYNC_PHSYNC		(1 << 0)
+
+#define CH7006_POWER				0x0e
+#define CH7006_POWER_SCART			(1 << 4)
+#define CH7006_POWER_RESET			(1 << 3)
+#define CH7006_POWER_LEVEL			0, 2:0
+#define CH7006_POWER_LEVEL_CVBS_OFF		0x0
+#define CH7006_POWER_LEVEL_POWER_OFF		0x1
+#define CH7006_POWER_LEVEL_SVIDEO_OFF		0x2
+#define CH7006_POWER_LEVEL_NORMAL		0x3
+#define CH7006_POWER_LEVEL_FULL_POWER_OFF	0x4
+
+#define CH7006_DETECT				0x10
+#define CH7006_DETECT_SVIDEO_Y_TEST		(1 << 3)
+#define CH7006_DETECT_SVIDEO_C_TEST		(1 << 2)
+#define CH7006_DETECT_CVBS_TEST			(1 << 1)
+#define CH7006_DETECT_SENSE			(1 << 0)
+
+#define CH7006_CONTRAST				0x11
+#define CH7006_CONTRAST_0			0, 2:0
+
+#define CH7006_PLLOV	 			0x13
+#define CH7006_PLLOV_N_8	 		8, 2:1
+#define CH7006_PLLOV_M_8	 		8, 0:0
+
+#define CH7006_PLLM	 			0x14
+#define CH7006_PLLM_0	 			0, 7:0
+
+#define CH7006_PLLN	 			0x15
+#define CH7006_PLLN_0	 			0, 7:0
+
+#define CH7006_BCLKOUT	 			0x17
+
+#define CH7006_SUBC_INC0			0x18
+#define CH7006_SUBC_INC0_28			28, 3:0
+
+#define CH7006_SUBC_INC1			0x19
+#define CH7006_SUBC_INC1_24			24, 3:0
+
+#define CH7006_SUBC_INC2			0x1a
+#define CH7006_SUBC_INC2_20			20, 3:0
+
+#define CH7006_SUBC_INC3			0x1b
+#define CH7006_SUBC_INC3_GPIO1_VAL		(1 << 7)
+#define CH7006_SUBC_INC3_GPIO0_VAL		(1 << 6)
+#define CH7006_SUBC_INC3_POUT_3_3V		(1 << 5)
+#define CH7006_SUBC_INC3_POUT_INV		(1 << 4)
+#define CH7006_SUBC_INC3_16			16, 3:0
+
+#define CH7006_SUBC_INC4			0x1c
+#define CH7006_SUBC_INC4_GPIO1_IN		(1 << 7)
+#define CH7006_SUBC_INC4_GPIO0_IN		(1 << 6)
+#define CH7006_SUBC_INC4_DS_INPUT		(1 << 4)
+#define CH7006_SUBC_INC4_12			12, 3:0
+
+#define CH7006_SUBC_INC5			0x1d
+#define CH7006_SUBC_INC5_8			8, 3:0
+
+#define CH7006_SUBC_INC6			0x1e
+#define CH7006_SUBC_INC6_4			4, 3:0
+
+#define CH7006_SUBC_INC7			0x1f
+#define CH7006_SUBC_INC7_0			0, 3:0
+
+#define CH7006_PLL_CONTROL			0x20
+#define CH7006_PLL_CONTROL_CPI			(1 << 5)
+#define CH7006_PLL_CONTROL_CAPACITOR		(1 << 4)
+#define CH7006_PLL_CONTROL_7STAGES		(1 << 3)
+#define CH7006_PLL_CONTROL_DIGITAL_5V		(1 << 2)
+#define CH7006_PLL_CONTROL_ANALOG_5V		(1 << 1)
+#define CH7006_PLL_CONTROL_MEMORY_5V		(1 << 0)
+
+#define CH7006_CALC_SUBC_INC0			0x21
+#define CH7006_CALC_SUBC_INC0_24		24, 4:3
+#define CH7006_CALC_SUBC_INC0_HYST		0, 2:1
+#define CH7006_CALC_SUBC_INC0_AUTO		(1 << 0)
+
+#define CH7006_CALC_SUBC_INC1			0x22
+#define CH7006_CALC_SUBC_INC1_16		16, 7:0
+
+#define CH7006_CALC_SUBC_INC2			0x23
+#define CH7006_CALC_SUBC_INC2_8			8, 7:0
+
+#define CH7006_CALC_SUBC_INC3			0x24
+#define CH7006_CALC_SUBC_INC3_0			0, 7:0
+
+#define CH7006_VERSION_ID			0x25
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig
new file mode 100644
index 0000000..d823e63
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/Kconfig
@@ -0,0 +1,44 @@
+config DRM_NOUVEAU
+	tristate "Nouveau (nVidia) cards"
+	depends on DRM
+        select FW_LOADER
+	select DRM_KMS_HELPER
+	select DRM_TTM
+	select FB_CFB_FILLRECT
+	select FB_CFB_COPYAREA
+	select FB_CFB_IMAGEBLIT
+	select FB
+	select FRAMEBUFFER_CONSOLE if !EMBEDDED
+	select FB_BACKLIGHT if DRM_NOUVEAU_BACKLIGHT
+	help
+	  Choose this option for open-source nVidia support.
+
+config DRM_NOUVEAU_BACKLIGHT
+	bool "Support for backlight control"
+	depends on DRM_NOUVEAU
+	default y
+	help
+	  Say Y here if you want to control the backlight of your display
+	  (e.g. a laptop panel).
+
+config DRM_NOUVEAU_DEBUG
+	bool "Build in Nouveau's debugfs support"
+	depends on DRM_NOUVEAU && DEBUG_FS
+	default y
+	help
+	  Say Y here if you want Nouveau to output debugging information
+	  via debugfs.
+
+menu "I2C encoder or helper chips"
+     depends on DRM
+
+config DRM_I2C_CH7006
+	tristate "Chrontel ch7006 TV encoder"
+	default m if DRM_NOUVEAU
+	help
+	  Support for Chrontel ch7006 and similar TV encoders, found
+	  on some nVidia video cards.
+
+	  This driver is currently only useful if you're also using
+	  the nouveau driver.
+endmenu
diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile
new file mode 100644
index 0000000..1d90d4d
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/Makefile
@@ -0,0 +1,31 @@
+#
+# Makefile for the drm device driver.  This driver provides support for the
+# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
+
+ccflags-y := -Iinclude/drm
+nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \
+             nouveau_object.o nouveau_irq.o nouveau_notifier.o \
+             nouveau_sgdma.o nouveau_dma.o \
+             nouveau_bo.o nouveau_fence.o nouveau_gem.o nouveau_ttm.o \
+             nouveau_hw.o nouveau_calc.o nouveau_bios.o nouveau_i2c.o \
+	     nouveau_display.o nouveau_connector.o nouveau_fbcon.o \
+	     nouveau_dp.o \
+             nv04_timer.o \
+             nv04_mc.o nv40_mc.o nv50_mc.o \
+             nv04_fb.o nv10_fb.o nv40_fb.o \
+             nv04_fifo.o nv10_fifo.o nv40_fifo.o nv50_fifo.o \
+             nv04_graph.o nv10_graph.o nv20_graph.o \
+             nv40_graph.o nv50_graph.o \
+             nv04_instmem.o nv50_instmem.o \
+             nv50_crtc.o nv50_dac.o nv50_sor.o \
+             nv50_cursor.o nv50_display.o nv50_fbcon.o \
+             nv04_dac.o nv04_dfp.o nv04_tv.o nv17_tv.o nv17_tv_modes.o \
+             nv04_crtc.o nv04_display.o nv04_cursor.o nv04_fbcon.o \
+             nv17_gpio.o
+
+nouveau-$(CONFIG_DRM_NOUVEAU_DEBUG) += nouveau_debugfs.o
+nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o
+nouveau-$(CONFIG_DRM_NOUVEAU_BACKLIGHT) += nouveau_backlight.o
+nouveau-$(CONFIG_ACPI) += nouveau_acpi.o
+
+obj-$(CONFIG_DRM_NOUVEAU)+= nouveau.o
diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c
new file mode 100644
index 0000000..1cf4882
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c
@@ -0,0 +1,125 @@
+#include <linux/pci.h>
+#include <linux/acpi.h>
+#include <acpi/acpi_drivers.h>
+#include <acpi/acpi_bus.h>
+
+#include "drmP.h"
+#include "drm.h"
+#include "drm_sarea.h"
+#include "drm_crtc_helper.h"
+#include "nouveau_drv.h"
+#include "nouveau_drm.h"
+#include "nv50_display.h"
+
+#define NOUVEAU_DSM_SUPPORTED 0x00
+#define NOUVEAU_DSM_SUPPORTED_FUNCTIONS 0x00
+
+#define NOUVEAU_DSM_ACTIVE 0x01
+#define NOUVEAU_DSM_ACTIVE_QUERY 0x00
+
+#define NOUVEAU_DSM_LED 0x02
+#define NOUVEAU_DSM_LED_STATE 0x00
+#define NOUVEAU_DSM_LED_OFF 0x10
+#define NOUVEAU_DSM_LED_STAMINA 0x11
+#define NOUVEAU_DSM_LED_SPEED 0x12
+
+#define NOUVEAU_DSM_POWER 0x03
+#define NOUVEAU_DSM_POWER_STATE 0x00
+#define NOUVEAU_DSM_POWER_SPEED 0x01
+#define NOUVEAU_DSM_POWER_STAMINA 0x02
+
+static int nouveau_dsm(struct drm_device *dev, int func, int arg, int *result)
+{
+	static char muid[] = {
+		0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D,
+		0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4,
+	};
+
+	struct pci_dev *pdev = dev->pdev;
+	struct acpi_handle *handle;
+	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+	struct acpi_object_list input;
+	union acpi_object params[4];
+	union acpi_object *obj;
+	int err;
+
+	handle = DEVICE_ACPI_HANDLE(&pdev->dev);
+
+	if (!handle)
+		return -ENODEV;
+
+	input.count = 4;
+	input.pointer = params;
+	params[0].type = ACPI_TYPE_BUFFER;
+	params[0].buffer.length = sizeof(muid);
+	params[0].buffer.pointer = (char *)muid;
+	params[1].type = ACPI_TYPE_INTEGER;
+	params[1].integer.value = 0x00000102;
+	params[2].type = ACPI_TYPE_INTEGER;
+	params[2].integer.value = func;
+	params[3].type = ACPI_TYPE_INTEGER;
+	params[3].integer.value = arg;
+
+	err = acpi_evaluate_object(handle, "_DSM", &input, &output);
+	if (err) {
+		NV_INFO(dev, "failed to evaluate _DSM: %d\n", err);
+		return err;
+	}
+
+	obj = (union acpi_object *)output.pointer;
+
+	if (obj->type == ACPI_TYPE_INTEGER)
+		if (obj->integer.value == 0x80000002)
+			return -ENODEV;
+
+	if (obj->type == ACPI_TYPE_BUFFER) {
+		if (obj->buffer.length == 4 && result) {
+			*result = 0;
+			*result |= obj->buffer.pointer[0];
+			*result |= (obj->buffer.pointer[1] << 8);
+			*result |= (obj->buffer.pointer[2] << 16);
+			*result |= (obj->buffer.pointer[3] << 24);
+		}
+	}
+
+	kfree(output.pointer);
+	return 0;
+}
+
+int nouveau_hybrid_setup(struct drm_device *dev)
+{
+	int result;
+
+	if (nouveau_dsm(dev, NOUVEAU_DSM_ACTIVE, NOUVEAU_DSM_ACTIVE_QUERY,
+								&result))
+		return -ENODEV;
+
+	NV_INFO(dev, "_DSM hardware status gave 0x%x\n", result);
+
+	if (result & 0x1) {	/* Stamina mode - disable the external GPU */
+		nouveau_dsm(dev, NOUVEAU_DSM_LED, NOUVEAU_DSM_LED_STAMINA,
+									NULL);
+		nouveau_dsm(dev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_STAMINA,
+									NULL);
+	} else {		/* Ensure that the external GPU is enabled */
+		nouveau_dsm(dev, NOUVEAU_DSM_LED, NOUVEAU_DSM_LED_SPEED, NULL);
+		nouveau_dsm(dev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_SPEED,
+									NULL);
+	}
+
+	return 0;
+}
+
+bool nouveau_dsm_probe(struct drm_device *dev)
+{
+	int support = 0;
+
+	if (nouveau_dsm(dev, NOUVEAU_DSM_SUPPORTED,
+				NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &support))
+		return false;
+
+	if (!support)
+		return false;
+
+	return true;
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_backlight.c b/drivers/gpu/drm/nouveau/nouveau_backlight.c
new file mode 100644
index 0000000..20564f8
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_backlight.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2009 Red Hat <mjg@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/*
+ * Authors:
+ *  Matthew Garrett <mjg@redhat.com>
+ *
+ * Register locations derived from NVClock by Roderick Colenbrander
+ */
+
+#include <linux/backlight.h>
+
+#include "drmP.h"
+#include "nouveau_drv.h"
+#include "nouveau_drm.h"
+#include "nouveau_reg.h"
+
+static int nv40_get_intensity(struct backlight_device *bd)
+{
+	struct drm_device *dev = bl_get_data(bd);
+	int val = (nv_rd32(dev, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK)
+									>> 16;
+
+	return val;
+}
+
+static int nv40_set_intensity(struct backlight_device *bd)
+{
+	struct drm_device *dev = bl_get_data(bd);
+	int val = bd->props.brightness;
+	int reg = nv_rd32(dev, NV40_PMC_BACKLIGHT);
+
+	nv_wr32(dev, NV40_PMC_BACKLIGHT,
+		 (val << 16) | (reg & ~NV40_PMC_BACKLIGHT_MASK));
+
+	return 0;
+}
+
+static struct backlight_ops nv40_bl_ops = {
+	.options = BL_CORE_SUSPENDRESUME,
+	.get_brightness = nv40_get_intensity,
+	.update_status = nv40_set_intensity,
+};
+
+static int nv50_get_intensity(struct backlight_device *bd)
+{
+	struct drm_device *dev = bl_get_data(bd);
+
+	return nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT);
+}
+
+static int nv50_set_intensity(struct backlight_device *bd)
+{
+	struct drm_device *dev = bl_get_data(bd);
+	int val = bd->props.brightness;
+
+	nv_wr32(dev, NV50_PDISPLAY_SOR_BACKLIGHT,
+		val | NV50_PDISPLAY_SOR_BACKLIGHT_ENABLE);
+	return 0;
+}
+
+static struct backlight_ops nv50_bl_ops = {
+	.options = BL_CORE_SUSPENDRESUME,
+	.get_brightness = nv50_get_intensity,
+	.update_status = nv50_set_intensity,
+};
+
+static int nouveau_nv40_backlight_init(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct backlight_device *bd;
+
+	if (!(nv_rd32(dev, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK))
+		return 0;
+
+	bd = backlight_device_register("nv_backlight", &dev->pdev->dev, dev,
+				       &nv40_bl_ops);
+	if (IS_ERR(bd))
+		return PTR_ERR(bd);
+
+	dev_priv->backlight = bd;
+	bd->props.max_brightness = 31;
+	bd->props.brightness = nv40_get_intensity(bd);
+	backlight_update_status(bd);
+
+	return 0;
+}
+
+static int nouveau_nv50_backlight_init(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct backlight_device *bd;
+
+	if (!nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT))
+		return 0;
+
+	bd = backlight_device_register("nv_backlight", &dev->pdev->dev, dev,
+				       &nv50_bl_ops);
+	if (IS_ERR(bd))
+		return PTR_ERR(bd);
+
+	dev_priv->backlight = bd;
+	bd->props.max_brightness = 1025;
+	bd->props.brightness = nv50_get_intensity(bd);
+	backlight_update_status(bd);
+	return 0;
+}
+
+int nouveau_backlight_init(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	switch (dev_priv->card_type) {
+	case NV_40:
+		return nouveau_nv40_backlight_init(dev);
+	case NV_50:
+		return nouveau_nv50_backlight_init(dev);
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+void nouveau_backlight_exit(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	if (dev_priv->backlight) {
+		backlight_device_unregister(dev_priv->backlight);
+		dev_priv->backlight = NULL;
+	}
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c
new file mode 100644
index 0000000..5eec5ed
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
@@ -0,0 +1,6095 @@
+/*
+ * Copyright 2005-2006 Erik Waling
+ * Copyright 2006 Stephane Marchesin
+ * Copyright 2007-2009 Stuart Bennett
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "drmP.h"
+#define NV_DEBUG_NOTRACE
+#include "nouveau_drv.h"
+#include "nouveau_hw.h"
+
+/* these defines are made up */
+#define NV_CIO_CRE_44_HEADA 0x0
+#define NV_CIO_CRE_44_HEADB 0x3
+#define FEATURE_MOBILE 0x10	/* also FEATURE_QUADRO for BMP */
+#define LEGACY_I2C_CRT 0x80
+#define LEGACY_I2C_PANEL 0x81
+#define LEGACY_I2C_TV 0x82
+
+#define EDID1_LEN 128
+
+#define BIOSLOG(sip, fmt, arg...) NV_DEBUG(sip->dev, fmt, ##arg)
+#define LOG_OLD_VALUE(x)
+
+#define ROM16(x) le16_to_cpu(*(uint16_t *)&(x))
+#define ROM32(x) le32_to_cpu(*(uint32_t *)&(x))
+
+struct init_exec {
+	bool execute;
+	bool repeat;
+};
+
+static bool nv_cksum(const uint8_t *data, unsigned int length)
+{
+	/*
+	 * There's a few checksums in the BIOS, so here's a generic checking
+	 * function.
+	 */
+	int i;
+	uint8_t sum = 0;
+
+	for (i = 0; i < length; i++)
+		sum += data[i];
+
+	if (sum)
+		return true;
+
+	return false;
+}
+
+static int
+score_vbios(struct drm_device *dev, const uint8_t *data, const bool writeable)
+{
+	if (!(data[0] == 0x55 && data[1] == 0xAA)) {
+		NV_TRACEWARN(dev, "... BIOS signature not found\n");
+		return 0;
+	}
+
+	if (nv_cksum(data, data[2] * 512)) {
+		NV_TRACEWARN(dev, "... BIOS checksum invalid\n");
+		/* if a ro image is somewhat bad, it's probably all rubbish */
+		return writeable ? 2 : 1;
+	} else
+		NV_TRACE(dev, "... appears to be valid\n");
+
+	return 3;
+}
+
+static void load_vbios_prom(struct drm_device *dev, uint8_t *data)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	uint32_t pci_nv_20, save_pci_nv_20;
+	int pcir_ptr;
+	int i;
+
+	if (dev_priv->card_type >= NV_50)
+		pci_nv_20 = 0x88050;
+	else
+		pci_nv_20 = NV_PBUS_PCI_NV_20;
+
+	/* enable ROM access */
+	save_pci_nv_20 = nvReadMC(dev, pci_nv_20);
+	nvWriteMC(dev, pci_nv_20,
+		  save_pci_nv_20 & ~NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED);
+
+	/* bail if no rom signature */
+	if (nv_rd08(dev, NV_PROM_OFFSET) != 0x55 ||
+	    nv_rd08(dev, NV_PROM_OFFSET + 1) != 0xaa)
+		goto out;
+
+	/* additional check (see note below) - read PCI record header */
+	pcir_ptr = nv_rd08(dev, NV_PROM_OFFSET + 0x18) |
+		   nv_rd08(dev, NV_PROM_OFFSET + 0x19) << 8;
+	if (nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr) != 'P' ||
+	    nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr + 1) != 'C' ||
+	    nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr + 2) != 'I' ||
+	    nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr + 3) != 'R')
+		goto out;
+
+	/* on some 6600GT/6800LE prom reads are messed up.  nvclock alleges a
+	 * a good read may be obtained by waiting or re-reading (cargocult: 5x)
+	 * each byte.  we'll hope pramin has something usable instead
+	 */
+	for (i = 0; i < NV_PROM_SIZE; i++)
+		data[i] = nv_rd08(dev, NV_PROM_OFFSET + i);
+
+out:
+	/* disable ROM access */
+	nvWriteMC(dev, pci_nv_20,
+		  save_pci_nv_20 | NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED);
+}
+
+static void load_vbios_pramin(struct drm_device *dev, uint8_t *data)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	uint32_t old_bar0_pramin = 0;
+	int i;
+
+	if (dev_priv->card_type >= NV_50) {
+		uint32_t vbios_vram = (nv_rd32(dev, 0x619f04) & ~0xff) << 8;
+
+		if (!vbios_vram)
+			vbios_vram = (nv_rd32(dev, 0x1700) << 16) + 0xf0000;
+
+		old_bar0_pramin = nv_rd32(dev, 0x1700);
+		nv_wr32(dev, 0x1700, vbios_vram >> 16);
+	}
+
+	/* bail if no rom signature */
+	if (nv_rd08(dev, NV_PRAMIN_OFFSET) != 0x55 ||
+	    nv_rd08(dev, NV_PRAMIN_OFFSET + 1) != 0xaa)
+		goto out;
+
+	for (i = 0; i < NV_PROM_SIZE; i++)
+		data[i] = nv_rd08(dev, NV_PRAMIN_OFFSET + i);
+
+out:
+	if (dev_priv->card_type >= NV_50)
+		nv_wr32(dev, 0x1700, old_bar0_pramin);
+}
+
+static void load_vbios_pci(struct drm_device *dev, uint8_t *data)
+{
+	void __iomem *rom = NULL;
+	size_t rom_len;
+	int ret;
+
+	ret = pci_enable_rom(dev->pdev);
+	if (ret)
+		return;
+
+	rom = pci_map_rom(dev->pdev, &rom_len);
+	if (!rom)
+		goto out;
+	memcpy_fromio(data, rom, rom_len);
+	pci_unmap_rom(dev->pdev, rom);
+
+out:
+	pci_disable_rom(dev->pdev);
+}
+
+struct methods {
+	const char desc[8];
+	void (*loadbios)(struct drm_device *, uint8_t *);
+	const bool rw;
+	int score;
+};
+
+static struct methods nv04_methods[] = {
+	{ "PROM", load_vbios_prom, false },
+	{ "PRAMIN", load_vbios_pramin, true },
+	{ "PCIROM", load_vbios_pci, true },
+	{ }
+};
+
+static struct methods nv50_methods[] = {
+	{ "PRAMIN", load_vbios_pramin, true },
+	{ "PROM", load_vbios_prom, false },
+	{ "PCIROM", load_vbios_pci, true },
+	{ }
+};
+
+static bool NVShadowVBIOS(struct drm_device *dev, uint8_t *data)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct methods *methods, *method;
+	int testscore = 3;
+
+	if (nouveau_vbios) {
+		method = nv04_methods;
+		while (method->loadbios) {
+			if (!strcasecmp(nouveau_vbios, method->desc))
+				break;
+			method++;
+		}
+
+		if (method->loadbios) {
+			NV_INFO(dev, "Attempting to use BIOS image from %s\n",
+				method->desc);
+
+			method->loadbios(dev, data);
+			if (score_vbios(dev, data, method->rw))
+				return true;
+		}
+
+		NV_ERROR(dev, "VBIOS source \'%s\' invalid\n", nouveau_vbios);
+	}
+
+	if (dev_priv->card_type < NV_50)
+		methods = nv04_methods;
+	else
+		methods = nv50_methods;
+
+	method = methods;
+	while (method->loadbios) {
+		NV_TRACE(dev, "Attempting to load BIOS image from %s\n",
+			 method->desc);
+		data[0] = data[1] = 0;	/* avoid reuse of previous image */
+		method->loadbios(dev, data);
+		method->score = score_vbios(dev, data, method->rw);
+		if (method->score == testscore)
+			return true;
+		method++;
+	}
+
+	while (--testscore > 0) {
+		method = methods;
+		while (method->loadbios) {
+			if (method->score == testscore) {
+				NV_TRACE(dev, "Using BIOS image from %s\n",
+					 method->desc);
+				method->loadbios(dev, data);
+				return true;
+			}
+			method++;
+		}
+	}
+
+	NV_ERROR(dev, "No valid BIOS image found\n");
+	return false;
+}
+
+struct init_tbl_entry {
+	char *name;
+	uint8_t id;
+	int length;
+	int length_offset;
+	int length_multiplier;
+	bool (*handler)(struct nvbios *, uint16_t, struct init_exec *);
+};
+
+struct bit_entry {
+	uint8_t id[2];
+	uint16_t length;
+	uint16_t offset;
+};
+
+static int parse_init_table(struct nvbios *, unsigned int, struct init_exec *);
+
+#define MACRO_INDEX_SIZE	2
+#define MACRO_SIZE		8
+#define CONDITION_SIZE		12
+#define IO_FLAG_CONDITION_SIZE	9
+#define IO_CONDITION_SIZE	5
+#define MEM_INIT_SIZE		66
+
+static void still_alive(void)
+{
+#if 0
+	sync();
+	msleep(2);
+#endif
+}
+
+static uint32_t
+munge_reg(struct nvbios *bios, uint32_t reg)
+{
+	struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
+	struct dcb_entry *dcbent = bios->display.output;
+
+	if (dev_priv->card_type < NV_50)
+		return reg;
+
+	if (reg & 0x40000000) {
+		BUG_ON(!dcbent);
+
+		reg += (ffs(dcbent->or) - 1) * 0x800;
+		if ((reg & 0x20000000) && !(dcbent->sorconf.link & 1))
+			reg += 0x00000080;
+	}
+
+	reg &= ~0x60000000;
+	return reg;
+}
+
+static int
+valid_reg(struct nvbios *bios, uint32_t reg)
+{
+	struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
+	struct drm_device *dev = bios->dev;
+
+	/* C51 has misaligned regs on purpose. Marvellous */
+	if (reg & 0x2 || (reg & 0x1 && dev_priv->VBIOS.pub.chip_version != 0x51)) {
+		NV_ERROR(dev, "========== misaligned reg 0x%08X ==========\n",
+			 reg);
+		return 0;
+	}
+	/*
+	 * Warn on C51 regs that have not been verified accessible in
+	 * mmiotracing
+	 */
+	if (reg & 0x1 && dev_priv->VBIOS.pub.chip_version == 0x51 &&
+	    reg != 0x130d && reg != 0x1311 && reg != 0x60081d)
+		NV_WARN(dev, "=== C51 misaligned reg 0x%08X not verified ===\n",
+			reg);
+
+	/* Trust the init scripts on G80 */
+	if (dev_priv->card_type >= NV_50)
+		return 1;
+
+	#define WITHIN(x, y, z) ((x >= y) && (x < y + z))
+	if (WITHIN(reg, NV_PMC_OFFSET, NV_PMC_SIZE))
+		return 1;
+	if (WITHIN(reg, NV_PBUS_OFFSET, NV_PBUS_SIZE))
+		return 1;
+	if (WITHIN(reg, NV_PFIFO_OFFSET, NV_PFIFO_SIZE))
+		return 1;
+	if (dev_priv->VBIOS.pub.chip_version >= 0x30 &&
+	    (WITHIN(reg, 0x4000, 0x600) || reg == 0x00004600))
+		return 1;
+	if (dev_priv->VBIOS.pub.chip_version >= 0x40 &&
+						WITHIN(reg, 0xc000, 0x48))
+		return 1;
+	if (dev_priv->VBIOS.pub.chip_version >= 0x17 && reg == 0x0000d204)
+		return 1;
+	if (dev_priv->VBIOS.pub.chip_version >= 0x40) {
+		if (reg == 0x00011014 || reg == 0x00020328)
+			return 1;
+		if (WITHIN(reg, 0x88000, NV_PBUS_SIZE)) /* new PBUS */
+			return 1;
+	}
+	if (WITHIN(reg, NV_PFB_OFFSET, NV_PFB_SIZE))
+		return 1;
+	if (WITHIN(reg, NV_PEXTDEV_OFFSET, NV_PEXTDEV_SIZE))
+		return 1;
+	if (WITHIN(reg, NV_PCRTC0_OFFSET, NV_PCRTC0_SIZE * 2))
+		return 1;
+	if (WITHIN(reg, NV_PRAMDAC0_OFFSET, NV_PRAMDAC0_SIZE * 2))
+		return 1;
+	if (dev_priv->VBIOS.pub.chip_version >= 0x17 && reg == 0x0070fff0)
+		return 1;
+	if (dev_priv->VBIOS.pub.chip_version == 0x51 &&
+				WITHIN(reg, NV_PRAMIN_OFFSET, NV_PRAMIN_SIZE))
+		return 1;
+	#undef WITHIN
+
+	NV_ERROR(dev, "========== unknown reg 0x%08X ==========\n", reg);
+
+	return 0;
+}
+
+static bool
+valid_idx_port(struct nvbios *bios, uint16_t port)
+{
+	struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
+	struct drm_device *dev = bios->dev;
+
+	/*
+	 * If adding more ports here, the read/write functions below will need
+	 * updating so that the correct mmio range (PRMCIO, PRMDIO, PRMVIO) is
+	 * used for the port in question
+	 */
+	if (dev_priv->card_type < NV_50) {
+		if (port == NV_CIO_CRX__COLOR)
+			return true;
+		if (port == NV_VIO_SRX)
+			return true;
+	} else {
+		if (port == NV_CIO_CRX__COLOR)
+			return true;
+	}
+
+	NV_ERROR(dev, "========== unknown indexed io port 0x%04X ==========\n",
+		 port);
+
+	return false;
+}
+
+static bool
+valid_port(struct nvbios *bios, uint16_t port)
+{
+	struct drm_device *dev = bios->dev;
+
+	/*
+	 * If adding more ports here, the read/write functions below will need
+	 * updating so that the correct mmio range (PRMCIO, PRMDIO, PRMVIO) is
+	 * used for the port in question
+	 */
+	if (port == NV_VIO_VSE2)
+		return true;
+
+	NV_ERROR(dev, "========== unknown io port 0x%04X ==========\n", port);
+
+	return false;
+}
+
+static uint32_t
+bios_rd32(struct nvbios *bios, uint32_t reg)
+{
+	uint32_t data;
+
+	reg = munge_reg(bios, reg);
+	if (!valid_reg(bios, reg))
+		return 0;
+
+	/*
+	 * C51 sometimes uses regs with bit0 set in the address. For these
+	 * cases there should exist a translation in a BIOS table to an IO
+	 * port address which the BIOS uses for accessing the reg
+	 *
+	 * These only seem to appear for the power control regs to a flat panel,
+	 * and the GPIO regs at 0x60081*.  In C51 mmio traces the normal regs
+	 * for 0x1308 and 0x1310 are used - hence the mask below.  An S3
+	 * suspend-resume mmio trace from a C51 will be required to see if this
+	 * is true for the power microcode in 0x14.., or whether the direct IO
+	 * port access method is needed
+	 */
+	if (reg & 0x1)
+		reg &= ~0x1;
+
+	data = nv_rd32(bios->dev, reg);
+
+	BIOSLOG(bios, "	Read:  Reg: 0x%08X, Data: 0x%08X\n", reg, data);
+
+	return data;
+}
+
+static void
+bios_wr32(struct nvbios *bios, uint32_t reg, uint32_t data)
+{
+	struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
+
+	reg = munge_reg(bios, reg);
+	if (!valid_reg(bios, reg))
+		return;
+
+	/* see note in bios_rd32 */
+	if (reg & 0x1)
+		reg &= 0xfffffffe;
+
+	LOG_OLD_VALUE(bios_rd32(bios, reg));
+	BIOSLOG(bios, "	Write: Reg: 0x%08X, Data: 0x%08X\n", reg, data);
+
+	if (dev_priv->VBIOS.execute) {
+		still_alive();
+		nv_wr32(bios->dev, reg, data);
+	}
+}
+
+static uint8_t
+bios_idxprt_rd(struct nvbios *bios, uint16_t port, uint8_t index)
+{
+	struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
+	struct drm_device *dev = bios->dev;
+	uint8_t data;
+
+	if (!valid_idx_port(bios, port))
+		return 0;
+
+	if (dev_priv->card_type < NV_50) {
+		if (port == NV_VIO_SRX)
+			data = NVReadVgaSeq(dev, bios->state.crtchead, index);
+		else	/* assume NV_CIO_CRX__COLOR */
+			data = NVReadVgaCrtc(dev, bios->state.crtchead, index);
+	} else {
+		uint32_t data32;
+
+		data32 = bios_rd32(bios, NV50_PDISPLAY_VGACRTC(index & ~3));
+		data = (data32 >> ((index & 3) << 3)) & 0xff;
+	}
+
+	BIOSLOG(bios, "	Indexed IO read:  Port: 0x%04X, Index: 0x%02X, "
+		      "Head: 0x%02X, Data: 0x%02X\n",
+		port, index, bios->state.crtchead, data);
+	return data;
+}
+
+static void
+bios_idxprt_wr(struct nvbios *bios, uint16_t port, uint8_t index, uint8_t data)
+{
+	struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
+	struct drm_device *dev = bios->dev;
+
+	if (!valid_idx_port(bios, port))
+		return;
+
+	/*
+	 * The current head is maintained in the nvbios member  state.crtchead.
+	 * We trap changes to CR44 and update the head variable and hence the
+	 * register set written.
+	 * As CR44 only exists on CRTC0, we update crtchead to head0 in advance
+	 * of the write, and to head1 after the write
+	 */
+	if (port == NV_CIO_CRX__COLOR && index == NV_CIO_CRE_44 &&
+	    data != NV_CIO_CRE_44_HEADB)
+		bios->state.crtchead = 0;
+
+	LOG_OLD_VALUE(bios_idxprt_rd(bios, port, index));
+	BIOSLOG(bios, "	Indexed IO write: Port: 0x%04X, Index: 0x%02X, "
+		      "Head: 0x%02X, Data: 0x%02X\n",
+		port, index, bios->state.crtchead, data);
+
+	if (bios->execute && dev_priv->card_type < NV_50) {
+		still_alive();
+		if (port == NV_VIO_SRX)
+			NVWriteVgaSeq(dev, bios->state.crtchead, index, data);
+		else	/* assume NV_CIO_CRX__COLOR */
+			NVWriteVgaCrtc(dev, bios->state.crtchead, index, data);
+	} else
+	if (bios->execute) {
+		uint32_t data32, shift = (index & 3) << 3;
+
+		still_alive();
+
+		data32  = bios_rd32(bios, NV50_PDISPLAY_VGACRTC(index & ~3));
+		data32 &= ~(0xff << shift);
+		data32 |= (data << shift);
+		bios_wr32(bios, NV50_PDISPLAY_VGACRTC(index & ~3), data32);
+	}
+
+	if (port == NV_CIO_CRX__COLOR &&
+	    index == NV_CIO_CRE_44 && data == NV_CIO_CRE_44_HEADB)
+		bios->state.crtchead = 1;
+}
+
+static uint8_t
+bios_port_rd(struct nvbios *bios, uint16_t port)
+{
+	uint8_t data, head = bios->state.crtchead;
+
+	if (!valid_port(bios, port))
+		return 0;
+
+	data = NVReadPRMVIO(bios->dev, head, NV_PRMVIO0_OFFSET + port);
+
+	BIOSLOG(bios, "	IO read:  Port: 0x%04X, Head: 0x%02X, Data: 0x%02X\n",
+		port, head, data);
+
+	return data;
+}
+
+static void
+bios_port_wr(struct nvbios *bios, uint16_t port, uint8_t data)
+{
+	int head = bios->state.crtchead;
+
+	if (!valid_port(bios, port))
+		return;
+
+	LOG_OLD_VALUE(bios_port_rd(bios, port));
+	BIOSLOG(bios, "	IO write: Port: 0x%04X, Head: 0x%02X, Data: 0x%02X\n",
+		port, head, data);
+
+	if (!bios->execute)
+		return;
+
+	still_alive();
+	NVWritePRMVIO(bios->dev, head, NV_PRMVIO0_OFFSET + port, data);
+}
+
+static bool
+io_flag_condition_met(struct nvbios *bios, uint16_t offset, uint8_t cond)
+{
+	/*
+	 * The IO flag condition entry has 2 bytes for the CRTC port; 1 byte
+	 * for the CRTC index; 1 byte for the mask to apply to the value
+	 * retrieved from the CRTC; 1 byte for the shift right to apply to the
+	 * masked CRTC value; 2 bytes for the offset to the flag array, to
+	 * which the shifted value is added; 1 byte for the mask applied to the
+	 * value read from the flag array; and 1 byte for the value to compare
+	 * against the masked byte from the flag table.
+	 */
+
+	uint16_t condptr = bios->io_flag_condition_tbl_ptr + cond * IO_FLAG_CONDITION_SIZE;
+	uint16_t crtcport = ROM16(bios->data[condptr]);
+	uint8_t crtcindex = bios->data[condptr + 2];
+	uint8_t mask = bios->data[condptr + 3];
+	uint8_t shift = bios->data[condptr + 4];
+	uint16_t flagarray = ROM16(bios->data[condptr + 5]);
+	uint8_t flagarraymask = bios->data[condptr + 7];
+	uint8_t cmpval = bios->data[condptr + 8];
+	uint8_t data;
+
+	BIOSLOG(bios, "0x%04X: Port: 0x%04X, Index: 0x%02X, Mask: 0x%02X, "
+		      "Shift: 0x%02X, FlagArray: 0x%04X, FAMask: 0x%02X, "
+		      "Cmpval: 0x%02X\n",
+		offset, crtcport, crtcindex, mask, shift, flagarray, flagarraymask, cmpval);
+
+	data = bios_idxprt_rd(bios, crtcport, crtcindex);
+
+	data = bios->data[flagarray + ((data & mask) >> shift)];
+	data &= flagarraymask;
+
+	BIOSLOG(bios, "0x%04X: Checking if 0x%02X equals 0x%02X\n",
+		offset, data, cmpval);
+
+	return (data == cmpval);
+}
+
+static bool
+bios_condition_met(struct nvbios *bios, uint16_t offset, uint8_t cond)
+{
+	/*
+	 * The condition table entry has 4 bytes for the address of the
+	 * register to check, 4 bytes for a mask to apply to the register and
+	 * 4 for a test comparison value
+	 */
+
+	uint16_t condptr = bios->condition_tbl_ptr + cond * CONDITION_SIZE;
+	uint32_t reg = ROM32(bios->data[condptr]);
+	uint32_t mask = ROM32(bios->data[condptr + 4]);
+	uint32_t cmpval = ROM32(bios->data[condptr + 8]);
+	uint32_t data;
+
+	BIOSLOG(bios, "0x%04X: Cond: 0x%02X, Reg: 0x%08X, Mask: 0x%08X\n",
+		offset, cond, reg, mask);
+
+	data = bios_rd32(bios, reg) & mask;
+
+	BIOSLOG(bios, "0x%04X: Checking if 0x%08X equals 0x%08X\n",
+		offset, data, cmpval);
+
+	return (data == cmpval);
+}
+
+static bool
+io_condition_met(struct nvbios *bios, uint16_t offset, uint8_t cond)
+{
+	/*
+	 * The IO condition entry has 2 bytes for the IO port address; 1 byte
+	 * for the index to write to io_port; 1 byte for the mask to apply to
+	 * the byte read from io_port+1; and 1 byte for the value to compare
+	 * against the masked byte.
+	 */
+
+	uint16_t condptr = bios->io_condition_tbl_ptr + cond * IO_CONDITION_SIZE;
+	uint16_t io_port = ROM16(bios->data[condptr]);
+	uint8_t port_index = bios->data[condptr + 2];
+	uint8_t mask = bios->data[condptr + 3];
+	uint8_t cmpval = bios->data[condptr + 4];
+
+	uint8_t data = bios_idxprt_rd(bios, io_port, port_index) & mask;
+
+	BIOSLOG(bios, "0x%04X: Checking if 0x%02X equals 0x%02X\n",
+		offset, data, cmpval);
+
+	return (data == cmpval);
+}
+
+static int
+nv50_pll_set(struct drm_device *dev, uint32_t reg, uint32_t clk)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	uint32_t reg0 = nv_rd32(dev, reg + 0);
+	uint32_t reg1 = nv_rd32(dev, reg + 4);
+	struct nouveau_pll_vals pll;
+	struct pll_lims pll_limits;
+	int ret;
+
+	ret = get_pll_limits(dev, reg, &pll_limits);
+	if (ret)
+		return ret;
+
+	clk = nouveau_calc_pll_mnp(dev, &pll_limits, clk, &pll);
+	if (!clk)
+		return -ERANGE;
+
+	reg0 = (reg0 & 0xfff8ffff) | (pll.log2P << 16);
+	reg1 = (reg1 & 0xffff0000) | (pll.N1 << 8) | pll.M1;
+
+	if (dev_priv->VBIOS.execute) {
+		still_alive();
+		nv_wr32(dev, reg + 4, reg1);
+		nv_wr32(dev, reg + 0, reg0);
+	}
+
+	return 0;
+}
+
+static int
+setPLL(struct nvbios *bios, uint32_t reg, uint32_t clk)
+{
+	struct drm_device *dev = bios->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	/* clk in kHz */
+	struct pll_lims pll_lim;
+	struct nouveau_pll_vals pllvals;
+	int ret;
+
+	if (dev_priv->card_type >= NV_50)
+		return nv50_pll_set(dev, reg, clk);
+
+	/* high regs (such as in the mac g5 table) are not -= 4 */
+	ret = get_pll_limits(dev, reg > 0x405c ? reg : reg - 4, &pll_lim);
+	if (ret)
+		return ret;
+
+	clk = nouveau_calc_pll_mnp(dev, &pll_lim, clk, &pllvals);
+	if (!clk)
+		return -ERANGE;
+
+	if (bios->execute) {
+		still_alive();
+		nouveau_hw_setpll(dev, reg, &pllvals);
+	}
+
+	return 0;
+}
+
+static int dcb_entry_idx_from_crtchead(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nvbios *bios = &dev_priv->VBIOS;
+
+	/*
+	 * For the results of this function to be correct, CR44 must have been
+	 * set (using bios_idxprt_wr to set crtchead), CR58 set for CR57 = 0,
+	 * and the DCB table parsed, before the script calling the function is
+	 * run.  run_digital_op_script is example of how to do such setup
+	 */
+
+	uint8_t dcb_entry = NVReadVgaCrtc5758(dev, bios->state.crtchead, 0);
+
+	if (dcb_entry > bios->bdcb.dcb.entries) {
+		NV_ERROR(dev, "CR58 doesn't have a valid DCB entry currently "
+				"(%02X)\n", dcb_entry);
+		dcb_entry = 0x7f;	/* unused / invalid marker */
+	}
+
+	return dcb_entry;
+}
+
+static struct nouveau_i2c_chan *
+init_i2c_device_find(struct drm_device *dev, int i2c_index)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct bios_parsed_dcb *bdcb = &dev_priv->VBIOS.bdcb;
+
+	if (i2c_index == 0xff) {
+		/* note: dcb_entry_idx_from_crtchead needs pre-script set-up */
+		int idx = dcb_entry_idx_from_crtchead(dev), shift = 0;
+		int default_indices = bdcb->i2c_default_indices;
+
+		if (idx != 0x7f && bdcb->dcb.entry[idx].i2c_upper_default)
+			shift = 4;
+
+		i2c_index = (default_indices >> shift) & 0xf;
+	}
+	if (i2c_index == 0x80)	/* g80+ */
+		i2c_index = bdcb->i2c_default_indices & 0xf;
+
+	return nouveau_i2c_find(dev, i2c_index);
+}
+
+static uint32_t get_tmds_index_reg(struct drm_device *dev, uint8_t mlv)
+{
+	/*
+	 * For mlv < 0x80, it is an index into a table of TMDS base addresses.
+	 * For mlv == 0x80 use the "or" value of the dcb_entry indexed by
+	 * CR58 for CR57 = 0 to index a table of offsets to the basic
+	 * 0x6808b0 address.
+	 * For mlv == 0x81 use the "or" value of the dcb_entry indexed by
+	 * CR58 for CR57 = 0 to index a table of offsets to the basic
+	 * 0x6808b0 address, and then flip the offset by 8.
+	 */
+
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	const int pramdac_offset[13] = {
+		0, 0, 0x8, 0, 0x2000, 0, 0, 0, 0x2008, 0, 0, 0, 0x2000 };
+	const uint32_t pramdac_table[4] = {
+		0x6808b0, 0x6808b8, 0x6828b0, 0x6828b8 };
+
+	if (mlv >= 0x80) {
+		int dcb_entry, dacoffset;
+
+		/* note: dcb_entry_idx_from_crtchead needs pre-script set-up */
+		dcb_entry = dcb_entry_idx_from_crtchead(dev);
+		if (dcb_entry == 0x7f)
+			return 0;
+		dacoffset = pramdac_offset[
+				dev_priv->VBIOS.bdcb.dcb.entry[dcb_entry].or];
+		if (mlv == 0x81)
+			dacoffset ^= 8;
+		return 0x6808b0 + dacoffset;
+	} else {
+		if (mlv > ARRAY_SIZE(pramdac_table)) {
+			NV_ERROR(dev, "Magic Lookup Value too big (%02X)\n",
+									mlv);
+			return 0;
+		}
+		return pramdac_table[mlv];
+	}
+}
+
+static bool
+init_io_restrict_prog(struct nvbios *bios, uint16_t offset,
+		      struct init_exec *iexec)
+{
+	/*
+	 * INIT_IO_RESTRICT_PROG   opcode: 0x32 ('2')
+	 *
+	 * offset      (8  bit): opcode
+	 * offset + 1  (16 bit): CRTC port
+	 * offset + 3  (8  bit): CRTC index
+	 * offset + 4  (8  bit): mask
+	 * offset + 5  (8  bit): shift
+	 * offset + 6  (8  bit): count
+	 * offset + 7  (32 bit): register
+	 * offset + 11 (32 bit): configuration 1
+	 * ...
+	 *
+	 * Starting at offset + 11 there are "count" 32 bit values.
+	 * To find out which value to use read index "CRTC index" on "CRTC
+	 * port", AND this value with "mask" and then bit shift right "shift"
+	 * bits.  Read the appropriate value using this index and write to
+	 * "register"
+	 */
+
+	uint16_t crtcport = ROM16(bios->data[offset + 1]);
+	uint8_t crtcindex = bios->data[offset + 3];
+	uint8_t mask = bios->data[offset + 4];
+	uint8_t shift = bios->data[offset + 5];
+	uint8_t count = bios->data[offset + 6];
+	uint32_t reg = ROM32(bios->data[offset + 7]);
+	uint8_t config;
+	uint32_t configval;
+
+	if (!iexec->execute)
+		return true;
+
+	BIOSLOG(bios, "0x%04X: Port: 0x%04X, Index: 0x%02X, Mask: 0x%02X, "
+		      "Shift: 0x%02X, Count: 0x%02X, Reg: 0x%08X\n",
+		offset, crtcport, crtcindex, mask, shift, count, reg);
+
+	config = (bios_idxprt_rd(bios, crtcport, crtcindex) & mask) >> shift;
+	if (config > count) {
+		NV_ERROR(bios->dev,
+			 "0x%04X: Config 0x%02X exceeds maximal bound 0x%02X\n",
+			 offset, config, count);
+		return false;
+	}
+
+	configval = ROM32(bios->data[offset + 11 + config * 4]);
+
+	BIOSLOG(bios, "0x%04X: Writing config %02X\n", offset, config);
+
+	bios_wr32(bios, reg, configval);
+
+	return true;
+}
+
+static bool
+init_repeat(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_REPEAT   opcode: 0x33 ('3')
+	 *
+	 * offset      (8 bit): opcode
+	 * offset + 1  (8 bit): count
+	 *
+	 * Execute script following this opcode up to INIT_REPEAT_END
+	 * "count" times
+	 */
+
+	uint8_t count = bios->data[offset + 1];
+	uint8_t i;
+
+	/* no iexec->execute check by design */
+
+	BIOSLOG(bios, "0x%04X: Repeating following segment %d times\n",
+		offset, count);
+
+	iexec->repeat = true;
+
+	/*
+	 * count - 1, as the script block will execute once when we leave this
+	 * opcode -- this is compatible with bios behaviour as:
+	 * a) the block is always executed at least once, even if count == 0
+	 * b) the bios interpreter skips to the op following INIT_END_REPEAT,
+	 * while we don't
+	 */
+	for (i = 0; i < count - 1; i++)
+		parse_init_table(bios, offset + 2, iexec);
+
+	iexec->repeat = false;
+
+	return true;
+}
+
+static bool
+init_io_restrict_pll(struct nvbios *bios, uint16_t offset,
+		     struct init_exec *iexec)
+{
+	/*
+	 * INIT_IO_RESTRICT_PLL   opcode: 0x34 ('4')
+	 *
+	 * offset      (8  bit): opcode
+	 * offset + 1  (16 bit): CRTC port
+	 * offset + 3  (8  bit): CRTC index
+	 * offset + 4  (8  bit): mask
+	 * offset + 5  (8  bit): shift
+	 * offset + 6  (8  bit): IO flag condition index
+	 * offset + 7  (8  bit): count
+	 * offset + 8  (32 bit): register
+	 * offset + 12 (16 bit): frequency 1
+	 * ...
+	 *
+	 * Starting at offset + 12 there are "count" 16 bit frequencies (10kHz).
+	 * Set PLL register "register" to coefficients for frequency n,
+	 * selected by reading index "CRTC index" of "CRTC port" ANDed with
+	 * "mask" and shifted right by "shift".
+	 *
+	 * If "IO flag condition index" > 0, and condition met, double
+	 * frequency before setting it.
+	 */
+
+	uint16_t crtcport = ROM16(bios->data[offset + 1]);
+	uint8_t crtcindex = bios->data[offset + 3];
+	uint8_t mask = bios->data[offset + 4];
+	uint8_t shift = bios->data[offset + 5];
+	int8_t io_flag_condition_idx = bios->data[offset + 6];
+	uint8_t count = bios->data[offset + 7];
+	uint32_t reg = ROM32(bios->data[offset + 8]);
+	uint8_t config;
+	uint16_t freq;
+
+	if (!iexec->execute)
+		return true;
+
+	BIOSLOG(bios, "0x%04X: Port: 0x%04X, Index: 0x%02X, Mask: 0x%02X, "
+		      "Shift: 0x%02X, IO Flag Condition: 0x%02X, "
+		      "Count: 0x%02X, Reg: 0x%08X\n",
+		offset, crtcport, crtcindex, mask, shift,
+		io_flag_condition_idx, count, reg);
+
+	config = (bios_idxprt_rd(bios, crtcport, crtcindex) & mask) >> shift;
+	if (config > count) {
+		NV_ERROR(bios->dev,
+			 "0x%04X: Config 0x%02X exceeds maximal bound 0x%02X\n",
+			 offset, config, count);
+		return false;
+	}
+
+	freq = ROM16(bios->data[offset + 12 + config * 2]);
+
+	if (io_flag_condition_idx > 0) {
+		if (io_flag_condition_met(bios, offset, io_flag_condition_idx)) {
+			BIOSLOG(bios, "0x%04X: Condition fulfilled -- "
+				      "frequency doubled\n", offset);
+			freq *= 2;
+		} else
+			BIOSLOG(bios, "0x%04X: Condition not fulfilled -- "
+				      "frequency unchanged\n", offset);
+	}
+
+	BIOSLOG(bios, "0x%04X: Reg: 0x%08X, Config: 0x%02X, Freq: %d0kHz\n",
+		offset, reg, config, freq);
+
+	setPLL(bios, reg, freq * 10);
+
+	return true;
+}
+
+static bool
+init_end_repeat(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_END_REPEAT   opcode: 0x36 ('6')
+	 *
+	 * offset      (8 bit): opcode
+	 *
+	 * Marks the end of the block for INIT_REPEAT to repeat
+	 */
+
+	/* no iexec->execute check by design */
+
+	/*
+	 * iexec->repeat flag necessary to go past INIT_END_REPEAT opcode when
+	 * we're not in repeat mode
+	 */
+	if (iexec->repeat)
+		return false;
+
+	return true;
+}
+
+static bool
+init_copy(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_COPY   opcode: 0x37 ('7')
+	 *
+	 * offset      (8  bit): opcode
+	 * offset + 1  (32 bit): register
+	 * offset + 5  (8  bit): shift
+	 * offset + 6  (8  bit): srcmask
+	 * offset + 7  (16 bit): CRTC port
+	 * offset + 9  (8 bit): CRTC index
+	 * offset + 10  (8 bit): mask
+	 *
+	 * Read index "CRTC index" on "CRTC port", AND with "mask", OR with
+	 * (REGVAL("register") >> "shift" & "srcmask") and write-back to CRTC
+	 * port
+	 */
+
+	uint32_t reg = ROM32(bios->data[offset + 1]);
+	uint8_t shift = bios->data[offset + 5];
+	uint8_t srcmask = bios->data[offset + 6];
+	uint16_t crtcport = ROM16(bios->data[offset + 7]);
+	uint8_t crtcindex = bios->data[offset + 9];
+	uint8_t mask = bios->data[offset + 10];
+	uint32_t data;
+	uint8_t crtcdata;
+
+	if (!iexec->execute)
+		return true;
+
+	BIOSLOG(bios, "0x%04X: Reg: 0x%08X, Shift: 0x%02X, SrcMask: 0x%02X, "
+		      "Port: 0x%04X, Index: 0x%02X, Mask: 0x%02X\n",
+		offset, reg, shift, srcmask, crtcport, crtcindex, mask);
+
+	data = bios_rd32(bios, reg);
+
+	if (shift < 0x80)
+		data >>= shift;
+	else
+		data <<= (0x100 - shift);
+
+	data &= srcmask;
+
+	crtcdata  = bios_idxprt_rd(bios, crtcport, crtcindex) & mask;
+	crtcdata |= (uint8_t)data;
+	bios_idxprt_wr(bios, crtcport, crtcindex, crtcdata);
+
+	return true;
+}
+
+static bool
+init_not(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_NOT   opcode: 0x38 ('8')
+	 *
+	 * offset      (8  bit): opcode
+	 *
+	 * Invert the current execute / no-execute condition (i.e. "else")
+	 */
+	if (iexec->execute)
+		BIOSLOG(bios, "0x%04X: ------ Skipping following commands  ------\n", offset);
+	else
+		BIOSLOG(bios, "0x%04X: ------ Executing following commands ------\n", offset);
+
+	iexec->execute = !iexec->execute;
+	return true;
+}
+
+static bool
+init_io_flag_condition(struct nvbios *bios, uint16_t offset,
+		       struct init_exec *iexec)
+{
+	/*
+	 * INIT_IO_FLAG_CONDITION   opcode: 0x39 ('9')
+	 *
+	 * offset      (8 bit): opcode
+	 * offset + 1  (8 bit): condition number
+	 *
+	 * Check condition "condition number" in the IO flag condition table.
+	 * If condition not met skip subsequent opcodes until condition is
+	 * inverted (INIT_NOT), or we hit INIT_RESUME
+	 */
+
+	uint8_t cond = bios->data[offset + 1];
+
+	if (!iexec->execute)
+		return true;
+
+	if (io_flag_condition_met(bios, offset, cond))
+		BIOSLOG(bios, "0x%04X: Condition fulfilled -- continuing to execute\n", offset);
+	else {
+		BIOSLOG(bios, "0x%04X: Condition not fulfilled -- skipping following commands\n", offset);
+		iexec->execute = false;
+	}
+
+	return true;
+}
+
+static bool
+init_idx_addr_latched(struct nvbios *bios, uint16_t offset,
+		      struct init_exec *iexec)
+{
+	/*
+	 * INIT_INDEX_ADDRESS_LATCHED   opcode: 0x49 ('I')
+	 *
+	 * offset      (8  bit): opcode
+	 * offset + 1  (32 bit): control register
+	 * offset + 5  (32 bit): data register
+	 * offset + 9  (32 bit): mask
+	 * offset + 13 (32 bit): data
+	 * offset + 17 (8  bit): count
+	 * offset + 18 (8  bit): address 1
+	 * offset + 19 (8  bit): data 1
+	 * ...
+	 *
+	 * For each of "count" address and data pairs, write "data n" to
+	 * "data register", read the current value of "control register",
+	 * and write it back once ANDed with "mask", ORed with "data",
+	 * and ORed with "address n"
+	 */
+
+	uint32_t controlreg = ROM32(bios->data[offset + 1]);
+	uint32_t datareg = ROM32(bios->data[offset + 5]);
+	uint32_t mask = ROM32(bios->data[offset + 9]);
+	uint32_t data = ROM32(bios->data[offset + 13]);
+	uint8_t count = bios->data[offset + 17];
+	uint32_t value;
+	int i;
+
+	if (!iexec->execute)
+		return true;
+
+	BIOSLOG(bios, "0x%04X: ControlReg: 0x%08X, DataReg: 0x%08X, "
+		      "Mask: 0x%08X, Data: 0x%08X, Count: 0x%02X\n",
+		offset, controlreg, datareg, mask, data, count);
+
+	for (i = 0; i < count; i++) {
+		uint8_t instaddress = bios->data[offset + 18 + i * 2];
+		uint8_t instdata = bios->data[offset + 19 + i * 2];
+
+		BIOSLOG(bios, "0x%04X: Address: 0x%02X, Data: 0x%02X\n",
+			offset, instaddress, instdata);
+
+		bios_wr32(bios, datareg, instdata);
+		value  = bios_rd32(bios, controlreg) & mask;
+		value |= data;
+		value |= instaddress;
+		bios_wr32(bios, controlreg, value);
+	}
+
+	return true;
+}
+
+static bool
+init_io_restrict_pll2(struct nvbios *bios, uint16_t offset,
+		      struct init_exec *iexec)
+{
+	/*
+	 * INIT_IO_RESTRICT_PLL2   opcode: 0x4A ('J')
+	 *
+	 * offset      (8  bit): opcode
+	 * offset + 1  (16 bit): CRTC port
+	 * offset + 3  (8  bit): CRTC index
+	 * offset + 4  (8  bit): mask
+	 * offset + 5  (8  bit): shift
+	 * offset + 6  (8  bit): count
+	 * offset + 7  (32 bit): register
+	 * offset + 11 (32 bit): frequency 1
+	 * ...
+	 *
+	 * Starting at offset + 11 there are "count" 32 bit frequencies (kHz).
+	 * Set PLL register "register" to coefficients for frequency n,
+	 * selected by reading index "CRTC index" of "CRTC port" ANDed with
+	 * "mask" and shifted right by "shift".
+	 */
+
+	uint16_t crtcport = ROM16(bios->data[offset + 1]);
+	uint8_t crtcindex = bios->data[offset + 3];
+	uint8_t mask = bios->data[offset + 4];
+	uint8_t shift = bios->data[offset + 5];
+	uint8_t count = bios->data[offset + 6];
+	uint32_t reg = ROM32(bios->data[offset + 7]);
+	uint8_t config;
+	uint32_t freq;
+
+	if (!iexec->execute)
+		return true;
+
+	BIOSLOG(bios, "0x%04X: Port: 0x%04X, Index: 0x%02X, Mask: 0x%02X, "
+		      "Shift: 0x%02X, Count: 0x%02X, Reg: 0x%08X\n",
+		offset, crtcport, crtcindex, mask, shift, count, reg);
+
+	if (!reg)
+		return true;
+
+	config = (bios_idxprt_rd(bios, crtcport, crtcindex) & mask) >> shift;
+	if (config > count) {
+		NV_ERROR(bios->dev,
+			 "0x%04X: Config 0x%02X exceeds maximal bound 0x%02X\n",
+			 offset, config, count);
+		return false;
+	}
+
+	freq = ROM32(bios->data[offset + 11 + config * 4]);
+
+	BIOSLOG(bios, "0x%04X: Reg: 0x%08X, Config: 0x%02X, Freq: %dkHz\n",
+		offset, reg, config, freq);
+
+	setPLL(bios, reg, freq);
+
+	return true;
+}
+
+static bool
+init_pll2(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_PLL2   opcode: 0x4B ('K')
+	 *
+	 * offset      (8  bit): opcode
+	 * offset + 1  (32 bit): register
+	 * offset + 5  (32 bit): freq
+	 *
+	 * Set PLL register "register" to coefficients for frequency "freq"
+	 */
+
+	uint32_t reg = ROM32(bios->data[offset + 1]);
+	uint32_t freq = ROM32(bios->data[offset + 5]);
+
+	if (!iexec->execute)
+		return true;
+
+	BIOSLOG(bios, "0x%04X: Reg: 0x%04X, Freq: %dkHz\n",
+		offset, reg, freq);
+
+	setPLL(bios, reg, freq);
+	return true;
+}
+
+static bool
+init_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_I2C_BYTE   opcode: 0x4C ('L')
+	 *
+	 * offset      (8 bit): opcode
+	 * offset + 1  (8 bit): DCB I2C table entry index
+	 * offset + 2  (8 bit): I2C slave address
+	 * offset + 3  (8 bit): count
+	 * offset + 4  (8 bit): I2C register 1
+	 * offset + 5  (8 bit): mask 1
+	 * offset + 6  (8 bit): data 1
+	 * ...
+	 *
+	 * For each of "count" registers given by "I2C register n" on the device
+	 * addressed by "I2C slave address" on the I2C bus given by
+	 * "DCB I2C table entry index", read the register, AND the result with
+	 * "mask n" and OR it with "data n" before writing it back to the device
+	 */
+
+	uint8_t i2c_index = bios->data[offset + 1];
+	uint8_t i2c_address = bios->data[offset + 2];
+	uint8_t count = bios->data[offset + 3];
+	struct nouveau_i2c_chan *chan;
+	struct i2c_msg msg;
+	int i;
+
+	if (!iexec->execute)
+		return true;
+
+	BIOSLOG(bios, "0x%04X: DCBI2CIndex: 0x%02X, I2CAddress: 0x%02X, "
+		      "Count: 0x%02X\n",
+		offset, i2c_index, i2c_address, count);
+
+	chan = init_i2c_device_find(bios->dev, i2c_index);
+	if (!chan)
+		return false;
+
+	for (i = 0; i < count; i++) {
+		uint8_t i2c_reg = bios->data[offset + 4 + i * 3];
+		uint8_t mask = bios->data[offset + 5 + i * 3];
+		uint8_t data = bios->data[offset + 6 + i * 3];
+		uint8_t value;
+
+		msg.addr = i2c_address;
+		msg.flags = I2C_M_RD;
+		msg.len = 1;
+		msg.buf = &value;
+		if (i2c_transfer(&chan->adapter, &msg, 1) != 1)
+			return false;
+
+		BIOSLOG(bios, "0x%04X: I2CReg: 0x%02X, Value: 0x%02X, "
+			      "Mask: 0x%02X, Data: 0x%02X\n",
+			offset, i2c_reg, value, mask, data);
+
+		value = (value & mask) | data;
+
+		if (bios->execute) {
+			msg.addr = i2c_address;
+			msg.flags = 0;
+			msg.len = 1;
+			msg.buf = &value;
+			if (i2c_transfer(&chan->adapter, &msg, 1) != 1)
+				return false;
+		}
+	}
+
+	return true;
+}
+
+static bool
+init_zm_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_ZM_I2C_BYTE   opcode: 0x4D ('M')
+	 *
+	 * offset      (8 bit): opcode
+	 * offset + 1  (8 bit): DCB I2C table entry index
+	 * offset + 2  (8 bit): I2C slave address
+	 * offset + 3  (8 bit): count
+	 * offset + 4  (8 bit): I2C register 1
+	 * offset + 5  (8 bit): data 1
+	 * ...
+	 *
+	 * For each of "count" registers given by "I2C register n" on the device
+	 * addressed by "I2C slave address" on the I2C bus given by
+	 * "DCB I2C table entry index", set the register to "data n"
+	 */
+
+	uint8_t i2c_index = bios->data[offset + 1];
+	uint8_t i2c_address = bios->data[offset + 2];
+	uint8_t count = bios->data[offset + 3];
+	struct nouveau_i2c_chan *chan;
+	struct i2c_msg msg;
+	int i;
+
+	if (!iexec->execute)
+		return true;
+
+	BIOSLOG(bios, "0x%04X: DCBI2CIndex: 0x%02X, I2CAddress: 0x%02X, "
+		      "Count: 0x%02X\n",
+		offset, i2c_index, i2c_address, count);
+
+	chan = init_i2c_device_find(bios->dev, i2c_index);
+	if (!chan)
+		return false;
+
+	for (i = 0; i < count; i++) {
+		uint8_t i2c_reg = bios->data[offset + 4 + i * 2];
+		uint8_t data = bios->data[offset + 5 + i * 2];
+
+		BIOSLOG(bios, "0x%04X: I2CReg: 0x%02X, Data: 0x%02X\n",
+			offset, i2c_reg, data);
+
+		if (bios->execute) {
+			msg.addr = i2c_address;
+			msg.flags = 0;
+			msg.len = 1;
+			msg.buf = &data;
+			if (i2c_transfer(&chan->adapter, &msg, 1) != 1)
+				return false;
+		}
+	}
+
+	return true;
+}
+
+static bool
+init_zm_i2c(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_ZM_I2C   opcode: 0x4E ('N')
+	 *
+	 * offset      (8 bit): opcode
+	 * offset + 1  (8 bit): DCB I2C table entry index
+	 * offset + 2  (8 bit): I2C slave address
+	 * offset + 3  (8 bit): count
+	 * offset + 4  (8 bit): data 1
+	 * ...
+	 *
+	 * Send "count" bytes ("data n") to the device addressed by "I2C slave
+	 * address" on the I2C bus given by "DCB I2C table entry index"
+	 */
+
+	uint8_t i2c_index = bios->data[offset + 1];
+	uint8_t i2c_address = bios->data[offset + 2];
+	uint8_t count = bios->data[offset + 3];
+	struct nouveau_i2c_chan *chan;
+	struct i2c_msg msg;
+	uint8_t data[256];
+	int i;
+
+	if (!iexec->execute)
+		return true;
+
+	BIOSLOG(bios, "0x%04X: DCBI2CIndex: 0x%02X, I2CAddress: 0x%02X, "
+		      "Count: 0x%02X\n",
+		offset, i2c_index, i2c_address, count);
+
+	chan = init_i2c_device_find(bios->dev, i2c_index);
+	if (!chan)
+		return false;
+
+	for (i = 0; i < count; i++) {
+		data[i] = bios->data[offset + 4 + i];
+
+		BIOSLOG(bios, "0x%04X: Data: 0x%02X\n", offset, data[i]);
+	}
+
+	if (bios->execute) {
+		msg.addr = i2c_address;
+		msg.flags = 0;
+		msg.len = count;
+		msg.buf = data;
+		if (i2c_transfer(&chan->adapter, &msg, 1) != 1)
+			return false;
+	}
+
+	return true;
+}
+
+static bool
+init_tmds(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_TMDS   opcode: 0x4F ('O')	(non-canon name)
+	 *
+	 * offset      (8 bit): opcode
+	 * offset + 1  (8 bit): magic lookup value
+	 * offset + 2  (8 bit): TMDS address
+	 * offset + 3  (8 bit): mask
+	 * offset + 4  (8 bit): data
+	 *
+	 * Read the data reg for TMDS address "TMDS address", AND it with mask
+	 * and OR it with data, then write it back
+	 * "magic lookup value" determines which TMDS base address register is
+	 * used -- see get_tmds_index_reg()
+	 */
+
+	uint8_t mlv = bios->data[offset + 1];
+	uint32_t tmdsaddr = bios->data[offset + 2];
+	uint8_t mask = bios->data[offset + 3];
+	uint8_t data = bios->data[offset + 4];
+	uint32_t reg, value;
+
+	if (!iexec->execute)
+		return true;
+
+	BIOSLOG(bios, "0x%04X: MagicLookupValue: 0x%02X, TMDSAddr: 0x%02X, "
+		      "Mask: 0x%02X, Data: 0x%02X\n",
+		offset, mlv, tmdsaddr, mask, data);
+
+	reg = get_tmds_index_reg(bios->dev, mlv);
+	if (!reg)
+		return false;
+
+	bios_wr32(bios, reg,
+		  tmdsaddr | NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE);
+	value = (bios_rd32(bios, reg + 4) & mask) | data;
+	bios_wr32(bios, reg + 4, value);
+	bios_wr32(bios, reg, tmdsaddr);
+
+	return true;
+}
+
+static bool
+init_zm_tmds_group(struct nvbios *bios, uint16_t offset,
+		   struct init_exec *iexec)
+{
+	/*
+	 * INIT_ZM_TMDS_GROUP   opcode: 0x50 ('P')	(non-canon name)
+	 *
+	 * offset      (8 bit): opcode
+	 * offset + 1  (8 bit): magic lookup value
+	 * offset + 2  (8 bit): count
+	 * offset + 3  (8 bit): addr 1
+	 * offset + 4  (8 bit): data 1
+	 * ...
+	 *
+	 * For each of "count" TMDS address and data pairs write "data n" to
+	 * "addr n".  "magic lookup value" determines which TMDS base address
+	 * register is used -- see get_tmds_index_reg()
+	 */
+
+	uint8_t mlv = bios->data[offset + 1];
+	uint8_t count = bios->data[offset + 2];
+	uint32_t reg;
+	int i;
+
+	if (!iexec->execute)
+		return true;
+
+	BIOSLOG(bios, "0x%04X: MagicLookupValue: 0x%02X, Count: 0x%02X\n",
+		offset, mlv, count);
+
+	reg = get_tmds_index_reg(bios->dev, mlv);
+	if (!reg)
+		return false;
+
+	for (i = 0; i < count; i++) {
+		uint8_t tmdsaddr = bios->data[offset + 3 + i * 2];
+		uint8_t tmdsdata = bios->data[offset + 4 + i * 2];
+
+		bios_wr32(bios, reg + 4, tmdsdata);
+		bios_wr32(bios, reg, tmdsaddr);
+	}
+
+	return true;
+}
+
+static bool
+init_cr_idx_adr_latch(struct nvbios *bios, uint16_t offset,
+		      struct init_exec *iexec)
+{
+	/*
+	 * INIT_CR_INDEX_ADDRESS_LATCHED   opcode: 0x51 ('Q')
+	 *
+	 * offset      (8 bit): opcode
+	 * offset + 1  (8 bit): CRTC index1
+	 * offset + 2  (8 bit): CRTC index2
+	 * offset + 3  (8 bit): baseaddr
+	 * offset + 4  (8 bit): count
+	 * offset + 5  (8 bit): data 1
+	 * ...
+	 *
+	 * For each of "count" address and data pairs, write "baseaddr + n" to
+	 * "CRTC index1" and "data n" to "CRTC index2"
+	 * Once complete, restore initial value read from "CRTC index1"
+	 */
+	uint8_t crtcindex1 = bios->data[offset + 1];
+	uint8_t crtcindex2 = bios->data[offset + 2];
+	uint8_t baseaddr = bios->data[offset + 3];
+	uint8_t count = bios->data[offset + 4];
+	uint8_t oldaddr, data;
+	int i;
+
+	if (!iexec->execute)
+		return true;
+
+	BIOSLOG(bios, "0x%04X: Index1: 0x%02X, Index2: 0x%02X, "
+		      "BaseAddr: 0x%02X, Count: 0x%02X\n",
+		offset, crtcindex1, crtcindex2, baseaddr, count);
+
+	oldaddr = bios_idxprt_rd(bios, NV_CIO_CRX__COLOR, crtcindex1);
+
+	for (i = 0; i < count; i++) {
+		bios_idxprt_wr(bios, NV_CIO_CRX__COLOR, crtcindex1,
+				     baseaddr + i);
+		data = bios->data[offset + 5 + i];
+		bios_idxprt_wr(bios, NV_CIO_CRX__COLOR, crtcindex2, data);
+	}
+
+	bios_idxprt_wr(bios, NV_CIO_CRX__COLOR, crtcindex1, oldaddr);
+
+	return true;
+}
+
+static bool
+init_cr(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_CR   opcode: 0x52 ('R')
+	 *
+	 * offset      (8  bit): opcode
+	 * offset + 1  (8  bit): CRTC index
+	 * offset + 2  (8  bit): mask
+	 * offset + 3  (8  bit): data
+	 *
+	 * Assign the value of at "CRTC index" ANDed with mask and ORed with
+	 * data back to "CRTC index"
+	 */
+
+	uint8_t crtcindex = bios->data[offset + 1];
+	uint8_t mask = bios->data[offset + 2];
+	uint8_t data = bios->data[offset + 3];
+	uint8_t value;
+
+	if (!iexec->execute)
+		return true;
+
+	BIOSLOG(bios, "0x%04X: Index: 0x%02X, Mask: 0x%02X, Data: 0x%02X\n",
+		offset, crtcindex, mask, data);
+
+	value  = bios_idxprt_rd(bios, NV_CIO_CRX__COLOR, crtcindex) & mask;
+	value |= data;
+	bios_idxprt_wr(bios, NV_CIO_CRX__COLOR, crtcindex, value);
+
+	return true;
+}
+
+static bool
+init_zm_cr(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_ZM_CR   opcode: 0x53 ('S')
+	 *
+	 * offset      (8 bit): opcode
+	 * offset + 1  (8 bit): CRTC index
+	 * offset + 2  (8 bit): value
+	 *
+	 * Assign "value" to CRTC register with index "CRTC index".
+	 */
+
+	uint8_t crtcindex = ROM32(bios->data[offset + 1]);
+	uint8_t data = bios->data[offset + 2];
+
+	if (!iexec->execute)
+		return true;
+
+	bios_idxprt_wr(bios, NV_CIO_CRX__COLOR, crtcindex, data);
+
+	return true;
+}
+
+static bool
+init_zm_cr_group(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_ZM_CR_GROUP   opcode: 0x54 ('T')
+	 *
+	 * offset      (8 bit): opcode
+	 * offset + 1  (8 bit): count
+	 * offset + 2  (8 bit): CRTC index 1
+	 * offset + 3  (8 bit): value 1
+	 * ...
+	 *
+	 * For "count", assign "value n" to CRTC register with index
+	 * "CRTC index n".
+	 */
+
+	uint8_t count = bios->data[offset + 1];
+	int i;
+
+	if (!iexec->execute)
+		return true;
+
+	for (i = 0; i < count; i++)
+		init_zm_cr(bios, offset + 2 + 2 * i - 1, iexec);
+
+	return true;
+}
+
+static bool
+init_condition_time(struct nvbios *bios, uint16_t offset,
+		    struct init_exec *iexec)
+{
+	/*
+	 * INIT_CONDITION_TIME   opcode: 0x56 ('V')
+	 *
+	 * offset      (8 bit): opcode
+	 * offset + 1  (8 bit): condition number
+	 * offset + 2  (8 bit): retries / 50
+	 *
+	 * Check condition "condition number" in the condition table.
+	 * Bios code then sleeps for 2ms if the condition is not met, and
+	 * repeats up to "retries" times, but on one C51 this has proved
+	 * insufficient.  In mmiotraces the driver sleeps for 20ms, so we do
+	 * this, and bail after "retries" times, or 2s, whichever is less.
+	 * If still not met after retries, clear execution flag for this table.
+	 */
+
+	uint8_t cond = bios->data[offset + 1];
+	uint16_t retries = bios->data[offset + 2] * 50;
+	unsigned cnt;
+
+	if (!iexec->execute)
+		return true;
+
+	if (retries > 100)
+		retries = 100;
+
+	BIOSLOG(bios, "0x%04X: Condition: 0x%02X, Retries: 0x%02X\n",
+		offset, cond, retries);
+
+	if (!bios->execute) /* avoid 2s delays when "faking" execution */
+		retries = 1;
+
+	for (cnt = 0; cnt < retries; cnt++) {
+		if (bios_condition_met(bios, offset, cond)) {
+			BIOSLOG(bios, "0x%04X: Condition met, continuing\n",
+								offset);
+			break;
+		} else {
+			BIOSLOG(bios, "0x%04X: "
+				"Condition not met, sleeping for 20ms\n",
+								offset);
+			msleep(20);
+		}
+	}
+
+	if (!bios_condition_met(bios, offset, cond)) {
+		NV_WARN(bios->dev,
+			"0x%04X: Condition still not met after %dms, "
+			"skipping following opcodes\n", offset, 20 * retries);
+		iexec->execute = false;
+	}
+
+	return true;
+}
+
+static bool
+init_zm_reg_sequence(struct nvbios *bios, uint16_t offset,
+		     struct init_exec *iexec)
+{
+	/*
+	 * INIT_ZM_REG_SEQUENCE   opcode: 0x58 ('X')
+	 *
+	 * offset      (8  bit): opcode
+	 * offset + 1  (32 bit): base register
+	 * offset + 5  (8  bit): count
+	 * offset + 6  (32 bit): value 1
+	 * ...
+	 *
+	 * Starting at offset + 6 there are "count" 32 bit values.
+	 * For "count" iterations set "base register" + 4 * current_iteration
+	 * to "value current_iteration"
+	 */
+
+	uint32_t basereg = ROM32(bios->data[offset + 1]);
+	uint32_t count = bios->data[offset + 5];
+	int i;
+
+	if (!iexec->execute)
+		return true;
+
+	BIOSLOG(bios, "0x%04X: BaseReg: 0x%08X, Count: 0x%02X\n",
+		offset, basereg, count);
+
+	for (i = 0; i < count; i++) {
+		uint32_t reg = basereg + i * 4;
+		uint32_t data = ROM32(bios->data[offset + 6 + i * 4]);
+
+		bios_wr32(bios, reg, data);
+	}
+
+	return true;
+}
+
+static bool
+init_sub_direct(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_SUB_DIRECT   opcode: 0x5B ('[')
+	 *
+	 * offset      (8  bit): opcode
+	 * offset + 1  (16 bit): subroutine offset (in bios)
+	 *
+	 * Calls a subroutine that will execute commands until INIT_DONE
+	 * is found.
+	 */
+
+	uint16_t sub_offset = ROM16(bios->data[offset + 1]);
+
+	if (!iexec->execute)
+		return true;
+
+	BIOSLOG(bios, "0x%04X: Executing subroutine at 0x%04X\n",
+		offset, sub_offset);
+
+	parse_init_table(bios, sub_offset, iexec);
+
+	BIOSLOG(bios, "0x%04X: End of 0x%04X subroutine\n", offset, sub_offset);
+
+	return true;
+}
+
+static bool
+init_copy_nv_reg(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_COPY_NV_REG   opcode: 0x5F ('_')
+	 *
+	 * offset      (8  bit): opcode
+	 * offset + 1  (32 bit): src reg
+	 * offset + 5  (8  bit): shift
+	 * offset + 6  (32 bit): src mask
+	 * offset + 10 (32 bit): xor
+	 * offset + 14 (32 bit): dst reg
+	 * offset + 18 (32 bit): dst mask
+	 *
+	 * Shift REGVAL("src reg") right by (signed) "shift", AND result with
+	 * "src mask", then XOR with "xor". Write this OR'd with
+	 * (REGVAL("dst reg") AND'd with "dst mask") to "dst reg"
+	 */
+
+	uint32_t srcreg = *((uint32_t *)(&bios->data[offset + 1]));
+	uint8_t shift = bios->data[offset + 5];
+	uint32_t srcmask = *((uint32_t *)(&bios->data[offset + 6]));
+	uint32_t xor = *((uint32_t *)(&bios->data[offset + 10]));
+	uint32_t dstreg = *((uint32_t *)(&bios->data[offset + 14]));
+	uint32_t dstmask = *((uint32_t *)(&bios->data[offset + 18]));
+	uint32_t srcvalue, dstvalue;
+
+	if (!iexec->execute)
+		return true;
+
+	BIOSLOG(bios, "0x%04X: SrcReg: 0x%08X, Shift: 0x%02X, SrcMask: 0x%08X, "
+		      "Xor: 0x%08X, DstReg: 0x%08X, DstMask: 0x%08X\n",
+		offset, srcreg, shift, srcmask, xor, dstreg, dstmask);
+
+	srcvalue = bios_rd32(bios, srcreg);
+
+	if (shift < 0x80)
+		srcvalue >>= shift;
+	else
+		srcvalue <<= (0x100 - shift);
+
+	srcvalue = (srcvalue & srcmask) ^ xor;
+
+	dstvalue = bios_rd32(bios, dstreg) & dstmask;
+
+	bios_wr32(bios, dstreg, dstvalue | srcvalue);
+
+	return true;
+}
+
+static bool
+init_zm_index_io(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_ZM_INDEX_IO   opcode: 0x62 ('b')
+	 *
+	 * offset      (8  bit): opcode
+	 * offset + 1  (16 bit): CRTC port
+	 * offset + 3  (8  bit): CRTC index
+	 * offset + 4  (8  bit): data
+	 *
+	 * Write "data" to index "CRTC index" of "CRTC port"
+	 */
+	uint16_t crtcport = ROM16(bios->data[offset + 1]);
+	uint8_t crtcindex = bios->data[offset + 3];
+	uint8_t data = bios->data[offset + 4];
+
+	if (!iexec->execute)
+		return true;
+
+	bios_idxprt_wr(bios, crtcport, crtcindex, data);
+
+	return true;
+}
+
+static bool
+init_compute_mem(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_COMPUTE_MEM   opcode: 0x63 ('c')
+	 *
+	 * offset      (8 bit): opcode
+	 *
+	 * This opcode is meant to set NV_PFB_CFG0 (0x100200) appropriately so
+	 * that the hardware can correctly calculate how much VRAM it has
+	 * (and subsequently report that value in NV_PFB_CSTATUS (0x10020C))
+	 *
+	 * The implementation of this opcode in general consists of two parts:
+	 * 1) determination of the memory bus width
+	 * 2) determination of how many of the card's RAM pads have ICs attached
+	 *
+	 * 1) is done by a cunning combination of writes to offsets 0x1c and
+	 * 0x3c in the framebuffer, and seeing whether the written values are
+	 * read back correctly. This then affects bits 4-7 of NV_PFB_CFG0
+	 *
+	 * 2) is done by a cunning combination of writes to an offset slightly
+	 * less than the maximum memory reported by NV_PFB_CSTATUS, then seeing
+	 * if the test pattern can be read back. This then affects bits 12-15 of
+	 * NV_PFB_CFG0
+	 *
+	 * In this context a "cunning combination" may include multiple reads
+	 * and writes to varying locations, often alternating the test pattern
+	 * and 0, doubtless to make sure buffers are filled, residual charges
+	 * on tracks are removed etc.
+	 *
+	 * Unfortunately, the "cunning combination"s mentioned above, and the
+	 * changes to the bits in NV_PFB_CFG0 differ with nearly every bios
+	 * trace I have.
+	 *
+	 * Therefore, we cheat and assume the value of NV_PFB_CFG0 with which
+	 * we started was correct, and use that instead
+	 */
+
+	/* no iexec->execute check by design */
+
+	/*
+	 * This appears to be a NOP on G8x chipsets, both io logs of the VBIOS
+	 * and kmmio traces of the binary driver POSTing the card show nothing
+	 * being done for this opcode.  why is it still listed in the table?!
+	 */
+
+	struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
+
+	if (dev_priv->card_type >= NV_50)
+		return true;
+
+	/*
+	 * On every card I've seen, this step gets done for us earlier in
+	 * the init scripts
+	uint8_t crdata = bios_idxprt_rd(dev, NV_VIO_SRX, 0x01);
+	bios_idxprt_wr(dev, NV_VIO_SRX, 0x01, crdata | 0x20);
+	 */
+
+	/*
+	 * This also has probably been done in the scripts, but an mmio trace of
+	 * s3 resume shows nvidia doing it anyway (unlike the NV_VIO_SRX write)
+	 */
+	bios_wr32(bios, NV_PFB_REFCTRL, NV_PFB_REFCTRL_VALID_1);
+
+	/* write back the saved configuration value */
+	bios_wr32(bios, NV_PFB_CFG0, bios->state.saved_nv_pfb_cfg0);
+
+	return true;
+}
+
+static bool
+init_reset(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_RESET   opcode: 0x65 ('e')
+	 *
+	 * offset      (8  bit): opcode
+	 * offset + 1  (32 bit): register
+	 * offset + 5  (32 bit): value1
+	 * offset + 9  (32 bit): value2
+	 *
+	 * Assign "value1" to "register", then assign "value2" to "register"
+	 */
+
+	uint32_t reg = ROM32(bios->data[offset + 1]);
+	uint32_t value1 = ROM32(bios->data[offset + 5]);
+	uint32_t value2 = ROM32(bios->data[offset + 9]);
+	uint32_t pci_nv_19, pci_nv_20;
+
+	/* no iexec->execute check by design */
+
+	pci_nv_19 = bios_rd32(bios, NV_PBUS_PCI_NV_19);
+	bios_wr32(bios, NV_PBUS_PCI_NV_19, 0);
+	bios_wr32(bios, reg, value1);
+
+	udelay(10);
+
+	bios_wr32(bios, reg, value2);
+	bios_wr32(bios, NV_PBUS_PCI_NV_19, pci_nv_19);
+
+	pci_nv_20 = bios_rd32(bios, NV_PBUS_PCI_NV_20);
+	pci_nv_20 &= ~NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED;	/* 0xfffffffe */
+	bios_wr32(bios, NV_PBUS_PCI_NV_20, pci_nv_20);
+
+	return true;
+}
+
+static bool
+init_configure_mem(struct nvbios *bios, uint16_t offset,
+		   struct init_exec *iexec)
+{
+	/*
+	 * INIT_CONFIGURE_MEM   opcode: 0x66 ('f')
+	 *
+	 * offset      (8 bit): opcode
+	 *
+	 * Equivalent to INIT_DONE on bios version 3 or greater.
+	 * For early bios versions, sets up the memory registers, using values
+	 * taken from the memory init table
+	 */
+
+	/* no iexec->execute check by design */
+
+	uint16_t meminitoffs = bios->legacy.mem_init_tbl_ptr + MEM_INIT_SIZE * (bios_idxprt_rd(bios, NV_CIO_CRX__COLOR, NV_CIO_CRE_SCRATCH4__INDEX) >> 4);
+	uint16_t seqtbloffs = bios->legacy.sdr_seq_tbl_ptr, meminitdata = meminitoffs + 6;
+	uint32_t reg, data;
+
+	if (bios->major_version > 2)
+		return false;
+
+	bios_idxprt_wr(bios, NV_VIO_SRX, NV_VIO_SR_CLOCK_INDEX, bios_idxprt_rd(
+		       bios, NV_VIO_SRX, NV_VIO_SR_CLOCK_INDEX) | 0x20);
+
+	if (bios->data[meminitoffs] & 1)
+		seqtbloffs = bios->legacy.ddr_seq_tbl_ptr;
+
+	for (reg = ROM32(bios->data[seqtbloffs]);
+	     reg != 0xffffffff;
+	     reg = ROM32(bios->data[seqtbloffs += 4])) {
+
+		switch (reg) {
+		case NV_PFB_PRE:
+			data = NV_PFB_PRE_CMD_PRECHARGE;
+			break;
+		case NV_PFB_PAD:
+			data = NV_PFB_PAD_CKE_NORMAL;
+			break;
+		case NV_PFB_REF:
+			data = NV_PFB_REF_CMD_REFRESH;
+			break;
+		default:
+			data = ROM32(bios->data[meminitdata]);
+			meminitdata += 4;
+			if (data == 0xffffffff)
+				continue;
+		}
+
+		bios_wr32(bios, reg, data);
+	}
+
+	return true;
+}
+
+static bool
+init_configure_clk(struct nvbios *bios, uint16_t offset,
+		   struct init_exec *iexec)
+{
+	/*
+	 * INIT_CONFIGURE_CLK   opcode: 0x67 ('g')
+	 *
+	 * offset      (8 bit): opcode
+	 *
+	 * Equivalent to INIT_DONE on bios version 3 or greater.
+	 * For early bios versions, sets up the NVClk and MClk PLLs, using
+	 * values taken from the memory init table
+	 */
+
+	/* no iexec->execute check by design */
+
+	uint16_t meminitoffs = bios->legacy.mem_init_tbl_ptr + MEM_INIT_SIZE * (bios_idxprt_rd(bios, NV_CIO_CRX__COLOR, NV_CIO_CRE_SCRATCH4__INDEX) >> 4);
+	int clock;
+
+	if (bios->major_version > 2)
+		return false;
+
+	clock = ROM16(bios->data[meminitoffs + 4]) * 10;
+	setPLL(bios, NV_PRAMDAC_NVPLL_COEFF, clock);
+
+	clock = ROM16(bios->data[meminitoffs + 2]) * 10;
+	if (bios->data[meminitoffs] & 1) /* DDR */
+		clock *= 2;
+	setPLL(bios, NV_PRAMDAC_MPLL_COEFF, clock);
+
+	return true;
+}
+
+static bool
+init_configure_preinit(struct nvbios *bios, uint16_t offset,
+		       struct init_exec *iexec)
+{
+	/*
+	 * INIT_CONFIGURE_PREINIT   opcode: 0x68 ('h')
+	 *
+	 * offset      (8 bit): opcode
+	 *
+	 * Equivalent to INIT_DONE on bios version 3 or greater.
+	 * For early bios versions, does early init, loading ram and crystal
+	 * configuration from straps into CR3C
+	 */
+
+	/* no iexec->execute check by design */
+
+	uint32_t straps = bios_rd32(bios, NV_PEXTDEV_BOOT_0);
+	uint8_t cr3c = ((straps << 2) & 0xf0) | (straps & (1 << 6));
+
+	if (bios->major_version > 2)
+		return false;
+
+	bios_idxprt_wr(bios, NV_CIO_CRX__COLOR,
+			     NV_CIO_CRE_SCRATCH4__INDEX, cr3c);
+
+	return true;
+}
+
+static bool
+init_io(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_IO   opcode: 0x69 ('i')
+	 *
+	 * offset      (8  bit): opcode
+	 * offset + 1  (16 bit): CRTC port
+	 * offset + 3  (8  bit): mask
+	 * offset + 4  (8  bit): data
+	 *
+	 * Assign ((IOVAL("crtc port") & "mask") | "data") to "crtc port"
+	 */
+
+	struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
+	uint16_t crtcport = ROM16(bios->data[offset + 1]);
+	uint8_t mask = bios->data[offset + 3];
+	uint8_t data = bios->data[offset + 4];
+
+	if (!iexec->execute)
+		return true;
+
+	BIOSLOG(bios, "0x%04X: Port: 0x%04X, Mask: 0x%02X, Data: 0x%02X\n",
+		offset, crtcport, mask, data);
+
+	/*
+	 * I have no idea what this does, but NVIDIA do this magic sequence
+	 * in the places where this INIT_IO happens..
+	 */
+	if (dev_priv->card_type >= NV_50 && crtcport == 0x3c3 && data == 1) {
+		int i;
+
+		bios_wr32(bios, 0x614100, (bios_rd32(
+			  bios, 0x614100) & 0x0fffffff) | 0x00800000);
+
+		bios_wr32(bios, 0x00e18c, bios_rd32(
+			  bios, 0x00e18c) | 0x00020000);
+
+		bios_wr32(bios, 0x614900, (bios_rd32(
+			  bios, 0x614900) & 0x0fffffff) | 0x00800000);
+
+		bios_wr32(bios, 0x000200, bios_rd32(
+			  bios, 0x000200) & ~0x40000000);
+
+		mdelay(10);
+
+		bios_wr32(bios, 0x00e18c, bios_rd32(
+			  bios, 0x00e18c) & ~0x00020000);
+
+		bios_wr32(bios, 0x000200, bios_rd32(
+			  bios, 0x000200) | 0x40000000);
+
+		bios_wr32(bios, 0x614100, 0x00800018);
+		bios_wr32(bios, 0x614900, 0x00800018);
+
+		mdelay(10);
+
+		bios_wr32(bios, 0x614100, 0x10000018);
+		bios_wr32(bios, 0x614900, 0x10000018);
+
+		for (i = 0; i < 3; i++)
+			bios_wr32(bios, 0x614280 + (i*0x800), bios_rd32(
+				  bios, 0x614280 + (i*0x800)) & 0xf0f0f0f0);
+
+		for (i = 0; i < 2; i++)
+			bios_wr32(bios, 0x614300 + (i*0x800), bios_rd32(
+				  bios, 0x614300 + (i*0x800)) & 0xfffff0f0);
+
+		for (i = 0; i < 3; i++)
+			bios_wr32(bios, 0x614380 + (i*0x800), bios_rd32(
+				  bios, 0x614380 + (i*0x800)) & 0xfffff0f0);
+
+		for (i = 0; i < 2; i++)
+			bios_wr32(bios, 0x614200 + (i*0x800), bios_rd32(
+				  bios, 0x614200 + (i*0x800)) & 0xfffffff0);
+
+		for (i = 0; i < 2; i++)
+			bios_wr32(bios, 0x614108 + (i*0x800), bios_rd32(
+				  bios, 0x614108 + (i*0x800)) & 0x0fffffff);
+		return true;
+	}
+
+	bios_port_wr(bios, crtcport, (bios_port_rd(bios, crtcport) & mask) |
+									data);
+	return true;
+}
+
+static bool
+init_sub(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_SUB   opcode: 0x6B ('k')
+	 *
+	 * offset      (8 bit): opcode
+	 * offset + 1  (8 bit): script number
+	 *
+	 * Execute script number "script number", as a subroutine
+	 */
+
+	uint8_t sub = bios->data[offset + 1];
+
+	if (!iexec->execute)
+		return true;
+
+	BIOSLOG(bios, "0x%04X: Calling script %d\n", offset, sub);
+
+	parse_init_table(bios,
+			 ROM16(bios->data[bios->init_script_tbls_ptr + sub * 2]),
+			 iexec);
+
+	BIOSLOG(bios, "0x%04X: End of script %d\n", offset, sub);
+
+	return true;
+}
+
+static bool
+init_ram_condition(struct nvbios *bios, uint16_t offset,
+		   struct init_exec *iexec)
+{
+	/*
+	 * INIT_RAM_CONDITION   opcode: 0x6D ('m')
+	 *
+	 * offset      (8 bit): opcode
+	 * offset + 1  (8 bit): mask
+	 * offset + 2  (8 bit): cmpval
+	 *
+	 * Test if (NV_PFB_BOOT_0 & "mask") equals "cmpval".
+	 * If condition not met skip subsequent opcodes until condition is
+	 * inverted (INIT_NOT), or we hit INIT_RESUME
+	 */
+
+	uint8_t mask = bios->data[offset + 1];
+	uint8_t cmpval = bios->data[offset + 2];
+	uint8_t data;
+
+	if (!iexec->execute)
+		return true;
+
+	data = bios_rd32(bios, NV_PFB_BOOT_0) & mask;
+
+	BIOSLOG(bios, "0x%04X: Checking if 0x%08X equals 0x%08X\n",
+		offset, data, cmpval);
+
+	if (data == cmpval)
+		BIOSLOG(bios, "0x%04X: Condition fulfilled -- continuing to execute\n", offset);
+	else {
+		BIOSLOG(bios, "0x%04X: Condition not fulfilled -- skipping following commands\n", offset);
+		iexec->execute = false;
+	}
+
+	return true;
+}
+
+static bool
+init_nv_reg(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_NV_REG   opcode: 0x6E ('n')
+	 *
+	 * offset      (8  bit): opcode
+	 * offset + 1  (32 bit): register
+	 * offset + 5  (32 bit): mask
+	 * offset + 9  (32 bit): data
+	 *
+	 * Assign ((REGVAL("register") & "mask") | "data") to "register"
+	 */
+
+	uint32_t reg = ROM32(bios->data[offset + 1]);
+	uint32_t mask = ROM32(bios->data[offset + 5]);
+	uint32_t data = ROM32(bios->data[offset + 9]);
+
+	if (!iexec->execute)
+		return true;
+
+	BIOSLOG(bios, "0x%04X: Reg: 0x%08X, Mask: 0x%08X, Data: 0x%08X\n",
+		offset, reg, mask, data);
+
+	bios_wr32(bios, reg, (bios_rd32(bios, reg) & mask) | data);
+
+	return true;
+}
+
+static bool
+init_macro(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_MACRO   opcode: 0x6F ('o')
+	 *
+	 * offset      (8 bit): opcode
+	 * offset + 1  (8 bit): macro number
+	 *
+	 * Look up macro index "macro number" in the macro index table.
+	 * The macro index table entry has 1 byte for the index in the macro
+	 * table, and 1 byte for the number of times to repeat the macro.
+	 * The macro table entry has 4 bytes for the register address and
+	 * 4 bytes for the value to write to that register
+	 */
+
+	uint8_t macro_index_tbl_idx = bios->data[offset + 1];
+	uint16_t tmp = bios->macro_index_tbl_ptr + (macro_index_tbl_idx * MACRO_INDEX_SIZE);
+	uint8_t macro_tbl_idx = bios->data[tmp];
+	uint8_t count = bios->data[tmp + 1];
+	uint32_t reg, data;
+	int i;
+
+	if (!iexec->execute)
+		return true;
+
+	BIOSLOG(bios, "0x%04X: Macro: 0x%02X, MacroTableIndex: 0x%02X, "
+		      "Count: 0x%02X\n",
+		offset, macro_index_tbl_idx, macro_tbl_idx, count);
+
+	for (i = 0; i < count; i++) {
+		uint16_t macroentryptr = bios->macro_tbl_ptr + (macro_tbl_idx + i) * MACRO_SIZE;
+
+		reg = ROM32(bios->data[macroentryptr]);
+		data = ROM32(bios->data[macroentryptr + 4]);
+
+		bios_wr32(bios, reg, data);
+	}
+
+	return true;
+}
+
+static bool
+init_done(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_DONE   opcode: 0x71 ('q')
+	 *
+	 * offset      (8  bit): opcode
+	 *
+	 * End the current script
+	 */
+
+	/* mild retval abuse to stop parsing this table */
+	return false;
+}
+
+static bool
+init_resume(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_RESUME   opcode: 0x72 ('r')
+	 *
+	 * offset      (8  bit): opcode
+	 *
+	 * End the current execute / no-execute condition
+	 */
+
+	if (iexec->execute)
+		return true;
+
+	iexec->execute = true;
+	BIOSLOG(bios, "0x%04X: ---- Executing following commands ----\n", offset);
+
+	return true;
+}
+
+static bool
+init_time(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_TIME   opcode: 0x74 ('t')
+	 *
+	 * offset      (8  bit): opcode
+	 * offset + 1  (16 bit): time
+	 *
+	 * Sleep for "time" microseconds.
+	 */
+
+	unsigned time = ROM16(bios->data[offset + 1]);
+
+	if (!iexec->execute)
+		return true;
+
+	BIOSLOG(bios, "0x%04X: Sleeping for 0x%04X microseconds\n",
+		offset, time);
+
+	if (time < 1000)
+		udelay(time);
+	else
+		msleep((time + 900) / 1000);
+
+	return true;
+}
+
+static bool
+init_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_CONDITION   opcode: 0x75 ('u')
+	 *
+	 * offset      (8 bit): opcode
+	 * offset + 1  (8 bit): condition number
+	 *
+	 * Check condition "condition number" in the condition table.
+	 * If condition not met skip subsequent opcodes until condition is
+	 * inverted (INIT_NOT), or we hit INIT_RESUME
+	 */
+
+	uint8_t cond = bios->data[offset + 1];
+
+	if (!iexec->execute)
+		return true;
+
+	BIOSLOG(bios, "0x%04X: Condition: 0x%02X\n", offset, cond);
+
+	if (bios_condition_met(bios, offset, cond))
+		BIOSLOG(bios, "0x%04X: Condition fulfilled -- continuing to execute\n", offset);
+	else {
+		BIOSLOG(bios, "0x%04X: Condition not fulfilled -- skipping following commands\n", offset);
+		iexec->execute = false;
+	}
+
+	return true;
+}
+
+static bool
+init_io_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_IO_CONDITION  opcode: 0x76
+	 *
+	 * offset      (8 bit): opcode
+	 * offset + 1  (8 bit): condition number
+	 *
+	 * Check condition "condition number" in the io condition table.
+	 * If condition not met skip subsequent opcodes until condition is
+	 * inverted (INIT_NOT), or we hit INIT_RESUME
+	 */
+
+	uint8_t cond = bios->data[offset + 1];
+
+	if (!iexec->execute)
+		return true;
+
+	BIOSLOG(bios, "0x%04X: IO condition: 0x%02X\n", offset, cond);
+
+	if (io_condition_met(bios, offset, cond))
+		BIOSLOG(bios, "0x%04X: Condition fulfilled -- continuing to execute\n", offset);
+	else {
+		BIOSLOG(bios, "0x%04X: Condition not fulfilled -- skipping following commands\n", offset);
+		iexec->execute = false;
+	}
+
+	return true;
+}
+
+static bool
+init_index_io(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_INDEX_IO   opcode: 0x78 ('x')
+	 *
+	 * offset      (8  bit): opcode
+	 * offset + 1  (16 bit): CRTC port
+	 * offset + 3  (8  bit): CRTC index
+	 * offset + 4  (8  bit): mask
+	 * offset + 5  (8  bit): data
+	 *
+	 * Read value at index "CRTC index" on "CRTC port", AND with "mask",
+	 * OR with "data", write-back
+	 */
+
+	uint16_t crtcport = ROM16(bios->data[offset + 1]);
+	uint8_t crtcindex = bios->data[offset + 3];
+	uint8_t mask = bios->data[offset + 4];
+	uint8_t data = bios->data[offset + 5];
+	uint8_t value;
+
+	if (!iexec->execute)
+		return true;
+
+	BIOSLOG(bios, "0x%04X: Port: 0x%04X, Index: 0x%02X, Mask: 0x%02X, "
+		      "Data: 0x%02X\n",
+		offset, crtcport, crtcindex, mask, data);
+
+	value = (bios_idxprt_rd(bios, crtcport, crtcindex) & mask) | data;
+	bios_idxprt_wr(bios, crtcport, crtcindex, value);
+
+	return true;
+}
+
+static bool
+init_pll(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_PLL   opcode: 0x79 ('y')
+	 *
+	 * offset      (8  bit): opcode
+	 * offset + 1  (32 bit): register
+	 * offset + 5  (16 bit): freq
+	 *
+	 * Set PLL register "register" to coefficients for frequency (10kHz)
+	 * "freq"
+	 */
+
+	uint32_t reg = ROM32(bios->data[offset + 1]);
+	uint16_t freq = ROM16(bios->data[offset + 5]);
+
+	if (!iexec->execute)
+		return true;
+
+	BIOSLOG(bios, "0x%04X: Reg: 0x%08X, Freq: %d0kHz\n", offset, reg, freq);
+
+	setPLL(bios, reg, freq * 10);
+
+	return true;
+}
+
+static bool
+init_zm_reg(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_ZM_REG   opcode: 0x7A ('z')
+	 *
+	 * offset      (8  bit): opcode
+	 * offset + 1  (32 bit): register
+	 * offset + 5  (32 bit): value
+	 *
+	 * Assign "value" to "register"
+	 */
+
+	uint32_t reg = ROM32(bios->data[offset + 1]);
+	uint32_t value = ROM32(bios->data[offset + 5]);
+
+	if (!iexec->execute)
+		return true;
+
+	if (reg == 0x000200)
+		value |= 1;
+
+	bios_wr32(bios, reg, value);
+
+	return true;
+}
+
+static bool
+init_ram_restrict_pll(struct nvbios *bios, uint16_t offset,
+		      struct init_exec *iexec)
+{
+	/*
+	 * INIT_RAM_RESTRICT_PLL   opcode: 0x87 ('')
+	 *
+	 * offset      (8 bit): opcode
+	 * offset + 1  (8 bit): PLL type
+	 * offset + 2 (32 bit): frequency 0
+	 *
+	 * Uses the RAMCFG strap of PEXTDEV_BOOT as an index into the table at
+	 * ram_restrict_table_ptr.  The value read from there is used to select
+	 * a frequency from the table starting at 'frequency 0' to be
+	 * programmed into the PLL corresponding to 'type'.
+	 *
+	 * The PLL limits table on cards using this opcode has a mapping of
+	 * 'type' to the relevant registers.
+	 */
+
+	struct drm_device *dev = bios->dev;
+	uint32_t strap = (bios_rd32(bios, NV_PEXTDEV_BOOT_0) & 0x0000003c) >> 2;
+	uint8_t index = bios->data[bios->ram_restrict_tbl_ptr + strap];
+	uint8_t type = bios->data[offset + 1];
+	uint32_t freq = ROM32(bios->data[offset + 2 + (index * 4)]);
+	uint8_t *pll_limits = &bios->data[bios->pll_limit_tbl_ptr], *entry;
+	int i;
+
+	if (!iexec->execute)
+		return true;
+
+	if (!bios->pll_limit_tbl_ptr || (pll_limits[0] & 0xf0) != 0x30) {
+		NV_ERROR(dev, "PLL limits table not version 3.x\n");
+		return true; /* deliberate, allow default clocks to remain */
+	}
+
+	entry = pll_limits + pll_limits[1];
+	for (i = 0; i < pll_limits[3]; i++, entry += pll_limits[2]) {
+		if (entry[0] == type) {
+			uint32_t reg = ROM32(entry[3]);
+
+			BIOSLOG(bios, "0x%04X: "
+				      "Type %02x Reg 0x%08x Freq %dKHz\n",
+				offset, type, reg, freq);
+
+			setPLL(bios, reg, freq);
+			return true;
+		}
+	}
+
+	NV_ERROR(dev, "PLL type 0x%02x not found in PLL limits table", type);
+	return true;
+}
+
+static bool
+init_8c(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_8C   opcode: 0x8C ('')
+	 *
+	 * NOP so far....
+	 *
+	 */
+
+	return true;
+}
+
+static bool
+init_8d(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_8D   opcode: 0x8D ('')
+	 *
+	 * NOP so far....
+	 *
+	 */
+
+	return true;
+}
+
+static bool
+init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_GPIO   opcode: 0x8E ('')
+	 *
+	 * offset      (8 bit): opcode
+	 *
+	 * Loop over all entries in the DCB GPIO table, and initialise
+	 * each GPIO according to various values listed in each entry
+	 */
+
+	const uint32_t nv50_gpio_reg[4] = { 0xe104, 0xe108, 0xe280, 0xe284 };
+	const uint32_t nv50_gpio_ctl[2] = { 0xe100, 0xe28c };
+	const uint8_t *gpio_table = &bios->data[bios->bdcb.gpio_table_ptr];
+	const uint8_t *gpio_entry;
+	int i;
+
+	if (bios->bdcb.version != 0x40) {
+		NV_ERROR(bios->dev, "DCB table not version 4.0\n");
+		return false;
+	}
+
+	if (!bios->bdcb.gpio_table_ptr) {
+		NV_WARN(bios->dev, "Invalid pointer to INIT_8E table\n");
+		return false;
+	}
+
+	gpio_entry = gpio_table + gpio_table[1];
+	for (i = 0; i < gpio_table[2]; i++, gpio_entry += gpio_table[3]) {
+		uint32_t entry = ROM32(gpio_entry[0]), r, s, v;
+		int line = (entry & 0x0000001f);
+
+		BIOSLOG(bios, "0x%04X: Entry: 0x%08X\n", offset, entry);
+
+		if ((entry & 0x0000ff00) == 0x0000ff00)
+			continue;
+
+		r = nv50_gpio_reg[line >> 3];
+		s = (line & 0x07) << 2;
+		v = bios_rd32(bios, r) & ~(0x00000003 << s);
+		if (entry & 0x01000000)
+			v |= (((entry & 0x60000000) >> 29) ^ 2) << s;
+		else
+			v |= (((entry & 0x18000000) >> 27) ^ 2) << s;
+		bios_wr32(bios, r, v);
+
+		r = nv50_gpio_ctl[line >> 4];
+		s = (line & 0x0f);
+		v = bios_rd32(bios, r) & ~(0x00010001 << s);
+		switch ((entry & 0x06000000) >> 25) {
+		case 1:
+			v |= (0x00000001 << s);
+			break;
+		case 2:
+			v |= (0x00010000 << s);
+			break;
+		default:
+			break;
+		}
+		bios_wr32(bios, r, v);
+	}
+
+	return true;
+}
+
+/* hack to avoid moving the itbl_entry array before this function */
+int init_ram_restrict_zm_reg_group_blocklen;
+
+static bool
+init_ram_restrict_zm_reg_group(struct nvbios *bios, uint16_t offset,
+			       struct init_exec *iexec)
+{
+	/*
+	 * INIT_RAM_RESTRICT_ZM_REG_GROUP   opcode: 0x8F ('')
+	 *
+	 * offset      (8  bit): opcode
+	 * offset + 1  (32 bit): reg
+	 * offset + 5  (8  bit): regincrement
+	 * offset + 6  (8  bit): count
+	 * offset + 7  (32 bit): value 1,1
+	 * ...
+	 *
+	 * Use the RAMCFG strap of PEXTDEV_BOOT as an index into the table at
+	 * ram_restrict_table_ptr. The value read from here is 'n', and
+	 * "value 1,n" gets written to "reg". This repeats "count" times and on
+	 * each iteration 'm', "reg" increases by "regincrement" and
+	 * "value m,n" is used. The extent of n is limited by a number read
+	 * from the 'M' BIT table, herein called "blocklen"
+	 */
+
+	uint32_t reg = ROM32(bios->data[offset + 1]);
+	uint8_t regincrement = bios->data[offset + 5];
+	uint8_t count = bios->data[offset + 6];
+	uint32_t strap_ramcfg, data;
+	uint16_t blocklen;
+	uint8_t index;
+	int i;
+
+	/* previously set by 'M' BIT table */
+	blocklen = init_ram_restrict_zm_reg_group_blocklen;
+
+	if (!iexec->execute)
+		return true;
+
+	if (!blocklen) {
+		NV_ERROR(bios->dev,
+			 "0x%04X: Zero block length - has the M table "
+			 "been parsed?\n", offset);
+		return false;
+	}
+
+	strap_ramcfg = (bios_rd32(bios, NV_PEXTDEV_BOOT_0) >> 2) & 0xf;
+	index = bios->data[bios->ram_restrict_tbl_ptr + strap_ramcfg];
+
+	BIOSLOG(bios, "0x%04X: Reg: 0x%08X, RegIncrement: 0x%02X, "
+		      "Count: 0x%02X, StrapRamCfg: 0x%02X, Index: 0x%02X\n",
+		offset, reg, regincrement, count, strap_ramcfg, index);
+
+	for (i = 0; i < count; i++) {
+		data = ROM32(bios->data[offset + 7 + index * 4 + blocklen * i]);
+
+		bios_wr32(bios, reg, data);
+
+		reg += regincrement;
+	}
+
+	return true;
+}
+
+static bool
+init_copy_zm_reg(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_COPY_ZM_REG   opcode: 0x90 ('')
+	 *
+	 * offset      (8  bit): opcode
+	 * offset + 1  (32 bit): src reg
+	 * offset + 5  (32 bit): dst reg
+	 *
+	 * Put contents of "src reg" into "dst reg"
+	 */
+
+	uint32_t srcreg = ROM32(bios->data[offset + 1]);
+	uint32_t dstreg = ROM32(bios->data[offset + 5]);
+
+	if (!iexec->execute)
+		return true;
+
+	bios_wr32(bios, dstreg, bios_rd32(bios, srcreg));
+
+	return true;
+}
+
+static bool
+init_zm_reg_group_addr_latched(struct nvbios *bios, uint16_t offset,
+			       struct init_exec *iexec)
+{
+	/*
+	 * INIT_ZM_REG_GROUP_ADDRESS_LATCHED   opcode: 0x91 ('')
+	 *
+	 * offset      (8  bit): opcode
+	 * offset + 1  (32 bit): dst reg
+	 * offset + 5  (8  bit): count
+	 * offset + 6  (32 bit): data 1
+	 * ...
+	 *
+	 * For each of "count" values write "data n" to "dst reg"
+	 */
+
+	uint32_t reg = ROM32(bios->data[offset + 1]);
+	uint8_t count = bios->data[offset + 5];
+	int i;
+
+	if (!iexec->execute)
+		return true;
+
+	for (i = 0; i < count; i++) {
+		uint32_t data = ROM32(bios->data[offset + 6 + 4 * i]);
+		bios_wr32(bios, reg, data);
+	}
+
+	return true;
+}
+
+static bool
+init_reserved(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_RESERVED   opcode: 0x92 ('')
+	 *
+	 * offset      (8 bit): opcode
+	 *
+	 * Seemingly does nothing
+	 */
+
+	return true;
+}
+
+static bool
+init_96(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_96   opcode: 0x96 ('')
+	 *
+	 * offset      (8  bit): opcode
+	 * offset + 1  (32 bit): sreg
+	 * offset + 5  (8  bit): sshift
+	 * offset + 6  (8  bit): smask
+	 * offset + 7  (8  bit): index
+	 * offset + 8  (32 bit): reg
+	 * offset + 12 (32 bit): mask
+	 * offset + 16 (8  bit): shift
+	 *
+	 */
+
+	uint16_t xlatptr = bios->init96_tbl_ptr + (bios->data[offset + 7] * 2);
+	uint32_t reg = ROM32(bios->data[offset + 8]);
+	uint32_t mask = ROM32(bios->data[offset + 12]);
+	uint32_t val;
+
+	val = bios_rd32(bios, ROM32(bios->data[offset + 1]));
+	if (bios->data[offset + 5] < 0x80)
+		val >>= bios->data[offset + 5];
+	else
+		val <<= (0x100 - bios->data[offset + 5]);
+	val &= bios->data[offset + 6];
+
+	val   = bios->data[ROM16(bios->data[xlatptr]) + val];
+	val <<= bios->data[offset + 16];
+
+	if (!iexec->execute)
+		return true;
+
+	bios_wr32(bios, reg, (bios_rd32(bios, reg) & mask) | val);
+	return true;
+}
+
+static bool
+init_97(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_97   opcode: 0x97 ('')
+	 *
+	 * offset      (8  bit): opcode
+	 * offset + 1  (32 bit): register
+	 * offset + 5  (32 bit): mask
+	 * offset + 9  (32 bit): value
+	 *
+	 * Adds "value" to "register" preserving the fields specified
+	 * by "mask"
+	 */
+
+	uint32_t reg = ROM32(bios->data[offset + 1]);
+	uint32_t mask = ROM32(bios->data[offset + 5]);
+	uint32_t add = ROM32(bios->data[offset + 9]);
+	uint32_t val;
+
+	val = bios_rd32(bios, reg);
+	val = (val & mask) | ((val + add) & ~mask);
+
+	if (!iexec->execute)
+		return true;
+
+	bios_wr32(bios, reg, val);
+	return true;
+}
+
+static bool
+init_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_AUXCH   opcode: 0x98 ('')
+	 *
+	 * offset      (8  bit): opcode
+	 * offset + 1  (32 bit): address
+	 * offset + 5  (8  bit): count
+	 * offset + 6  (8  bit): mask 0
+	 * offset + 7  (8  bit): data 0
+	 *  ...
+	 *
+	 */
+
+	struct drm_device *dev = bios->dev;
+	struct nouveau_i2c_chan *auxch;
+	uint32_t addr = ROM32(bios->data[offset + 1]);
+	uint8_t len = bios->data[offset + 5];
+	int ret, i;
+
+	if (!bios->display.output) {
+		NV_ERROR(dev, "INIT_AUXCH: no active output\n");
+		return false;
+	}
+
+	auxch = init_i2c_device_find(dev, bios->display.output->i2c_index);
+	if (!auxch) {
+		NV_ERROR(dev, "INIT_AUXCH: couldn't get auxch %d\n",
+			 bios->display.output->i2c_index);
+		return false;
+	}
+
+	if (!iexec->execute)
+		return true;
+
+	offset += 6;
+	for (i = 0; i < len; i++, offset += 2) {
+		uint8_t data;
+
+		ret = nouveau_dp_auxch(auxch, 9, addr, &data, 1);
+		if (ret) {
+			NV_ERROR(dev, "INIT_AUXCH: rd auxch fail %d\n", ret);
+			return false;
+		}
+
+		data &= bios->data[offset + 0];
+		data |= bios->data[offset + 1];
+
+		ret = nouveau_dp_auxch(auxch, 8, addr, &data, 1);
+		if (ret) {
+			NV_ERROR(dev, "INIT_AUXCH: wr auxch fail %d\n", ret);
+			return false;
+		}
+	}
+
+	return true;
+}
+
+static bool
+init_zm_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+	/*
+	 * INIT_ZM_AUXCH   opcode: 0x99 ('')
+	 *
+	 * offset      (8  bit): opcode
+	 * offset + 1  (32 bit): address
+	 * offset + 5  (8  bit): count
+	 * offset + 6  (8  bit): data 0
+	 *  ...
+	 *
+	 */
+
+	struct drm_device *dev = bios->dev;
+	struct nouveau_i2c_chan *auxch;
+	uint32_t addr = ROM32(bios->data[offset + 1]);
+	uint8_t len = bios->data[offset + 5];
+	int ret, i;
+
+	if (!bios->display.output) {
+		NV_ERROR(dev, "INIT_ZM_AUXCH: no active output\n");
+		return false;
+	}
+
+	auxch = init_i2c_device_find(dev, bios->display.output->i2c_index);
+	if (!auxch) {
+		NV_ERROR(dev, "INIT_ZM_AUXCH: couldn't get auxch %d\n",
+			 bios->display.output->i2c_index);
+		return false;
+	}
+
+	if (!iexec->execute)
+		return true;
+
+	offset += 6;
+	for (i = 0; i < len; i++, offset++) {
+		ret = nouveau_dp_auxch(auxch, 8, addr, &bios->data[offset], 1);
+		if (ret) {
+			NV_ERROR(dev, "INIT_ZM_AUXCH: wr auxch fail %d\n", ret);
+			return false;
+		}
+	}
+
+	return true;
+}
+
+static struct init_tbl_entry itbl_entry[] = {
+	/* command name                       , id  , length  , offset  , mult    , command handler                 */
+	/* INIT_PROG (0x31, 15, 10, 4) removed due to no example of use */
+	{ "INIT_IO_RESTRICT_PROG"             , 0x32, 11      , 6       , 4       , init_io_restrict_prog           },
+	{ "INIT_REPEAT"                       , 0x33, 2       , 0       , 0       , init_repeat                     },
+	{ "INIT_IO_RESTRICT_PLL"              , 0x34, 12      , 7       , 2       , init_io_restrict_pll            },
+	{ "INIT_END_REPEAT"                   , 0x36, 1       , 0       , 0       , init_end_repeat                 },
+	{ "INIT_COPY"                         , 0x37, 11      , 0       , 0       , init_copy                       },
+	{ "INIT_NOT"                          , 0x38, 1       , 0       , 0       , init_not                        },
+	{ "INIT_IO_FLAG_CONDITION"            , 0x39, 2       , 0       , 0       , init_io_flag_condition          },
+	{ "INIT_INDEX_ADDRESS_LATCHED"        , 0x49, 18      , 17      , 2       , init_idx_addr_latched           },
+	{ "INIT_IO_RESTRICT_PLL2"             , 0x4A, 11      , 6       , 4       , init_io_restrict_pll2           },
+	{ "INIT_PLL2"                         , 0x4B, 9       , 0       , 0       , init_pll2                       },
+	{ "INIT_I2C_BYTE"                     , 0x4C, 4       , 3       , 3       , init_i2c_byte                   },
+	{ "INIT_ZM_I2C_BYTE"                  , 0x4D, 4       , 3       , 2       , init_zm_i2c_byte                },
+	{ "INIT_ZM_I2C"                       , 0x4E, 4       , 3       , 1       , init_zm_i2c                     },
+	{ "INIT_TMDS"                         , 0x4F, 5       , 0       , 0       , init_tmds                       },
+	{ "INIT_ZM_TMDS_GROUP"                , 0x50, 3       , 2       , 2       , init_zm_tmds_group              },
+	{ "INIT_CR_INDEX_ADDRESS_LATCHED"     , 0x51, 5       , 4       , 1       , init_cr_idx_adr_latch           },
+	{ "INIT_CR"                           , 0x52, 4       , 0       , 0       , init_cr                         },
+	{ "INIT_ZM_CR"                        , 0x53, 3       , 0       , 0       , init_zm_cr                      },
+	{ "INIT_ZM_CR_GROUP"                  , 0x54, 2       , 1       , 2       , init_zm_cr_group                },
+	{ "INIT_CONDITION_TIME"               , 0x56, 3       , 0       , 0       , init_condition_time             },
+	{ "INIT_ZM_REG_SEQUENCE"              , 0x58, 6       , 5       , 4       , init_zm_reg_sequence            },
+	/* INIT_INDIRECT_REG (0x5A, 7, 0, 0) removed due to no example of use */
+	{ "INIT_SUB_DIRECT"                   , 0x5B, 3       , 0       , 0       , init_sub_direct                 },
+	{ "INIT_COPY_NV_REG"                  , 0x5F, 22      , 0       , 0       , init_copy_nv_reg                },
+	{ "INIT_ZM_INDEX_IO"                  , 0x62, 5       , 0       , 0       , init_zm_index_io                },
+	{ "INIT_COMPUTE_MEM"                  , 0x63, 1       , 0       , 0       , init_compute_mem                },
+	{ "INIT_RESET"                        , 0x65, 13      , 0       , 0       , init_reset                      },
+	{ "INIT_CONFIGURE_MEM"                , 0x66, 1       , 0       , 0       , init_configure_mem              },
+	{ "INIT_CONFIGURE_CLK"                , 0x67, 1       , 0       , 0       , init_configure_clk              },
+	{ "INIT_CONFIGURE_PREINIT"            , 0x68, 1       , 0       , 0       , init_configure_preinit          },
+	{ "INIT_IO"                           , 0x69, 5       , 0       , 0       , init_io                         },
+	{ "INIT_SUB"                          , 0x6B, 2       , 0       , 0       , init_sub                        },
+	{ "INIT_RAM_CONDITION"                , 0x6D, 3       , 0       , 0       , init_ram_condition              },
+	{ "INIT_NV_REG"                       , 0x6E, 13      , 0       , 0       , init_nv_reg                     },
+	{ "INIT_MACRO"                        , 0x6F, 2       , 0       , 0       , init_macro                      },
+	{ "INIT_DONE"                         , 0x71, 1       , 0       , 0       , init_done                       },
+	{ "INIT_RESUME"                       , 0x72, 1       , 0       , 0       , init_resume                     },
+	/* INIT_RAM_CONDITION2 (0x73, 9, 0, 0) removed due to no example of use */
+	{ "INIT_TIME"                         , 0x74, 3       , 0       , 0       , init_time                       },
+	{ "INIT_CONDITION"                    , 0x75, 2       , 0       , 0       , init_condition                  },
+	{ "INIT_IO_CONDITION"                 , 0x76, 2       , 0       , 0       , init_io_condition               },
+	{ "INIT_INDEX_IO"                     , 0x78, 6       , 0       , 0       , init_index_io                   },
+	{ "INIT_PLL"                          , 0x79, 7       , 0       , 0       , init_pll                        },
+	{ "INIT_ZM_REG"                       , 0x7A, 9       , 0       , 0       , init_zm_reg                     },
+	/* INIT_RAM_RESTRICT_PLL's length is adjusted by the BIT M table */
+	{ "INIT_RAM_RESTRICT_PLL"             , 0x87, 2       , 0       , 0       , init_ram_restrict_pll           },
+	{ "INIT_8C"                           , 0x8C, 1       , 0       , 0       , init_8c                         },
+	{ "INIT_8D"                           , 0x8D, 1       , 0       , 0       , init_8d                         },
+	{ "INIT_GPIO"                         , 0x8E, 1       , 0       , 0       , init_gpio                       },
+	/* INIT_RAM_RESTRICT_ZM_REG_GROUP's mult is loaded by M table in BIT */
+	{ "INIT_RAM_RESTRICT_ZM_REG_GROUP"    , 0x8F, 7       , 6       , 0       , init_ram_restrict_zm_reg_group  },
+	{ "INIT_COPY_ZM_REG"                  , 0x90, 9       , 0       , 0       , init_copy_zm_reg                },
+	{ "INIT_ZM_REG_GROUP_ADDRESS_LATCHED" , 0x91, 6       , 5       , 4       , init_zm_reg_group_addr_latched  },
+	{ "INIT_RESERVED"                     , 0x92, 1       , 0       , 0       , init_reserved                   },
+	{ "INIT_96"                           , 0x96, 17      , 0       , 0       , init_96                         },
+	{ "INIT_97"                           , 0x97, 13      , 0       , 0       , init_97                         },
+	{ "INIT_AUXCH"                        , 0x98, 6       , 5       , 2       , init_auxch                      },
+	{ "INIT_ZM_AUXCH"                     , 0x99, 6       , 5       , 1       , init_zm_auxch                   },
+	{ NULL                                , 0   , 0       , 0       , 0       , NULL                            }
+};
+
+static unsigned int get_init_table_entry_length(struct nvbios *bios, unsigned int offset, int i)
+{
+	/* Calculates the length of a given init table entry. */
+	return itbl_entry[i].length + bios->data[offset + itbl_entry[i].length_offset]*itbl_entry[i].length_multiplier;
+}
+
+#define MAX_TABLE_OPS 1000
+
+static int
+parse_init_table(struct nvbios *bios, unsigned int offset,
+		 struct init_exec *iexec)
+{
+	/*
+	 * Parses all commands in an init table.
+	 *
+	 * We start out executing all commands found in the init table. Some
+	 * opcodes may change the status of iexec->execute to SKIP, which will
+	 * cause the following opcodes to perform no operation until the value
+	 * is changed back to EXECUTE.
+	 */
+
+	int count = 0, i;
+	uint8_t id;
+
+	/*
+	 * Loop until INIT_DONE causes us to break out of the loop
+	 * (or until offset > bios length just in case... )
+	 * (and no more than MAX_TABLE_OPS iterations, just in case... )
+	 */
+	while ((offset < bios->length) && (count++ < MAX_TABLE_OPS)) {
+		id = bios->data[offset];
+
+		/* Find matching id in itbl_entry */
+		for (i = 0; itbl_entry[i].name && (itbl_entry[i].id != id); i++)
+			;
+
+		if (itbl_entry[i].name) {
+			BIOSLOG(bios, "0x%04X: [ (0x%02X) - %s ]\n",
+				offset, itbl_entry[i].id, itbl_entry[i].name);
+
+			/* execute eventual command handler */
+			if (itbl_entry[i].handler)
+				if (!(*itbl_entry[i].handler)(bios, offset, iexec))
+					break;
+		} else {
+			NV_ERROR(bios->dev,
+				 "0x%04X: Init table command not found: "
+				 "0x%02X\n", offset, id);
+			return -ENOENT;
+		}
+
+		/*
+		 * Add the offset of the current command including all data
+		 * of that command. The offset will then be pointing on the
+		 * next op code.
+		 */
+		offset += get_init_table_entry_length(bios, offset, i);
+	}
+
+	if (offset >= bios->length)
+		NV_WARN(bios->dev,
+			"Offset 0x%04X greater than known bios image length.  "
+			"Corrupt image?\n", offset);
+	if (count >= MAX_TABLE_OPS)
+		NV_WARN(bios->dev,
+			"More than %d opcodes to a table is unlikely, "
+			"is the bios image corrupt?\n", MAX_TABLE_OPS);
+
+	return 0;
+}
+
+static void
+parse_init_tables(struct nvbios *bios)
+{
+	/* Loops and calls parse_init_table() for each present table. */
+
+	int i = 0;
+	uint16_t table;
+	struct init_exec iexec = {true, false};
+
+	if (bios->old_style_init) {
+		if (bios->init_script_tbls_ptr)
+			parse_init_table(bios, bios->init_script_tbls_ptr, &iexec);
+		if (bios->extra_init_script_tbl_ptr)
+			parse_init_table(bios, bios->extra_init_script_tbl_ptr, &iexec);
+
+		return;
+	}
+
+	while ((table = ROM16(bios->data[bios->init_script_tbls_ptr + i]))) {
+		NV_INFO(bios->dev,
+			"Parsing VBIOS init table %d at offset 0x%04X\n",
+			i / 2, table);
+		BIOSLOG(bios, "0x%04X: ------ Executing following commands ------\n", table);
+
+		parse_init_table(bios, table, &iexec);
+		i += 2;
+	}
+}
+
+static uint16_t clkcmptable(struct nvbios *bios, uint16_t clktable, int pxclk)
+{
+	int compare_record_len, i = 0;
+	uint16_t compareclk, scriptptr = 0;
+
+	if (bios->major_version < 5) /* pre BIT */
+		compare_record_len = 3;
+	else
+		compare_record_len = 4;
+
+	do {
+		compareclk = ROM16(bios->data[clktable + compare_record_len * i]);
+		if (pxclk >= compareclk * 10) {
+			if (bios->major_version < 5) {
+				uint8_t tmdssub = bios->data[clktable + 2 + compare_record_len * i];
+				scriptptr = ROM16(bios->data[bios->init_script_tbls_ptr + tmdssub * 2]);
+			} else
+				scriptptr = ROM16(bios->data[clktable + 2 + compare_record_len * i]);
+			break;
+		}
+		i++;
+	} while (compareclk);
+
+	return scriptptr;
+}
+
+static void
+run_digital_op_script(struct drm_device *dev, uint16_t scriptptr,
+		      struct dcb_entry *dcbent, int head, bool dl)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nvbios *bios = &dev_priv->VBIOS;
+	struct init_exec iexec = {true, false};
+
+	NV_TRACE(dev, "0x%04X: Parsing digital output script table\n",
+		 scriptptr);
+	bios_idxprt_wr(bios, NV_CIO_CRX__COLOR, NV_CIO_CRE_44,
+		       head ? NV_CIO_CRE_44_HEADB : NV_CIO_CRE_44_HEADA);
+	/* note: if dcb entries have been merged, index may be misleading */
+	NVWriteVgaCrtc5758(dev, head, 0, dcbent->index);
+	parse_init_table(bios, scriptptr, &iexec);
+
+	nv04_dfp_bind_head(dev, dcbent, head, dl);
+}
+
+static int call_lvds_manufacturer_script(struct drm_device *dev, struct dcb_entry *dcbent, int head, enum LVDS_script script)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nvbios *bios = &dev_priv->VBIOS;
+	uint8_t sub = bios->data[bios->fp.xlated_entry + script] + (bios->fp.link_c_increment && dcbent->or & OUTPUT_C ? 1 : 0);
+	uint16_t scriptofs = ROM16(bios->data[bios->init_script_tbls_ptr + sub * 2]);
+
+	if (!bios->fp.xlated_entry || !sub || !scriptofs)
+		return -EINVAL;
+
+	run_digital_op_script(dev, scriptofs, dcbent, head, bios->fp.dual_link);
+
+	if (script == LVDS_PANEL_OFF) {
+		/* off-on delay in ms */
+		msleep(ROM16(bios->data[bios->fp.xlated_entry + 7]));
+	}
+#ifdef __powerpc__
+	/* Powerbook specific quirks */
+	if (script == LVDS_RESET && ((dev->pci_device & 0xffff) == 0x0179 || (dev->pci_device & 0xffff) == 0x0329))
+		nv_write_tmds(dev, dcbent->or, 0, 0x02, 0x72);
+	if ((dev->pci_device & 0xffff) == 0x0179 || (dev->pci_device & 0xffff) == 0x0189 || (dev->pci_device & 0xffff) == 0x0329) {
+		if (script == LVDS_PANEL_ON) {
+			bios_wr32(bios, NV_PBUS_DEBUG_DUALHEAD_CTL, bios_rd32(bios, NV_PBUS_DEBUG_DUALHEAD_CTL) | (1 << 31));
+			bios_wr32(bios, NV_PCRTC_GPIO_EXT, bios_rd32(bios, NV_PCRTC_GPIO_EXT) | 1);
+		}
+		if (script == LVDS_PANEL_OFF) {
+			bios_wr32(bios, NV_PBUS_DEBUG_DUALHEAD_CTL, bios_rd32(bios, NV_PBUS_DEBUG_DUALHEAD_CTL) & ~(1 << 31));
+			bios_wr32(bios, NV_PCRTC_GPIO_EXT, bios_rd32(bios, NV_PCRTC_GPIO_EXT) & ~3);
+		}
+	}
+#endif
+
+	return 0;
+}
+
+static int run_lvds_table(struct drm_device *dev, struct dcb_entry *dcbent, int head, enum LVDS_script script, int pxclk)
+{
+	/*
+	 * The BIT LVDS table's header has the information to setup the
+	 * necessary registers. Following the standard 4 byte header are:
+	 * A bitmask byte and a dual-link transition pxclk value for use in
+	 * selecting the init script when not using straps; 4 script pointers
+	 * for panel power, selected by output and on/off; and 8 table pointers
+	 * for panel init, the needed one determined by output, and bits in the
+	 * conf byte. These tables are similar to the TMDS tables, consisting
+	 * of a list of pxclks and script pointers.
+	 */
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nvbios *bios = &dev_priv->VBIOS;
+	unsigned int outputset = (dcbent->or == 4) ? 1 : 0;
+	uint16_t scriptptr = 0, clktable;
+	uint8_t clktableptr = 0;
+
+	/*
+	 * For now we assume version 3.0 table - g80 support will need some
+	 * changes
+	 */
+
+	switch (script) {
+	case LVDS_INIT:
+		return -ENOSYS;
+	case LVDS_BACKLIGHT_ON:
+	case LVDS_PANEL_ON:
+		scriptptr = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 7 + outputset * 2]);
+		break;
+	case LVDS_BACKLIGHT_OFF:
+	case LVDS_PANEL_OFF:
+		scriptptr = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 11 + outputset * 2]);
+		break;
+	case LVDS_RESET:
+		if (dcbent->lvdsconf.use_straps_for_mode) {
+			if (bios->fp.dual_link)
+				clktableptr += 2;
+			if (bios->fp.BITbit1)
+				clktableptr++;
+		} else {
+			/* using EDID */
+			uint8_t fallback = bios->data[bios->fp.lvdsmanufacturerpointer + 4];
+			int fallbackcmpval = (dcbent->or == 4) ? 4 : 1;
+
+			if (bios->fp.dual_link) {
+				clktableptr += 2;
+				fallbackcmpval *= 2;
+			}
+			if (fallbackcmpval & fallback)
+				clktableptr++;
+		}
+
+		/* adding outputset * 8 may not be correct */
+		clktable = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 15 + clktableptr * 2 + outputset * 8]);
+		if (!clktable) {
+			NV_ERROR(dev, "Pixel clock comparison table not found\n");
+			return -ENOENT;
+		}
+		scriptptr = clkcmptable(bios, clktable, pxclk);
+	}
+
+	if (!scriptptr) {
+		NV_ERROR(dev, "LVDS output init script not found\n");
+		return -ENOENT;
+	}
+	run_digital_op_script(dev, scriptptr, dcbent, head, bios->fp.dual_link);
+
+	return 0;
+}
+
+int call_lvds_script(struct drm_device *dev, struct dcb_entry *dcbent, int head, enum LVDS_script script, int pxclk)
+{
+	/*
+	 * LVDS operations are multiplexed in an effort to present a single API
+	 * which works with two vastly differing underlying structures.
+	 * This acts as the demux
+	 */
+
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nvbios *bios = &dev_priv->VBIOS;
+	uint8_t lvds_ver = bios->data[bios->fp.lvdsmanufacturerpointer];
+	uint32_t sel_clk_binding, sel_clk;
+	int ret;
+
+	if (bios->fp.last_script_invoc == (script << 1 | head) || !lvds_ver ||
+	    (lvds_ver >= 0x30 && script == LVDS_INIT))
+		return 0;
+
+	if (!bios->fp.lvds_init_run) {
+		bios->fp.lvds_init_run = true;
+		call_lvds_script(dev, dcbent, head, LVDS_INIT, pxclk);
+	}
+
+	if (script == LVDS_PANEL_ON && bios->fp.reset_after_pclk_change)
+		call_lvds_script(dev, dcbent, head, LVDS_RESET, pxclk);
+	if (script == LVDS_RESET && bios->fp.power_off_for_reset)
+		call_lvds_script(dev, dcbent, head, LVDS_PANEL_OFF, pxclk);
+
+	NV_TRACE(dev, "Calling LVDS script %d:\n", script);
+
+	/* don't let script change pll->head binding */
+	sel_clk_binding = bios_rd32(bios, NV_PRAMDAC_SEL_CLK) & 0x50000;
+
+	if (lvds_ver < 0x30)
+		ret = call_lvds_manufacturer_script(dev, dcbent, head, script);
+	else
+		ret = run_lvds_table(dev, dcbent, head, script, pxclk);
+
+	bios->fp.last_script_invoc = (script << 1 | head);
+
+	sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK) & ~0x50000;
+	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, sel_clk | sel_clk_binding);
+	/* some scripts set a value in NV_PBUS_POWERCTRL_2 and break video overlay */
+	nvWriteMC(dev, NV_PBUS_POWERCTRL_2, 0);
+
+	return ret;
+}
+
+struct lvdstableheader {
+	uint8_t lvds_ver, headerlen, recordlen;
+};
+
+static int parse_lvds_manufacturer_table_header(struct drm_device *dev, struct nvbios *bios, struct lvdstableheader *lth)
+{
+	/*
+	 * BMP version (0xa) LVDS table has a simple header of version and
+	 * record length. The BIT LVDS table has the typical BIT table header:
+	 * version byte, header length byte, record length byte, and a byte for
+	 * the maximum number of records that can be held in the table.
+	 */
+
+	uint8_t lvds_ver, headerlen, recordlen;
+
+	memset(lth, 0, sizeof(struct lvdstableheader));
+
+	if (bios->fp.lvdsmanufacturerpointer == 0x0) {
+		NV_ERROR(dev, "Pointer to LVDS manufacturer table invalid\n");
+		return -EINVAL;
+	}
+
+	lvds_ver = bios->data[bios->fp.lvdsmanufacturerpointer];
+
+	switch (lvds_ver) {
+	case 0x0a:	/* pre NV40 */
+		headerlen = 2;
+		recordlen = bios->data[bios->fp.lvdsmanufacturerpointer + 1];
+		break;
+	case 0x30:	/* NV4x */
+		headerlen = bios->data[bios->fp.lvdsmanufacturerpointer + 1];
+		if (headerlen < 0x1f) {
+			NV_ERROR(dev, "LVDS table header not understood\n");
+			return -EINVAL;
+		}
+		recordlen = bios->data[bios->fp.lvdsmanufacturerpointer + 2];
+		break;
+	case 0x40:	/* G80/G90 */
+		headerlen = bios->data[bios->fp.lvdsmanufacturerpointer + 1];
+		if (headerlen < 0x7) {
+			NV_ERROR(dev, "LVDS table header not understood\n");
+			return -EINVAL;
+		}
+		recordlen = bios->data[bios->fp.lvdsmanufacturerpointer + 2];
+		break;
+	default:
+		NV_ERROR(dev,
+			 "LVDS table revision %d.%d not currently supported\n",
+			 lvds_ver >> 4, lvds_ver & 0xf);
+		return -ENOSYS;
+	}
+
+	lth->lvds_ver = lvds_ver;
+	lth->headerlen = headerlen;
+	lth->recordlen = recordlen;
+
+	return 0;
+}
+
+static int
+get_fp_strap(struct drm_device *dev, struct nvbios *bios)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	/*
+	 * The fp strap is normally dictated by the "User Strap" in
+	 * PEXTDEV_BOOT_0[20:16], but on BMP cards when bit 2 of the
+	 * Internal_Flags struct at 0x48 is set, the user strap gets overriden
+	 * by the PCI subsystem ID during POST, but not before the previous user
+	 * strap has been committed to CR58 for CR57=0xf on head A, which may be
+	 * read and used instead
+	 */
+
+	if (bios->major_version < 5 && bios->data[0x48] & 0x4)
+		return NVReadVgaCrtc5758(dev, 0, 0xf) & 0xf;
+
+	if (dev_priv->card_type >= NV_50)
+		return (bios_rd32(bios, NV_PEXTDEV_BOOT_0) >> 24) & 0xf;
+	else
+		return (bios_rd32(bios, NV_PEXTDEV_BOOT_0) >> 16) & 0xf;
+}
+
+static int parse_fp_mode_table(struct drm_device *dev, struct nvbios *bios)
+{
+	uint8_t *fptable;
+	uint8_t fptable_ver, headerlen = 0, recordlen, fpentries = 0xf, fpindex;
+	int ret, ofs, fpstrapping;
+	struct lvdstableheader lth;
+
+	if (bios->fp.fptablepointer == 0x0) {
+		/* Apple cards don't have the fp table; the laptops use DDC */
+		/* The table is also missing on some x86 IGPs */
+#ifndef __powerpc__
+		NV_ERROR(dev, "Pointer to flat panel table invalid\n");
+#endif
+		bios->pub.digital_min_front_porch = 0x4b;
+		return 0;
+	}
+
+	fptable = &bios->data[bios->fp.fptablepointer];
+	fptable_ver = fptable[0];
+
+	switch (fptable_ver) {
+	/*
+	 * BMP version 0x5.0x11 BIOSen have version 1 like tables, but no
+	 * version field, and miss one of the spread spectrum/PWM bytes.
+	 * This could affect early GF2Go parts (not seen any appropriate ROMs
+	 * though). Here we assume that a version of 0x05 matches this case
+	 * (combining with a BMP version check would be better), as the
+	 * common case for the panel type field is 0x0005, and that is in
+	 * fact what we are reading the first byte of.
+	 */
+	case 0x05:	/* some NV10, 11, 15, 16 */
+		recordlen = 42;
+		ofs = -1;
+		break;
+	case 0x10:	/* some NV15/16, and NV11+ */
+		recordlen = 44;
+		ofs = 0;
+		break;
+	case 0x20:	/* NV40+ */
+		headerlen = fptable[1];
+		recordlen = fptable[2];
+		fpentries = fptable[3];
+		/*
+		 * fptable[4] is the minimum
+		 * RAMDAC_FP_HCRTC -> RAMDAC_FP_HSYNC_START gap
+		 */
+		bios->pub.digital_min_front_porch = fptable[4];
+		ofs = -7;
+		break;
+	default:
+		NV_ERROR(dev,
+			 "FP table revision %d.%d not currently supported\n",
+			 fptable_ver >> 4, fptable_ver & 0xf);
+		return -ENOSYS;
+	}
+
+	if (!bios->is_mobile) /* !mobile only needs digital_min_front_porch */
+		return 0;
+
+	ret = parse_lvds_manufacturer_table_header(dev, bios, &lth);
+	if (ret)
+		return ret;
+
+	if (lth.lvds_ver == 0x30 || lth.lvds_ver == 0x40) {
+		bios->fp.fpxlatetableptr = bios->fp.lvdsmanufacturerpointer +
+							lth.headerlen + 1;
+		bios->fp.xlatwidth = lth.recordlen;
+	}
+	if (bios->fp.fpxlatetableptr == 0x0) {
+		NV_ERROR(dev, "Pointer to flat panel xlat table invalid\n");
+		return -EINVAL;
+	}
+
+	fpstrapping = get_fp_strap(dev, bios);
+
+	fpindex = bios->data[bios->fp.fpxlatetableptr +
+					fpstrapping * bios->fp.xlatwidth];
+
+	if (fpindex > fpentries) {
+		NV_ERROR(dev, "Bad flat panel table index\n");
+		return -ENOENT;
+	}
+
+	/* nv4x cards need both a strap value and fpindex of 0xf to use DDC */
+	if (lth.lvds_ver > 0x10)
+		bios->pub.fp_no_ddc = fpstrapping != 0xf || fpindex != 0xf;
+
+	/*
+	 * If either the strap or xlated fpindex value are 0xf there is no
+	 * panel using a strap-derived bios mode present.  this condition
+	 * includes, but is different from, the DDC panel indicator above
+	 */
+	if (fpstrapping == 0xf || fpindex == 0xf)
+		return 0;
+
+	bios->fp.mode_ptr = bios->fp.fptablepointer + headerlen +
+			    recordlen * fpindex + ofs;
+
+	NV_TRACE(dev, "BIOS FP mode: %dx%d (%dkHz pixel clock)\n",
+		 ROM16(bios->data[bios->fp.mode_ptr + 11]) + 1,
+		 ROM16(bios->data[bios->fp.mode_ptr + 25]) + 1,
+		 ROM16(bios->data[bios->fp.mode_ptr + 7]) * 10);
+
+	return 0;
+}
+
+bool nouveau_bios_fp_mode(struct drm_device *dev, struct drm_display_mode *mode)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nvbios *bios = &dev_priv->VBIOS;
+	uint8_t *mode_entry = &bios->data[bios->fp.mode_ptr];
+
+	if (!mode)	/* just checking whether we can produce a mode */
+		return bios->fp.mode_ptr;
+
+	memset(mode, 0, sizeof(struct drm_display_mode));
+	/*
+	 * For version 1.0 (version in byte 0):
+	 * bytes 1-2 are "panel type", including bits on whether Colour/mono,
+	 * single/dual link, and type (TFT etc.)
+	 * bytes 3-6 are bits per colour in RGBX
+	 */
+	mode->clock = ROM16(mode_entry[7]) * 10;
+	/* bytes 9-10 is HActive */
+	mode->hdisplay = ROM16(mode_entry[11]) + 1;
+	/*
+	 * bytes 13-14 is HValid Start
+	 * bytes 15-16 is HValid End
+	 */
+	mode->hsync_start = ROM16(mode_entry[17]) + 1;
+	mode->hsync_end = ROM16(mode_entry[19]) + 1;
+	mode->htotal = ROM16(mode_entry[21]) + 1;
+	/* bytes 23-24, 27-30 similarly, but vertical */
+	mode->vdisplay = ROM16(mode_entry[25]) + 1;
+	mode->vsync_start = ROM16(mode_entry[31]) + 1;
+	mode->vsync_end = ROM16(mode_entry[33]) + 1;
+	mode->vtotal = ROM16(mode_entry[35]) + 1;
+	mode->flags |= (mode_entry[37] & 0x10) ?
+			DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC;
+	mode->flags |= (mode_entry[37] & 0x1) ?
+			DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC;
+	/*
+	 * bytes 38-39 relate to spread spectrum settings
+	 * bytes 40-43 are something to do with PWM
+	 */
+
+	mode->status = MODE_OK;
+	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+	drm_mode_set_name(mode);
+	return bios->fp.mode_ptr;
+}
+
+int nouveau_bios_parse_lvds_table(struct drm_device *dev, int pxclk, bool *dl, bool *if_is_24bit)
+{
+	/*
+	 * The LVDS table header is (mostly) described in
+	 * parse_lvds_manufacturer_table_header(): the BIT header additionally
+	 * contains the dual-link transition pxclk (in 10s kHz), at byte 5 - if
+	 * straps are not being used for the panel, this specifies the frequency
+	 * at which modes should be set up in the dual link style.
+	 *
+	 * Following the header, the BMP (ver 0xa) table has several records,
+	 * indexed by a seperate xlat table, indexed in turn by the fp strap in
+	 * EXTDEV_BOOT. Each record had a config byte, followed by 6 script
+	 * numbers for use by INIT_SUB which controlled panel init and power,
+	 * and finally a dword of ms to sleep between power off and on
+	 * operations.
+	 *
+	 * In the BIT versions, the table following the header serves as an
+	 * integrated config and xlat table: the records in the table are
+	 * indexed by the FP strap nibble in EXTDEV_BOOT, and each record has
+	 * two bytes - the first as a config byte, the second for indexing the
+	 * fp mode table pointed to by the BIT 'D' table
+	 *
+	 * DDC is not used until after card init, so selecting the correct table
+	 * entry and setting the dual link flag for EDID equipped panels,
+	 * requiring tests against the native-mode pixel clock, cannot be done
+	 * until later, when this function should be called with non-zero pxclk
+	 */
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nvbios *bios = &dev_priv->VBIOS;
+	int fpstrapping = get_fp_strap(dev, bios), lvdsmanufacturerindex = 0;
+	struct lvdstableheader lth;
+	uint16_t lvdsofs;
+	int ret, chip_version = bios->pub.chip_version;
+
+	ret = parse_lvds_manufacturer_table_header(dev, bios, &lth);
+	if (ret)
+		return ret;
+
+	switch (lth.lvds_ver) {
+	case 0x0a:	/* pre NV40 */
+		lvdsmanufacturerindex = bios->data[
+					bios->fp.fpxlatemanufacturertableptr +
+					fpstrapping];
+
+		/* we're done if this isn't the EDID panel case */
+		if (!pxclk)
+			break;
+
+		if (chip_version < 0x25) {
+			/* nv17 behaviour
+			 *
+			 * It seems the old style lvds script pointer is reused
+			 * to select 18/24 bit colour depth for EDID panels.
+			 */
+			lvdsmanufacturerindex =
+				(bios->legacy.lvds_single_a_script_ptr & 1) ?
+									2 : 0;
+			if (pxclk >= bios->fp.duallink_transition_clk)
+				lvdsmanufacturerindex++;
+		} else if (chip_version < 0x30) {
+			/* nv28 behaviour (off-chip encoder)
+			 *
+			 * nv28 does a complex dance of first using byte 121 of
+			 * the EDID to choose the lvdsmanufacturerindex, then
+			 * later attempting to match the EDID manufacturer and
+			 * product IDs in a table (signature 'pidt' (panel id
+			 * table?)), setting an lvdsmanufacturerindex of 0 and
+			 * an fp strap of the match index (or 0xf if none)
+			 */
+			lvdsmanufacturerindex = 0;
+		} else {
+			/* nv31, nv34 behaviour */
+			lvdsmanufacturerindex = 0;
+			if (pxclk >= bios->fp.duallink_transition_clk)
+				lvdsmanufacturerindex = 2;
+			if (pxclk >= 140000)
+				lvdsmanufacturerindex = 3;
+		}
+
+		/*
+		 * nvidia set the high nibble of (cr57=f, cr58) to
+		 * lvdsmanufacturerindex in this case; we don't
+		 */
+		break;
+	case 0x30:	/* NV4x */
+	case 0x40:	/* G80/G90 */
+		lvdsmanufacturerindex = fpstrapping;
+		break;
+	default:
+		NV_ERROR(dev, "LVDS table revision not currently supported\n");
+		return -ENOSYS;
+	}
+
+	lvdsofs = bios->fp.xlated_entry = bios->fp.lvdsmanufacturerpointer + lth.headerlen + lth.recordlen * lvdsmanufacturerindex;
+	switch (lth.lvds_ver) {
+	case 0x0a:
+		bios->fp.power_off_for_reset = bios->data[lvdsofs] & 1;
+		bios->fp.reset_after_pclk_change = bios->data[lvdsofs] & 2;
+		bios->fp.dual_link = bios->data[lvdsofs] & 4;
+		bios->fp.link_c_increment = bios->data[lvdsofs] & 8;
+		*if_is_24bit = bios->data[lvdsofs] & 16;
+		break;
+	case 0x30:
+		/*
+		 * My money would be on there being a 24 bit interface bit in
+		 * this table, but I have no example of a laptop bios with a
+		 * 24 bit panel to confirm that. Hence we shout loudly if any
+		 * bit other than bit 0 is set (I've not even seen bit 1)
+		 */
+		if (bios->data[lvdsofs] > 1)
+			NV_ERROR(dev,
+				 "You have a very unusual laptop display; please report it\n");
+		/*
+		 * No sign of the "power off for reset" or "reset for panel
+		 * on" bits, but it's safer to assume we should
+		 */
+		bios->fp.power_off_for_reset = true;
+		bios->fp.reset_after_pclk_change = true;
+		/*
+		 * It's ok lvdsofs is wrong for nv4x edid case; dual_link is
+		 * over-written, and BITbit1 isn't used
+		 */
+		bios->fp.dual_link = bios->data[lvdsofs] & 1;
+		bios->fp.BITbit1 = bios->data[lvdsofs] & 2;
+		bios->fp.duallink_transition_clk = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 5]) * 10;
+		break;
+	case 0x40:
+		bios->fp.dual_link = bios->data[lvdsofs] & 1;
+		bios->fp.if_is_24bit = bios->data[lvdsofs] & 2;
+		bios->fp.strapless_is_24bit = bios->data[bios->fp.lvdsmanufacturerpointer + 4];
+		bios->fp.duallink_transition_clk = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 5]) * 10;
+		break;
+	}
+
+	/* set dual_link flag for EDID case */
+	if (pxclk && (chip_version < 0x25 || chip_version > 0x28))
+		bios->fp.dual_link = (pxclk >= bios->fp.duallink_transition_clk);
+
+	*dl = bios->fp.dual_link;
+
+	return 0;
+}
+
+static uint8_t *
+bios_output_config_match(struct drm_device *dev, struct dcb_entry *dcbent,
+			 uint16_t record, int record_len, int record_nr)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nvbios *bios = &dev_priv->VBIOS;
+	uint32_t entry;
+	uint16_t table;
+	int i, v;
+
+	for (i = 0; i < record_nr; i++, record += record_len) {
+		table = ROM16(bios->data[record]);
+		if (!table)
+			continue;
+		entry = ROM32(bios->data[table]);
+
+		v = (entry & 0x000f0000) >> 16;
+		if (!(v & dcbent->or))
+			continue;
+
+		v = (entry & 0x000000f0) >> 4;
+		if (v != dcbent->location)
+			continue;
+
+		v = (entry & 0x0000000f);
+		if (v != dcbent->type)
+			continue;
+
+		return &bios->data[table];
+	}
+
+	return NULL;
+}
+
+void *
+nouveau_bios_dp_table(struct drm_device *dev, struct dcb_entry *dcbent,
+		      int *length)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nvbios *bios = &dev_priv->VBIOS;
+	uint8_t *table;
+
+	if (!bios->display.dp_table_ptr) {
+		NV_ERROR(dev, "No pointer to DisplayPort table\n");
+		return NULL;
+	}
+	table = &bios->data[bios->display.dp_table_ptr];
+
+	if (table[0] != 0x21) {
+		NV_ERROR(dev, "DisplayPort table version 0x%02x unknown\n",
+			 table[0]);
+		return NULL;
+	}
+
+	*length = table[4];
+	return bios_output_config_match(dev, dcbent,
+					bios->display.dp_table_ptr + table[1],
+					table[2], table[3]);
+}
+
+int
+nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent,
+			       uint32_t sub, int pxclk)
+{
+	/*
+	 * The display script table is located by the BIT 'U' table.
+	 *
+	 * It contains an array of pointers to various tables describing
+	 * a particular output type.  The first 32-bits of the output
+	 * tables contains similar information to a DCB entry, and is
+	 * used to decide whether that particular table is suitable for
+	 * the output you want to access.
+	 *
+	 * The "record header length" field here seems to indicate the
+	 * offset of the first configuration entry in the output tables.
+	 * This is 10 on most cards I've seen, but 12 has been witnessed
+	 * on DP cards, and there's another script pointer within the
+	 * header.
+	 *
+	 * offset + 0   ( 8 bits): version
+	 * offset + 1   ( 8 bits): header length
+	 * offset + 2   ( 8 bits): record length
+	 * offset + 3   ( 8 bits): number of records
+	 * offset + 4   ( 8 bits): record header length
+	 * offset + 5   (16 bits): pointer to first output script table
+	 */
+
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct init_exec iexec = {true, false};
+	struct nvbios *bios = &dev_priv->VBIOS;
+	uint8_t *table = &bios->data[bios->display.script_table_ptr];
+	uint8_t *otable = NULL;
+	uint16_t script;
+	int i = 0;
+
+	if (!bios->display.script_table_ptr) {
+		NV_ERROR(dev, "No pointer to output script table\n");
+		return 1;
+	}
+
+	/*
+	 * Nothing useful has been in any of the pre-2.0 tables I've seen,
+	 * so until they are, we really don't need to care.
+	 */
+	if (table[0] < 0x20)
+		return 1;
+
+	if (table[0] != 0x20 && table[0] != 0x21) {
+		NV_ERROR(dev, "Output script table version 0x%02x unknown\n",
+			 table[0]);
+		return 1;
+	}
+
+	/*
+	 * The output script tables describing a particular output type
+	 * look as follows:
+	 *
+	 * offset + 0   (32 bits): output this table matches (hash of DCB)
+	 * offset + 4   ( 8 bits): unknown
+	 * offset + 5   ( 8 bits): number of configurations
+	 * offset + 6   (16 bits): pointer to some script
+	 * offset + 8   (16 bits): pointer to some script
+	 *
+	 * headerlen == 10
+	 * offset + 10           : configuration 0
+	 *
+	 * headerlen == 12
+	 * offset + 10           : pointer to some script
+	 * offset + 12           : configuration 0
+	 *
+	 * Each config entry is as follows:
+	 *
+	 * offset + 0   (16 bits): unknown, assumed to be a match value
+	 * offset + 2   (16 bits): pointer to script table (clock set?)
+	 * offset + 4   (16 bits): pointer to script table (reset?)
+	 *
+	 * There doesn't appear to be a count value to say how many
+	 * entries exist in each script table, instead, a 0 value in
+	 * the first 16-bit word seems to indicate both the end of the
+	 * list and the default entry.  The second 16-bit word in the
+	 * script tables is a pointer to the script to execute.
+	 */
+
+	NV_DEBUG(dev, "Searching for output entry for %d %d %d\n",
+			dcbent->type, dcbent->location, dcbent->or);
+	otable = bios_output_config_match(dev, dcbent, table[1] +
+					  bios->display.script_table_ptr,
+					  table[2], table[3]);
+	if (!otable) {
+		NV_ERROR(dev, "Couldn't find matching output script table\n");
+		return 1;
+	}
+
+	if (pxclk < -2 || pxclk > 0) {
+		/* Try to find matching script table entry */
+		for (i = 0; i < otable[5]; i++) {
+			if (ROM16(otable[table[4] + i*6]) == sub)
+				break;
+		}
+
+		if (i == otable[5]) {
+			NV_ERROR(dev, "Table 0x%04x not found for %d/%d, "
+				      "using first\n",
+				 sub, dcbent->type, dcbent->or);
+			i = 0;
+		}
+	}
+
+	bios->display.output = dcbent;
+
+	if (pxclk == 0) {
+		script = ROM16(otable[6]);
+		if (!script) {
+			NV_DEBUG(dev, "output script 0 not found\n");
+			return 1;
+		}
+
+		NV_TRACE(dev, "0x%04X: parsing output script 0\n", script);
+		parse_init_table(bios, script, &iexec);
+	} else
+	if (pxclk == -1) {
+		script = ROM16(otable[8]);
+		if (!script) {
+			NV_DEBUG(dev, "output script 1 not found\n");
+			return 1;
+		}
+
+		NV_TRACE(dev, "0x%04X: parsing output script 1\n", script);
+		parse_init_table(bios, script, &iexec);
+	} else
+	if (pxclk == -2) {
+		if (table[4] >= 12)
+			script = ROM16(otable[10]);
+		else
+			script = 0;
+		if (!script) {
+			NV_DEBUG(dev, "output script 2 not found\n");
+			return 1;
+		}
+
+		NV_TRACE(dev, "0x%04X: parsing output script 2\n", script);
+		parse_init_table(bios, script, &iexec);
+	} else
+	if (pxclk > 0) {
+		script = ROM16(otable[table[4] + i*6 + 2]);
+		if (script)
+			script = clkcmptable(bios, script, pxclk);
+		if (!script) {
+			NV_ERROR(dev, "clock script 0 not found\n");
+			return 1;
+		}
+
+		NV_TRACE(dev, "0x%04X: parsing clock script 0\n", script);
+		parse_init_table(bios, script, &iexec);
+	} else
+	if (pxclk < 0) {
+		script = ROM16(otable[table[4] + i*6 + 4]);
+		if (script)
+			script = clkcmptable(bios, script, -pxclk);
+		if (!script) {
+			NV_DEBUG(dev, "clock script 1 not found\n");
+			return 1;
+		}
+
+		NV_TRACE(dev, "0x%04X: parsing clock script 1\n", script);
+		parse_init_table(bios, script, &iexec);
+	}
+
+	return 0;
+}
+
+
+int run_tmds_table(struct drm_device *dev, struct dcb_entry *dcbent, int head, int pxclk)
+{
+	/*
+	 * the pxclk parameter is in kHz
+	 *
+	 * This runs the TMDS regs setting code found on BIT bios cards
+	 *
+	 * For ffs(or) == 1 use the first table, for ffs(or) == 2 and
+	 * ffs(or) == 3, use the second.
+	 */
+
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nvbios *bios = &dev_priv->VBIOS;
+	int cv = bios->pub.chip_version;
+	uint16_t clktable = 0, scriptptr;
+	uint32_t sel_clk_binding, sel_clk;
+
+	/* pre-nv17 off-chip tmds uses scripts, post nv17 doesn't */
+	if (cv >= 0x17 && cv != 0x1a && cv != 0x20 &&
+	    dcbent->location != DCB_LOC_ON_CHIP)
+		return 0;
+
+	switch (ffs(dcbent->or)) {
+	case 1:
+		clktable = bios->tmds.output0_script_ptr;
+		break;
+	case 2:
+	case 3:
+		clktable = bios->tmds.output1_script_ptr;
+		break;
+	}
+
+	if (!clktable) {
+		NV_ERROR(dev, "Pixel clock comparison table not found\n");
+		return -EINVAL;
+	}
+
+	scriptptr = clkcmptable(bios, clktable, pxclk);
+
+	if (!scriptptr) {
+		NV_ERROR(dev, "TMDS output init script not found\n");
+		return -ENOENT;
+	}
+
+	/* don't let script change pll->head binding */
+	sel_clk_binding = bios_rd32(bios, NV_PRAMDAC_SEL_CLK) & 0x50000;
+	run_digital_op_script(dev, scriptptr, dcbent, head, pxclk >= 165000);
+	sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK) & ~0x50000;
+	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, sel_clk | sel_clk_binding);
+
+	return 0;
+}
+
+int get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims *pll_lim)
+{
+	/*
+	 * PLL limits table
+	 *
+	 * Version 0x10: NV30, NV31
+	 * One byte header (version), one record of 24 bytes
+	 * Version 0x11: NV36 - Not implemented
+	 * Seems to have same record style as 0x10, but 3 records rather than 1
+	 * Version 0x20: Found on Geforce 6 cards
+	 * Trivial 4 byte BIT header. 31 (0x1f) byte record length
+	 * Version 0x21: Found on Geforce 7, 8 and some Geforce 6 cards
+	 * 5 byte header, fifth byte of unknown purpose. 35 (0x23) byte record
+	 * length in general, some (integrated) have an extra configuration byte
+	 * Version 0x30: Found on Geforce 8, separates the register mapping
+	 * from the limits tables.
+	 */
+
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nvbios *bios = &dev_priv->VBIOS;
+	int cv = bios->pub.chip_version, pllindex = 0;
+	uint8_t pll_lim_ver = 0, headerlen = 0, recordlen = 0, entries = 0;
+	uint32_t crystal_strap_mask, crystal_straps;
+
+	if (!bios->pll_limit_tbl_ptr) {
+		if (cv == 0x30 || cv == 0x31 || cv == 0x35 || cv == 0x36 ||
+		    cv >= 0x40) {
+			NV_ERROR(dev, "Pointer to PLL limits table invalid\n");
+			return -EINVAL;
+		}
+	} else
+		pll_lim_ver = bios->data[bios->pll_limit_tbl_ptr];
+
+	crystal_strap_mask = 1 << 6;
+	/* open coded dev->twoHeads test */
+	if (cv > 0x10 && cv != 0x15 && cv != 0x1a && cv != 0x20)
+		crystal_strap_mask |= 1 << 22;
+	crystal_straps = nvReadEXTDEV(dev, NV_PEXTDEV_BOOT_0) &
+							crystal_strap_mask;
+
+	switch (pll_lim_ver) {
+	/*
+	 * We use version 0 to indicate a pre limit table bios (single stage
+	 * pll) and load the hard coded limits instead.
+	 */
+	case 0:
+		break;
+	case 0x10:
+	case 0x11:
+		/*
+		 * Strictly v0x11 has 3 entries, but the last two don't seem
+		 * to get used.
+		 */
+		headerlen = 1;
+		recordlen = 0x18;
+		entries = 1;
+		pllindex = 0;
+		break;
+	case 0x20:
+	case 0x21:
+	case 0x30:
+	case 0x40:
+		headerlen = bios->data[bios->pll_limit_tbl_ptr + 1];
+		recordlen = bios->data[bios->pll_limit_tbl_ptr + 2];
+		entries = bios->data[bios->pll_limit_tbl_ptr + 3];
+		break;
+	default:
+		NV_ERROR(dev, "PLL limits table revision 0x%X not currently "
+				"supported\n", pll_lim_ver);
+		return -ENOSYS;
+	}
+
+	/* initialize all members to zero */
+	memset(pll_lim, 0, sizeof(struct pll_lims));
+
+	if (pll_lim_ver == 0x10 || pll_lim_ver == 0x11) {
+		uint8_t *pll_rec = &bios->data[bios->pll_limit_tbl_ptr + headerlen + recordlen * pllindex];
+
+		pll_lim->vco1.minfreq = ROM32(pll_rec[0]);
+		pll_lim->vco1.maxfreq = ROM32(pll_rec[4]);
+		pll_lim->vco2.minfreq = ROM32(pll_rec[8]);
+		pll_lim->vco2.maxfreq = ROM32(pll_rec[12]);
+		pll_lim->vco1.min_inputfreq = ROM32(pll_rec[16]);
+		pll_lim->vco2.min_inputfreq = ROM32(pll_rec[20]);
+		pll_lim->vco1.max_inputfreq = pll_lim->vco2.max_inputfreq = INT_MAX;
+
+		/* these values taken from nv30/31/36 */
+		pll_lim->vco1.min_n = 0x1;
+		if (cv == 0x36)
+			pll_lim->vco1.min_n = 0x5;
+		pll_lim->vco1.max_n = 0xff;
+		pll_lim->vco1.min_m = 0x1;
+		pll_lim->vco1.max_m = 0xd;
+		pll_lim->vco2.min_n = 0x4;
+		/*
+		 * On nv30, 31, 36 (i.e. all cards with two stage PLLs with this
+		 * table version (apart from nv35)), N2 is compared to
+		 * maxN2 (0x46) and 10 * maxM2 (0x4), so set maxN2 to 0x28 and
+		 * save a comparison
+		 */
+		pll_lim->vco2.max_n = 0x28;
+		if (cv == 0x30 || cv == 0x35)
+			/* only 5 bits available for N2 on nv30/35 */
+			pll_lim->vco2.max_n = 0x1f;
+		pll_lim->vco2.min_m = 0x1;
+		pll_lim->vco2.max_m = 0x4;
+		pll_lim->max_log2p = 0x7;
+		pll_lim->max_usable_log2p = 0x6;
+	} else if (pll_lim_ver == 0x20 || pll_lim_ver == 0x21) {
+		uint16_t plloffs = bios->pll_limit_tbl_ptr + headerlen;
+		uint32_t reg = 0; /* default match */
+		uint8_t *pll_rec;
+		int i;
+
+		/*
+		 * First entry is default match, if nothing better. warn if
+		 * reg field nonzero
+		 */
+		if (ROM32(bios->data[plloffs]))
+			NV_WARN(dev, "Default PLL limit entry has non-zero "
+				       "register field\n");
+
+		if (limit_match > MAX_PLL_TYPES)
+			/* we've been passed a reg as the match */
+			reg = limit_match;
+		else /* limit match is a pll type */
+			for (i = 1; i < entries && !reg; i++) {
+				uint32_t cmpreg = ROM32(bios->data[plloffs + recordlen * i]);
+
+				if (limit_match == NVPLL &&
+				    (cmpreg == NV_PRAMDAC_NVPLL_COEFF || cmpreg == 0x4000))
+					reg = cmpreg;
+				if (limit_match == MPLL &&
+				    (cmpreg == NV_PRAMDAC_MPLL_COEFF || cmpreg == 0x4020))
+					reg = cmpreg;
+				if (limit_match == VPLL1 &&
+				    (cmpreg == NV_PRAMDAC_VPLL_COEFF || cmpreg == 0x4010))
+					reg = cmpreg;
+				if (limit_match == VPLL2 &&
+				    (cmpreg == NV_RAMDAC_VPLL2 || cmpreg == 0x4018))
+					reg = cmpreg;
+			}
+
+		for (i = 1; i < entries; i++)
+			if (ROM32(bios->data[plloffs + recordlen * i]) == reg) {
+				pllindex = i;
+				break;
+			}
+
+		pll_rec = &bios->data[plloffs + recordlen * pllindex];
+
+		BIOSLOG(bios, "Loading PLL limits for reg 0x%08x\n",
+			pllindex ? reg : 0);
+
+		/*
+		 * Frequencies are stored in tables in MHz, kHz are more
+		 * useful, so we convert.
+		 */
+
+		/* What output frequencies can each VCO generate? */
+		pll_lim->vco1.minfreq = ROM16(pll_rec[4]) * 1000;
+		pll_lim->vco1.maxfreq = ROM16(pll_rec[6]) * 1000;
+		pll_lim->vco2.minfreq = ROM16(pll_rec[8]) * 1000;
+		pll_lim->vco2.maxfreq = ROM16(pll_rec[10]) * 1000;
+
+		/* What input frequencies they accept (past the m-divider)? */
+		pll_lim->vco1.min_inputfreq = ROM16(pll_rec[12]) * 1000;
+		pll_lim->vco2.min_inputfreq = ROM16(pll_rec[14]) * 1000;
+		pll_lim->vco1.max_inputfreq = ROM16(pll_rec[16]) * 1000;
+		pll_lim->vco2.max_inputfreq = ROM16(pll_rec[18]) * 1000;
+
+		/* What values are accepted as multiplier and divider? */
+		pll_lim->vco1.min_n = pll_rec[20];
+		pll_lim->vco1.max_n = pll_rec[21];
+		pll_lim->vco1.min_m = pll_rec[22];
+		pll_lim->vco1.max_m = pll_rec[23];
+		pll_lim->vco2.min_n = pll_rec[24];
+		pll_lim->vco2.max_n = pll_rec[25];
+		pll_lim->vco2.min_m = pll_rec[26];
+		pll_lim->vco2.max_m = pll_rec[27];
+
+		pll_lim->max_usable_log2p = pll_lim->max_log2p = pll_rec[29];
+		if (pll_lim->max_log2p > 0x7)
+			/* pll decoding in nv_hw.c assumes never > 7 */
+			NV_WARN(dev, "Max log2 P value greater than 7 (%d)\n",
+				pll_lim->max_log2p);
+		if (cv < 0x60)
+			pll_lim->max_usable_log2p = 0x6;
+		pll_lim->log2p_bias = pll_rec[30];
+
+		if (recordlen > 0x22)
+			pll_lim->refclk = ROM32(pll_rec[31]);
+
+		if (recordlen > 0x23 && pll_rec[35])
+			NV_WARN(dev,
+				"Bits set in PLL configuration byte (%x)\n",
+				pll_rec[35]);
+
+		/* C51 special not seen elsewhere */
+		if (cv == 0x51 && !pll_lim->refclk) {
+			uint32_t sel_clk = bios_rd32(bios, NV_PRAMDAC_SEL_CLK);
+
+			if (((limit_match == NV_PRAMDAC_VPLL_COEFF || limit_match == VPLL1) && sel_clk & 0x20) ||
+			    ((limit_match == NV_RAMDAC_VPLL2 || limit_match == VPLL2) && sel_clk & 0x80)) {
+				if (bios_idxprt_rd(bios, NV_CIO_CRX__COLOR, NV_CIO_CRE_CHIP_ID_INDEX) < 0xa3)
+					pll_lim->refclk = 200000;
+				else
+					pll_lim->refclk = 25000;
+			}
+		}
+	} else if (pll_lim_ver == 0x30) { /* ver 0x30 */
+		uint8_t *entry = &bios->data[bios->pll_limit_tbl_ptr + headerlen];
+		uint8_t *record = NULL;
+		int i;
+
+		BIOSLOG(bios, "Loading PLL limits for register 0x%08x\n",
+			limit_match);
+
+		for (i = 0; i < entries; i++, entry += recordlen) {
+			if (ROM32(entry[3]) == limit_match) {
+				record = &bios->data[ROM16(entry[1])];
+				break;
+			}
+		}
+
+		if (!record) {
+			NV_ERROR(dev, "Register 0x%08x not found in PLL "
+				 "limits table", limit_match);
+			return -ENOENT;
+		}
+
+		pll_lim->vco1.minfreq = ROM16(record[0]) * 1000;
+		pll_lim->vco1.maxfreq = ROM16(record[2]) * 1000;
+		pll_lim->vco2.minfreq = ROM16(record[4]) * 1000;
+		pll_lim->vco2.maxfreq = ROM16(record[6]) * 1000;
+		pll_lim->vco1.min_inputfreq = ROM16(record[8]) * 1000;
+		pll_lim->vco2.min_inputfreq = ROM16(record[10]) * 1000;
+		pll_lim->vco1.max_inputfreq = ROM16(record[12]) * 1000;
+		pll_lim->vco2.max_inputfreq = ROM16(record[14]) * 1000;
+		pll_lim->vco1.min_n = record[16];
+		pll_lim->vco1.max_n = record[17];
+		pll_lim->vco1.min_m = record[18];
+		pll_lim->vco1.max_m = record[19];
+		pll_lim->vco2.min_n = record[20];
+		pll_lim->vco2.max_n = record[21];
+		pll_lim->vco2.min_m = record[22];
+		pll_lim->vco2.max_m = record[23];
+		pll_lim->max_usable_log2p = pll_lim->max_log2p = record[25];
+		pll_lim->log2p_bias = record[27];
+		pll_lim->refclk = ROM32(record[28]);
+	} else if (pll_lim_ver) { /* ver 0x40 */
+		uint8_t *entry = &bios->data[bios->pll_limit_tbl_ptr + headerlen];
+		uint8_t *record = NULL;
+		int i;
+
+		BIOSLOG(bios, "Loading PLL limits for register 0x%08x\n",
+			limit_match);
+
+		for (i = 0; i < entries; i++, entry += recordlen) {
+			if (ROM32(entry[3]) == limit_match) {
+				record = &bios->data[ROM16(entry[1])];
+				break;
+			}
+		}
+
+		if (!record) {
+			NV_ERROR(dev, "Register 0x%08x not found in PLL "
+				 "limits table", limit_match);
+			return -ENOENT;
+		}
+
+		pll_lim->vco1.minfreq = ROM16(record[0]) * 1000;
+		pll_lim->vco1.maxfreq = ROM16(record[2]) * 1000;
+		pll_lim->vco1.min_inputfreq = ROM16(record[4]) * 1000;
+		pll_lim->vco1.max_inputfreq = ROM16(record[6]) * 1000;
+		pll_lim->vco1.min_m = record[8];
+		pll_lim->vco1.max_m = record[9];
+		pll_lim->vco1.min_n = record[10];
+		pll_lim->vco1.max_n = record[11];
+		pll_lim->min_p = record[12];
+		pll_lim->max_p = record[13];
+		/* where did this go to?? */
+		if (limit_match == 0x00614100 || limit_match == 0x00614900)
+			pll_lim->refclk = 27000;
+		else
+			pll_lim->refclk = 100000;
+	}
+
+	/*
+	 * By now any valid limit table ought to have set a max frequency for
+	 * vco1, so if it's zero it's either a pre limit table bios, or one
+	 * with an empty limit table (seen on nv18)
+	 */
+	if (!pll_lim->vco1.maxfreq) {
+		pll_lim->vco1.minfreq = bios->fminvco;
+		pll_lim->vco1.maxfreq = bios->fmaxvco;
+		pll_lim->vco1.min_inputfreq = 0;
+		pll_lim->vco1.max_inputfreq = INT_MAX;
+		pll_lim->vco1.min_n = 0x1;
+		pll_lim->vco1.max_n = 0xff;
+		pll_lim->vco1.min_m = 0x1;
+		if (crystal_straps == 0) {
+			/* nv05 does this, nv11 doesn't, nv10 unknown */
+			if (cv < 0x11)
+				pll_lim->vco1.min_m = 0x7;
+			pll_lim->vco1.max_m = 0xd;
+		} else {
+			if (cv < 0x11)
+				pll_lim->vco1.min_m = 0x8;
+			pll_lim->vco1.max_m = 0xe;
+		}
+		if (cv < 0x17 || cv == 0x1a || cv == 0x20)
+			pll_lim->max_log2p = 4;
+		else
+			pll_lim->max_log2p = 5;
+		pll_lim->max_usable_log2p = pll_lim->max_log2p;
+	}
+
+	if (!pll_lim->refclk)
+		switch (crystal_straps) {
+		case 0:
+			pll_lim->refclk = 13500;
+			break;
+		case (1 << 6):
+			pll_lim->refclk = 14318;
+			break;
+		case (1 << 22):
+			pll_lim->refclk = 27000;
+			break;
+		case (1 << 22 | 1 << 6):
+			pll_lim->refclk = 25000;
+			break;
+		}
+
+#if 0 /* for easy debugging */
+	ErrorF("pll.vco1.minfreq: %d\n", pll_lim->vco1.minfreq);
+	ErrorF("pll.vco1.maxfreq: %d\n", pll_lim->vco1.maxfreq);
+	ErrorF("pll.vco2.minfreq: %d\n", pll_lim->vco2.minfreq);
+	ErrorF("pll.vco2.maxfreq: %d\n", pll_lim->vco2.maxfreq);
+
+	ErrorF("pll.vco1.min_inputfreq: %d\n", pll_lim->vco1.min_inputfreq);
+	ErrorF("pll.vco1.max_inputfreq: %d\n", pll_lim->vco1.max_inputfreq);
+	ErrorF("pll.vco2.min_inputfreq: %d\n", pll_lim->vco2.min_inputfreq);
+	ErrorF("pll.vco2.max_inputfreq: %d\n", pll_lim->vco2.max_inputfreq);
+
+	ErrorF("pll.vco1.min_n: %d\n", pll_lim->vco1.min_n);
+	ErrorF("pll.vco1.max_n: %d\n", pll_lim->vco1.max_n);
+	ErrorF("pll.vco1.min_m: %d\n", pll_lim->vco1.min_m);
+	ErrorF("pll.vco1.max_m: %d\n", pll_lim->vco1.max_m);
+	ErrorF("pll.vco2.min_n: %d\n", pll_lim->vco2.min_n);
+	ErrorF("pll.vco2.max_n: %d\n", pll_lim->vco2.max_n);
+	ErrorF("pll.vco2.min_m: %d\n", pll_lim->vco2.min_m);
+	ErrorF("pll.vco2.max_m: %d\n", pll_lim->vco2.max_m);
+
+	ErrorF("pll.max_log2p: %d\n", pll_lim->max_log2p);
+	ErrorF("pll.log2p_bias: %d\n", pll_lim->log2p_bias);
+
+	ErrorF("pll.refclk: %d\n", pll_lim->refclk);
+#endif
+
+	return 0;
+}
+
+static void parse_bios_version(struct drm_device *dev, struct nvbios *bios, uint16_t offset)
+{
+	/*
+	 * offset + 0  (8 bits): Micro version
+	 * offset + 1  (8 bits): Minor version
+	 * offset + 2  (8 bits): Chip version
+	 * offset + 3  (8 bits): Major version
+	 */
+
+	bios->major_version = bios->data[offset + 3];
+	bios->pub.chip_version = bios->data[offset + 2];
+	NV_TRACE(dev, "Bios version %02x.%02x.%02x.%02x\n",
+		 bios->data[offset + 3], bios->data[offset + 2],
+		 bios->data[offset + 1], bios->data[offset]);
+}
+
+static void parse_script_table_pointers(struct nvbios *bios, uint16_t offset)
+{
+	/*
+	 * Parses the init table segment for pointers used in script execution.
+	 *
+	 * offset + 0  (16 bits): init script tables pointer
+	 * offset + 2  (16 bits): macro index table pointer
+	 * offset + 4  (16 bits): macro table pointer
+	 * offset + 6  (16 bits): condition table pointer
+	 * offset + 8  (16 bits): io condition table pointer
+	 * offset + 10 (16 bits): io flag condition table pointer
+	 * offset + 12 (16 bits): init function table pointer
+	 */
+
+	bios->init_script_tbls_ptr = ROM16(bios->data[offset]);
+	bios->macro_index_tbl_ptr = ROM16(bios->data[offset + 2]);
+	bios->macro_tbl_ptr = ROM16(bios->data[offset + 4]);
+	bios->condition_tbl_ptr = ROM16(bios->data[offset + 6]);
+	bios->io_condition_tbl_ptr = ROM16(bios->data[offset + 8]);
+	bios->io_flag_condition_tbl_ptr = ROM16(bios->data[offset + 10]);
+	bios->init_function_tbl_ptr = ROM16(bios->data[offset + 12]);
+}
+
+static int parse_bit_A_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry)
+{
+	/*
+	 * Parses the load detect values for g80 cards.
+	 *
+	 * offset + 0 (16 bits): loadval table pointer
+	 */
+
+	uint16_t load_table_ptr;
+	uint8_t version, headerlen, entrylen, num_entries;
+
+	if (bitentry->length != 3) {
+		NV_ERROR(dev, "Do not understand BIT A table\n");
+		return -EINVAL;
+	}
+
+	load_table_ptr = ROM16(bios->data[bitentry->offset]);
+
+	if (load_table_ptr == 0x0) {
+		NV_ERROR(dev, "Pointer to BIT loadval table invalid\n");
+		return -EINVAL;
+	}
+
+	version = bios->data[load_table_ptr];
+
+	if (version != 0x10) {
+		NV_ERROR(dev, "BIT loadval table version %d.%d not supported\n",
+			 version >> 4, version & 0xF);
+		return -ENOSYS;
+	}
+
+	headerlen = bios->data[load_table_ptr + 1];
+	entrylen = bios->data[load_table_ptr + 2];
+	num_entries = bios->data[load_table_ptr + 3];
+
+	if (headerlen != 4 || entrylen != 4 || num_entries != 2) {
+		NV_ERROR(dev, "Do not understand BIT loadval table\n");
+		return -EINVAL;
+	}
+
+	/* First entry is normal dac, 2nd tv-out perhaps? */
+	bios->pub.dactestval = ROM32(bios->data[load_table_ptr + headerlen]) & 0x3ff;
+
+	return 0;
+}
+
+static int parse_bit_C_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry)
+{
+	/*
+	 * offset + 8  (16 bits): PLL limits table pointer
+	 *
+	 * There's more in here, but that's unknown.
+	 */
+
+	if (bitentry->length < 10) {
+		NV_ERROR(dev, "Do not understand BIT C table\n");
+		return -EINVAL;
+	}
+
+	bios->pll_limit_tbl_ptr = ROM16(bios->data[bitentry->offset + 8]);
+
+	return 0;
+}
+
+static int parse_bit_display_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry)
+{
+	/*
+	 * Parses the flat panel table segment that the bit entry points to.
+	 * Starting at bitentry->offset:
+	 *
+	 * offset + 0  (16 bits): ??? table pointer - seems to have 18 byte
+	 * records beginning with a freq.
+	 * offset + 2  (16 bits): mode table pointer
+	 */
+
+	if (bitentry->length != 4) {
+		NV_ERROR(dev, "Do not understand BIT display table\n");
+		return -EINVAL;
+	}
+
+	bios->fp.fptablepointer = ROM16(bios->data[bitentry->offset + 2]);
+
+	return 0;
+}
+
+static int parse_bit_init_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry)
+{
+	/*
+	 * Parses the init table segment that the bit entry points to.
+	 *
+	 * See parse_script_table_pointers for layout
+	 */
+
+	if (bitentry->length < 14) {
+		NV_ERROR(dev, "Do not understand init table\n");
+		return -EINVAL;
+	}
+
+	parse_script_table_pointers(bios, bitentry->offset);
+
+	if (bitentry->length >= 16)
+		bios->some_script_ptr = ROM16(bios->data[bitentry->offset + 14]);
+	if (bitentry->length >= 18)
+		bios->init96_tbl_ptr = ROM16(bios->data[bitentry->offset + 16]);
+
+	return 0;
+}
+
+static int parse_bit_i_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry)
+{
+	/*
+	 * BIT 'i' (info?) table
+	 *
+	 * offset + 0  (32 bits): BIOS version dword (as in B table)
+	 * offset + 5  (8  bits): BIOS feature byte (same as for BMP?)
+	 * offset + 13 (16 bits): pointer to table containing DAC load
+	 * detection comparison values
+	 *
+	 * There's other things in the table, purpose unknown
+	 */
+
+	uint16_t daccmpoffset;
+	uint8_t dacver, dacheaderlen;
+
+	if (bitentry->length < 6) {
+		NV_ERROR(dev, "BIT i table too short for needed information\n");
+		return -EINVAL;
+	}
+
+	parse_bios_version(dev, bios, bitentry->offset);
+
+	/*
+	 * bit 4 seems to indicate a mobile bios (doesn't suffer from BMP's
+	 * Quadro identity crisis), other bits possibly as for BMP feature byte
+	 */
+	bios->feature_byte = bios->data[bitentry->offset + 5];
+	bios->is_mobile = bios->feature_byte & FEATURE_MOBILE;
+
+	if (bitentry->length < 15) {
+		NV_WARN(dev, "BIT i table not long enough for DAC load "
+			       "detection comparison table\n");
+		return -EINVAL;
+	}
+
+	daccmpoffset = ROM16(bios->data[bitentry->offset + 13]);
+
+	/* doesn't exist on g80 */
+	if (!daccmpoffset)
+		return 0;
+
+	/*
+	 * The first value in the table, following the header, is the
+	 * comparison value, the second entry is a comparison value for
+	 * TV load detection.
+	 */
+
+	dacver = bios->data[daccmpoffset];
+	dacheaderlen = bios->data[daccmpoffset + 1];
+
+	if (dacver != 0x00 && dacver != 0x10) {
+		NV_WARN(dev, "DAC load detection comparison table version "
+			       "%d.%d not known\n", dacver >> 4, dacver & 0xf);
+		return -ENOSYS;
+	}
+
+	bios->pub.dactestval = ROM32(bios->data[daccmpoffset + dacheaderlen]);
+	bios->pub.tvdactestval = ROM32(bios->data[daccmpoffset + dacheaderlen + 4]);
+
+	return 0;
+}
+
+static int parse_bit_lvds_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry)
+{
+	/*
+	 * Parses the LVDS table segment that the bit entry points to.
+	 * Starting at bitentry->offset:
+	 *
+	 * offset + 0  (16 bits): LVDS strap xlate table pointer
+	 */
+
+	if (bitentry->length != 2) {
+		NV_ERROR(dev, "Do not understand BIT LVDS table\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * No idea if it's still called the LVDS manufacturer table, but
+	 * the concept's close enough.
+	 */
+	bios->fp.lvdsmanufacturerpointer = ROM16(bios->data[bitentry->offset]);
+
+	return 0;
+}
+
+static int
+parse_bit_M_tbl_entry(struct drm_device *dev, struct nvbios *bios,
+		      struct bit_entry *bitentry)
+{
+	/*
+	 * offset + 2  (8  bits): number of options in an
+	 * 	INIT_RAM_RESTRICT_ZM_REG_GROUP opcode option set
+	 * offset + 3  (16 bits): pointer to strap xlate table for RAM
+	 * 	restrict option selection
+	 *
+	 * There's a bunch of bits in this table other than the RAM restrict
+	 * stuff that we don't use - their use currently unknown
+	 */
+
+	uint16_t rr_strap_xlat;
+	uint8_t rr_group_count;
+	int i;
+
+	/*
+	 * Older bios versions don't have a sufficiently long table for
+	 * what we want
+	 */
+	if (bitentry->length < 0x5)
+		return 0;
+
+	if (bitentry->id[1] < 2) {
+		rr_group_count = bios->data[bitentry->offset + 2];
+		rr_strap_xlat = ROM16(bios->data[bitentry->offset + 3]);
+	} else {
+		rr_group_count = bios->data[bitentry->offset + 0];
+		rr_strap_xlat = ROM16(bios->data[bitentry->offset + 1]);
+	}
+
+	/* adjust length of INIT_87 */
+	for (i = 0; itbl_entry[i].name && (itbl_entry[i].id != 0x87); i++);
+	itbl_entry[i].length += rr_group_count * 4;
+
+	/* set up multiplier for INIT_RAM_RESTRICT_ZM_REG_GROUP */
+	for (; itbl_entry[i].name && (itbl_entry[i].id != 0x8f); i++);
+	itbl_entry[i].length_multiplier = rr_group_count * 4;
+
+	init_ram_restrict_zm_reg_group_blocklen = itbl_entry[i].length_multiplier;
+	bios->ram_restrict_tbl_ptr = rr_strap_xlat;
+
+	return 0;
+}
+
+static int parse_bit_tmds_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry)
+{
+	/*
+	 * Parses the pointer to the TMDS table
+	 *
+	 * Starting at bitentry->offset:
+	 *
+	 * offset + 0  (16 bits): TMDS table pointer
+	 *
+	 * The TMDS table is typically found just before the DCB table, with a
+	 * characteristic signature of 0x11,0x13 (1.1 being version, 0x13 being
+	 * length?)
+	 *
+	 * At offset +7 is a pointer to a script, which I don't know how to
+	 * run yet.
+	 * At offset +9 is a pointer to another script, likewise
+	 * Offset +11 has a pointer to a table where the first word is a pxclk
+	 * frequency and the second word a pointer to a script, which should be
+	 * run if the comparison pxclk frequency is less than the pxclk desired.
+	 * This repeats for decreasing comparison frequencies
+	 * Offset +13 has a pointer to a similar table
+	 * The selection of table (and possibly +7/+9 script) is dictated by
+	 * "or" from the DCB.
+	 */
+
+	uint16_t tmdstableptr, script1, script2;
+
+	if (bitentry->length != 2) {
+		NV_ERROR(dev, "Do not understand BIT TMDS table\n");
+		return -EINVAL;
+	}
+
+	tmdstableptr = ROM16(bios->data[bitentry->offset]);
+
+	if (tmdstableptr == 0x0) {
+		NV_ERROR(dev, "Pointer to TMDS table invalid\n");
+		return -EINVAL;
+	}
+
+	/* nv50+ has v2.0, but we don't parse it atm */
+	if (bios->data[tmdstableptr] != 0x11) {
+		NV_WARN(dev,
+			"TMDS table revision %d.%d not currently supported\n",
+			bios->data[tmdstableptr] >> 4, bios->data[tmdstableptr] & 0xf);
+		return -ENOSYS;
+	}
+
+	/*
+	 * These two scripts are odd: they don't seem to get run even when
+	 * they are not stubbed.
+	 */
+	script1 = ROM16(bios->data[tmdstableptr + 7]);
+	script2 = ROM16(bios->data[tmdstableptr + 9]);
+	if (bios->data[script1] != 'q' || bios->data[script2] != 'q')
+		NV_WARN(dev, "TMDS table script pointers not stubbed\n");
+
+	bios->tmds.output0_script_ptr = ROM16(bios->data[tmdstableptr + 11]);
+	bios->tmds.output1_script_ptr = ROM16(bios->data[tmdstableptr + 13]);
+
+	return 0;
+}
+
+static int
+parse_bit_U_tbl_entry(struct drm_device *dev, struct nvbios *bios,
+		      struct bit_entry *bitentry)
+{
+	/*
+	 * Parses the pointer to the G80 output script tables
+	 *
+	 * Starting at bitentry->offset:
+	 *
+	 * offset + 0  (16 bits): output script table pointer
+	 */
+
+	uint16_t outputscripttableptr;
+
+	if (bitentry->length != 3) {
+		NV_ERROR(dev, "Do not understand BIT U table\n");
+		return -EINVAL;
+	}
+
+	outputscripttableptr = ROM16(bios->data[bitentry->offset]);
+	bios->display.script_table_ptr = outputscripttableptr;
+	return 0;
+}
+
+static int
+parse_bit_displayport_tbl_entry(struct drm_device *dev, struct nvbios *bios,
+				struct bit_entry *bitentry)
+{
+	bios->display.dp_table_ptr = ROM16(bios->data[bitentry->offset]);
+	return 0;
+}
+
+struct bit_table {
+	const char id;
+	int (* const parse_fn)(struct drm_device *, struct nvbios *, struct bit_entry *);
+};
+
+#define BIT_TABLE(id, funcid) ((struct bit_table){ id, parse_bit_##funcid##_tbl_entry })
+
+static int
+parse_bit_table(struct nvbios *bios, const uint16_t bitoffset,
+		struct bit_table *table)
+{
+	struct drm_device *dev = bios->dev;
+	uint8_t maxentries = bios->data[bitoffset + 4];
+	int i, offset;
+	struct bit_entry bitentry;
+
+	for (i = 0, offset = bitoffset + 6; i < maxentries; i++, offset += 6) {
+		bitentry.id[0] = bios->data[offset];
+
+		if (bitentry.id[0] != table->id)
+			continue;
+
+		bitentry.id[1] = bios->data[offset + 1];
+		bitentry.length = ROM16(bios->data[offset + 2]);
+		bitentry.offset = ROM16(bios->data[offset + 4]);
+
+		return table->parse_fn(dev, bios, &bitentry);
+	}
+
+	NV_INFO(dev, "BIT table '%c' not found\n", table->id);
+	return -ENOSYS;
+}
+
+static int
+parse_bit_structure(struct nvbios *bios, const uint16_t bitoffset)
+{
+	int ret;
+
+	/*
+	 * The only restriction on parsing order currently is having 'i' first
+	 * for use of bios->*_version or bios->feature_byte while parsing;
+	 * functions shouldn't be actually *doing* anything apart from pulling
+	 * data from the image into the bios struct, thus no interdependencies
+	 */
+	ret = parse_bit_table(bios, bitoffset, &BIT_TABLE('i', i));
+	if (ret) /* info? */
+		return ret;
+	if (bios->major_version >= 0x60) /* g80+ */
+		parse_bit_table(bios, bitoffset, &BIT_TABLE('A', A));
+	ret = parse_bit_table(bios, bitoffset, &BIT_TABLE('C', C));
+	if (ret)
+		return ret;
+	parse_bit_table(bios, bitoffset, &BIT_TABLE('D', display));
+	ret = parse_bit_table(bios, bitoffset, &BIT_TABLE('I', init));
+	if (ret)
+		return ret;
+	parse_bit_table(bios, bitoffset, &BIT_TABLE('M', M)); /* memory? */
+	parse_bit_table(bios, bitoffset, &BIT_TABLE('L', lvds));
+	parse_bit_table(bios, bitoffset, &BIT_TABLE('T', tmds));
+	parse_bit_table(bios, bitoffset, &BIT_TABLE('U', U));
+	parse_bit_table(bios, bitoffset, &BIT_TABLE('d', displayport));
+
+	return 0;
+}
+
+static int parse_bmp_structure(struct drm_device *dev, struct nvbios *bios, unsigned int offset)
+{
+	/*
+	 * Parses the BMP structure for useful things, but does not act on them
+	 *
+	 * offset +   5: BMP major version
+	 * offset +   6: BMP minor version
+	 * offset +   9: BMP feature byte
+	 * offset +  10: BCD encoded BIOS version
+	 *
+	 * offset +  18: init script table pointer (for bios versions < 5.10h)
+	 * offset +  20: extra init script table pointer (for bios
+	 * versions < 5.10h)
+	 *
+	 * offset +  24: memory init table pointer (used on early bios versions)
+	 * offset +  26: SDR memory sequencing setup data table
+	 * offset +  28: DDR memory sequencing setup data table
+	 *
+	 * offset +  54: index of I2C CRTC pair to use for CRT output
+	 * offset +  55: index of I2C CRTC pair to use for TV output
+	 * offset +  56: index of I2C CRTC pair to use for flat panel output
+	 * offset +  58: write CRTC index for I2C pair 0
+	 * offset +  59: read CRTC index for I2C pair 0
+	 * offset +  60: write CRTC index for I2C pair 1
+	 * offset +  61: read CRTC index for I2C pair 1
+	 *
+	 * offset +  67: maximum internal PLL frequency (single stage PLL)
+	 * offset +  71: minimum internal PLL frequency (single stage PLL)
+	 *
+	 * offset +  75: script table pointers, as described in
+	 * parse_script_table_pointers
+	 *
+	 * offset +  89: TMDS single link output A table pointer
+	 * offset +  91: TMDS single link output B table pointer
+	 * offset +  95: LVDS single link output A table pointer
+	 * offset + 105: flat panel timings table pointer
+	 * offset + 107: flat panel strapping translation table pointer
+	 * offset + 117: LVDS manufacturer panel config table pointer
+	 * offset + 119: LVDS manufacturer strapping translation table pointer
+	 *
+	 * offset + 142: PLL limits table pointer
+	 *
+	 * offset + 156: minimum pixel clock for LVDS dual link
+	 */
+
+	uint8_t *bmp = &bios->data[offset], bmp_version_major, bmp_version_minor;
+	uint16_t bmplength;
+	uint16_t legacy_scripts_offset, legacy_i2c_offset;
+
+	/* load needed defaults in case we can't parse this info */
+	bios->bdcb.dcb.i2c[0].write = NV_CIO_CRE_DDC_WR__INDEX;
+	bios->bdcb.dcb.i2c[0].read = NV_CIO_CRE_DDC_STATUS__INDEX;
+	bios->bdcb.dcb.i2c[1].write = NV_CIO_CRE_DDC0_WR__INDEX;
+	bios->bdcb.dcb.i2c[1].read = NV_CIO_CRE_DDC0_STATUS__INDEX;
+	bios->pub.digital_min_front_porch = 0x4b;
+	bios->fmaxvco = 256000;
+	bios->fminvco = 128000;
+	bios->fp.duallink_transition_clk = 90000;
+
+	bmp_version_major = bmp[5];
+	bmp_version_minor = bmp[6];
+
+	NV_TRACE(dev, "BMP version %d.%d\n",
+		 bmp_version_major, bmp_version_minor);
+
+	/*
+	 * Make sure that 0x36 is blank and can't be mistaken for a DCB
+	 * pointer on early versions
+	 */
+	if (bmp_version_major < 5)
+		*(uint16_t *)&bios->data[0x36] = 0;
+
+	/*
+	 * Seems that the minor version was 1 for all major versions prior
+	 * to 5. Version 6 could theoretically exist, but I suspect BIT
+	 * happened instead.
+	 */
+	if ((bmp_version_major < 5 && bmp_version_minor != 1) || bmp_version_major > 5) {
+		NV_ERROR(dev, "You have an unsupported BMP version. "
+				"Please send in your bios\n");
+		return -ENOSYS;
+	}
+
+	if (bmp_version_major == 0)
+		/* nothing that's currently useful in this version */
+		return 0;
+	else if (bmp_version_major == 1)
+		bmplength = 44; /* exact for 1.01 */
+	else if (bmp_version_major == 2)
+		bmplength = 48; /* exact for 2.01 */
+	else if (bmp_version_major == 3)
+		bmplength = 54;
+		/* guessed - mem init tables added in this version */
+	else if (bmp_version_major == 4 || bmp_version_minor < 0x1)
+		/* don't know if 5.0 exists... */
+		bmplength = 62;
+		/* guessed - BMP I2C indices added in version 4*/
+	else if (bmp_version_minor < 0x6)
+		bmplength = 67; /* exact for 5.01 */
+	else if (bmp_version_minor < 0x10)
+		bmplength = 75; /* exact for 5.06 */
+	else if (bmp_version_minor == 0x10)
+		bmplength = 89; /* exact for 5.10h */
+	else if (bmp_version_minor < 0x14)
+		bmplength = 118; /* exact for 5.11h */
+	else if (bmp_version_minor < 0x24)
+		/*
+		 * Not sure of version where pll limits came in;
+		 * certainly exist by 0x24 though.
+		 */
+		/* length not exact: this is long enough to get lvds members */
+		bmplength = 123;
+	else if (bmp_version_minor < 0x27)
+		/*
+		 * Length not exact: this is long enough to get pll limit
+		 * member
+		 */
+		bmplength = 144;
+	else
+		/*
+		 * Length not exact: this is long enough to get dual link
+		 * transition clock.
+		 */
+		bmplength = 158;
+
+	/* checksum */
+	if (nv_cksum(bmp, 8)) {
+		NV_ERROR(dev, "Bad BMP checksum\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Bit 4 seems to indicate either a mobile bios or a quadro card --
+	 * mobile behaviour consistent (nv11+), quadro only seen nv18gl-nv36gl
+	 * (not nv10gl), bit 5 that the flat panel tables are present, and
+	 * bit 6 a tv bios.
+	 */
+	bios->feature_byte = bmp[9];
+
+	parse_bios_version(dev, bios, offset + 10);
+
+	if (bmp_version_major < 5 || bmp_version_minor < 0x10)
+		bios->old_style_init = true;
+	legacy_scripts_offset = 18;
+	if (bmp_version_major < 2)
+		legacy_scripts_offset -= 4;
+	bios->init_script_tbls_ptr = ROM16(bmp[legacy_scripts_offset]);
+	bios->extra_init_script_tbl_ptr = ROM16(bmp[legacy_scripts_offset + 2]);
+
+	if (bmp_version_major > 2) {	/* appears in BMP 3 */
+		bios->legacy.mem_init_tbl_ptr = ROM16(bmp[24]);
+		bios->legacy.sdr_seq_tbl_ptr = ROM16(bmp[26]);
+		bios->legacy.ddr_seq_tbl_ptr = ROM16(bmp[28]);
+	}
+
+	legacy_i2c_offset = 0x48;	/* BMP version 2 & 3 */
+	if (bmplength > 61)
+		legacy_i2c_offset = offset + 54;
+	bios->legacy.i2c_indices.crt = bios->data[legacy_i2c_offset];
+	bios->legacy.i2c_indices.tv = bios->data[legacy_i2c_offset + 1];
+	bios->legacy.i2c_indices.panel = bios->data[legacy_i2c_offset + 2];
+	bios->bdcb.dcb.i2c[0].write = bios->data[legacy_i2c_offset + 4];
+	bios->bdcb.dcb.i2c[0].read = bios->data[legacy_i2c_offset + 5];
+	bios->bdcb.dcb.i2c[1].write = bios->data[legacy_i2c_offset + 6];
+	bios->bdcb.dcb.i2c[1].read = bios->data[legacy_i2c_offset + 7];
+
+	if (bmplength > 74) {
+		bios->fmaxvco = ROM32(bmp[67]);
+		bios->fminvco = ROM32(bmp[71]);
+	}
+	if (bmplength > 88)
+		parse_script_table_pointers(bios, offset + 75);
+	if (bmplength > 94) {
+		bios->tmds.output0_script_ptr = ROM16(bmp[89]);
+		bios->tmds.output1_script_ptr = ROM16(bmp[91]);
+		/*
+		 * Never observed in use with lvds scripts, but is reused for
+		 * 18/24 bit panel interface default for EDID equipped panels
+		 * (if_is_24bit not set directly to avoid any oscillation).
+		 */
+		bios->legacy.lvds_single_a_script_ptr = ROM16(bmp[95]);
+	}
+	if (bmplength > 108) {
+		bios->fp.fptablepointer = ROM16(bmp[105]);
+		bios->fp.fpxlatetableptr = ROM16(bmp[107]);
+		bios->fp.xlatwidth = 1;
+	}
+	if (bmplength > 120) {
+		bios->fp.lvdsmanufacturerpointer = ROM16(bmp[117]);
+		bios->fp.fpxlatemanufacturertableptr = ROM16(bmp[119]);
+	}
+	if (bmplength > 143)
+		bios->pll_limit_tbl_ptr = ROM16(bmp[142]);
+
+	if (bmplength > 157)
+		bios->fp.duallink_transition_clk = ROM16(bmp[156]) * 10;
+
+	return 0;
+}
+
+static uint16_t findstr(uint8_t *data, int n, const uint8_t *str, int len)
+{
+	int i, j;
+
+	for (i = 0; i <= (n - len); i++) {
+		for (j = 0; j < len; j++)
+			if (data[i + j] != str[j])
+				break;
+		if (j == len)
+			return i;
+	}
+
+	return 0;
+}
+
+static int
+read_dcb_i2c_entry(struct drm_device *dev, int dcb_version, uint8_t *i2ctable, int index, struct dcb_i2c_entry *i2c)
+{
+	uint8_t dcb_i2c_ver = dcb_version, headerlen = 0, entry_len = 4;
+	int i2c_entries = DCB_MAX_NUM_I2C_ENTRIES;
+	int recordoffset = 0, rdofs = 1, wrofs = 0;
+	uint8_t port_type = 0;
+
+	if (!i2ctable)
+		return -EINVAL;
+
+	if (dcb_version >= 0x30) {
+		if (i2ctable[0] != dcb_version) /* necessary? */
+			NV_WARN(dev,
+				"DCB I2C table version mismatch (%02X vs %02X)\n",
+				i2ctable[0], dcb_version);
+		dcb_i2c_ver = i2ctable[0];
+		headerlen = i2ctable[1];
+		if (i2ctable[2] <= DCB_MAX_NUM_I2C_ENTRIES)
+			i2c_entries = i2ctable[2];
+		else
+			NV_WARN(dev,
+				"DCB I2C table has more entries than indexable "
+				"(%d entries, max index 15)\n", i2ctable[2]);
+		entry_len = i2ctable[3];
+		/* [4] is i2c_default_indices, read in parse_dcb_table() */
+	}
+	/*
+	 * It's your own fault if you call this function on a DCB 1.1 BIOS --
+	 * the test below is for DCB 1.2
+	 */
+	if (dcb_version < 0x14) {
+		recordoffset = 2;
+		rdofs = 0;
+		wrofs = 1;
+	}
+
+	if (index == 0xf)
+		return 0;
+	if (index > i2c_entries) {
+		NV_ERROR(dev, "DCB I2C index too big (%d > %d)\n",
+			 index, i2ctable[2]);
+		return -ENOENT;
+	}
+	if (i2ctable[headerlen + entry_len * index + 3] == 0xff) {
+		NV_ERROR(dev, "DCB I2C entry invalid\n");
+		return -EINVAL;
+	}
+
+	if (dcb_i2c_ver >= 0x30) {
+		port_type = i2ctable[headerlen + recordoffset + 3 + entry_len * index];
+
+		/*
+		 * Fixup for chips using same address offset for read and
+		 * write.
+		 */
+		if (port_type == 4)	/* seen on C51 */
+			rdofs = wrofs = 1;
+		if (port_type >= 5)	/* G80+ */
+			rdofs = wrofs = 0;
+	}
+
+	if (dcb_i2c_ver >= 0x40 && port_type != 5 && port_type != 6)
+		NV_WARN(dev, "DCB I2C table has port type %d\n", port_type);
+
+	i2c->port_type = port_type;
+	i2c->read = i2ctable[headerlen + recordoffset + rdofs + entry_len * index];
+	i2c->write = i2ctable[headerlen + recordoffset + wrofs + entry_len * index];
+
+	return 0;
+}
+
+static struct dcb_gpio_entry *
+new_gpio_entry(struct nvbios *bios)
+{
+	struct parsed_dcb_gpio *gpio = &bios->bdcb.gpio;
+
+	return &gpio->entry[gpio->entries++];
+}
+
+struct dcb_gpio_entry *
+nouveau_bios_gpio_entry(struct drm_device *dev, enum dcb_gpio_tag tag)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nvbios *bios = &dev_priv->VBIOS;
+	int i;
+
+	for (i = 0; i < bios->bdcb.gpio.entries; i++) {
+		if (bios->bdcb.gpio.entry[i].tag != tag)
+			continue;
+
+		return &bios->bdcb.gpio.entry[i];
+	}
+
+	return NULL;
+}
+
+static void
+parse_dcb30_gpio_entry(struct nvbios *bios, uint16_t offset)
+{
+	struct dcb_gpio_entry *gpio;
+	uint16_t ent = ROM16(bios->data[offset]);
+	uint8_t line = ent & 0x1f,
+		tag = ent >> 5 & 0x3f,
+		flags = ent >> 11 & 0x1f;
+
+	if (tag == 0x3f)
+		return;
+
+	gpio = new_gpio_entry(bios);
+
+	gpio->tag = tag;
+	gpio->line = line;
+	gpio->invert = flags != 4;
+}
+
+static void
+parse_dcb40_gpio_entry(struct nvbios *bios, uint16_t offset)
+{
+	struct dcb_gpio_entry *gpio;
+	uint32_t ent = ROM32(bios->data[offset]);
+	uint8_t line = ent & 0x1f,
+		tag = ent >> 8 & 0xff;
+
+	if (tag == 0xff)
+		return;
+
+	gpio = new_gpio_entry(bios);
+
+	/* Currently unused, we may need more fields parsed at some
+	 * point. */
+	gpio->tag = tag;
+	gpio->line = line;
+}
+
+static void
+parse_dcb_gpio_table(struct nvbios *bios)
+{
+	struct drm_device *dev = bios->dev;
+	uint16_t gpio_table_ptr = bios->bdcb.gpio_table_ptr;
+	uint8_t *gpio_table = &bios->data[gpio_table_ptr];
+	int header_len = gpio_table[1],
+	    entries = gpio_table[2],
+	    entry_len = gpio_table[3];
+	void (*parse_entry)(struct nvbios *, uint16_t) = NULL;
+	int i;
+
+	if (bios->bdcb.version >= 0x40) {
+		if (gpio_table_ptr && entry_len != 4) {
+			NV_WARN(dev, "Invalid DCB GPIO table entry length.\n");
+			return;
+		}
+
+		parse_entry = parse_dcb40_gpio_entry;
+
+	} else if (bios->bdcb.version >= 0x30) {
+		if (gpio_table_ptr && entry_len != 2) {
+			NV_WARN(dev, "Invalid DCB GPIO table entry length.\n");
+			return;
+		}
+
+		parse_entry = parse_dcb30_gpio_entry;
+
+	} else if (bios->bdcb.version >= 0x22) {
+		/*
+		 * DCBs older than v3.0 don't really have a GPIO
+		 * table, instead they keep some GPIO info at fixed
+		 * locations.
+		 */
+		uint16_t dcbptr = ROM16(bios->data[0x36]);
+		uint8_t *tvdac_gpio = &bios->data[dcbptr - 5];
+
+		if (tvdac_gpio[0] & 1) {
+			struct dcb_gpio_entry *gpio = new_gpio_entry(bios);
+
+			gpio->tag = DCB_GPIO_TVDAC0;
+			gpio->line = tvdac_gpio[1] >> 4;
+			gpio->invert = tvdac_gpio[0] & 2;
+		}
+	}
+
+	if (!gpio_table_ptr)
+		return;
+
+	if (entries > DCB_MAX_NUM_GPIO_ENTRIES) {
+		NV_WARN(dev, "Too many entries in the DCB GPIO table.\n");
+		entries = DCB_MAX_NUM_GPIO_ENTRIES;
+	}
+
+	for (i = 0; i < entries; i++)
+		parse_entry(bios, gpio_table_ptr + header_len + entry_len * i);
+}
+
+struct dcb_connector_table_entry *
+nouveau_bios_connector_entry(struct drm_device *dev, int index)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nvbios *bios = &dev_priv->VBIOS;
+	struct dcb_connector_table_entry *cte;
+
+	if (index >= bios->bdcb.connector.entries)
+		return NULL;
+
+	cte = &bios->bdcb.connector.entry[index];
+	if (cte->type == 0xff)
+		return NULL;
+
+	return cte;
+}
+
+static void
+parse_dcb_connector_table(struct nvbios *bios)
+{
+	struct drm_device *dev = bios->dev;
+	struct dcb_connector_table *ct = &bios->bdcb.connector;
+	struct dcb_connector_table_entry *cte;
+	uint8_t *conntab = &bios->data[bios->bdcb.connector_table_ptr];
+	uint8_t *entry;
+	int i;
+
+	if (!bios->bdcb.connector_table_ptr) {
+		NV_DEBUG(dev, "No DCB connector table present\n");
+		return;
+	}
+
+	NV_INFO(dev, "DCB connector table: VHER 0x%02x %d %d %d\n",
+		conntab[0], conntab[1], conntab[2], conntab[3]);
+	if ((conntab[0] != 0x30 && conntab[0] != 0x40) ||
+	    (conntab[3] != 2 && conntab[3] != 4)) {
+		NV_ERROR(dev, "  Unknown!  Please report.\n");
+		return;
+	}
+
+	ct->entries = conntab[2];
+
+	entry = conntab + conntab[1];
+	cte = &ct->entry[0];
+	for (i = 0; i < conntab[2]; i++, entry += conntab[3], cte++) {
+		if (conntab[3] == 2)
+			cte->entry = ROM16(entry[0]);
+		else
+			cte->entry = ROM32(entry[0]);
+		cte->type  = (cte->entry & 0x000000ff) >> 0;
+		cte->index = (cte->entry & 0x00000f00) >> 8;
+		switch (cte->entry & 0x00033000) {
+		case 0x00001000:
+			cte->gpio_tag = 0x07;
+			break;
+		case 0x00002000:
+			cte->gpio_tag = 0x08;
+			break;
+		case 0x00010000:
+			cte->gpio_tag = 0x51;
+			break;
+		case 0x00020000:
+			cte->gpio_tag = 0x52;
+			break;
+		default:
+			cte->gpio_tag = 0xff;
+			break;
+		}
+
+		if (cte->type == 0xff)
+			continue;
+
+		NV_INFO(dev, "  %d: 0x%08x: type 0x%02x idx %d tag 0x%02x\n",
+			i, cte->entry, cte->type, cte->index, cte->gpio_tag);
+	}
+}
+
+static struct dcb_entry *new_dcb_entry(struct parsed_dcb *dcb)
+{
+	struct dcb_entry *entry = &dcb->entry[dcb->entries];
+
+	memset(entry, 0, sizeof(struct dcb_entry));
+	entry->index = dcb->entries++;
+
+	return entry;
+}
+
+static void fabricate_vga_output(struct parsed_dcb *dcb, int i2c, int heads)
+{
+	struct dcb_entry *entry = new_dcb_entry(dcb);
+
+	entry->type = 0;
+	entry->i2c_index = i2c;
+	entry->heads = heads;
+	entry->location = DCB_LOC_ON_CHIP;
+	/* "or" mostly unused in early gen crt modesetting, 0 is fine */
+}
+
+static void fabricate_dvi_i_output(struct parsed_dcb *dcb, bool twoHeads)
+{
+	struct dcb_entry *entry = new_dcb_entry(dcb);
+
+	entry->type = 2;
+	entry->i2c_index = LEGACY_I2C_PANEL;
+	entry->heads = twoHeads ? 3 : 1;
+	entry->location = !DCB_LOC_ON_CHIP;	/* ie OFF CHIP */
+	entry->or = 1;	/* means |0x10 gets set on CRE_LCD__INDEX */
+	entry->duallink_possible = false; /* SiI164 and co. are single link */
+
+#if 0
+	/*
+	 * For dvi-a either crtc probably works, but my card appears to only
+	 * support dvi-d.  "nvidia" still attempts to program it for dvi-a,
+	 * doing the full fp output setup (program 0x6808.. fp dimension regs,
+	 * setting 0x680848 to 0x10000111 to enable, maybe setting 0x680880);
+	 * the monitor picks up the mode res ok and lights up, but no pixel
+	 * data appears, so the board manufacturer probably connected up the
+	 * sync lines, but missed the video traces / components
+	 *
+	 * with this introduction, dvi-a left as an exercise for the reader.
+	 */
+	fabricate_vga_output(dcb, LEGACY_I2C_PANEL, entry->heads);
+#endif
+}
+
+static void fabricate_tv_output(struct parsed_dcb *dcb, bool twoHeads)
+{
+	struct dcb_entry *entry = new_dcb_entry(dcb);
+
+	entry->type = 1;
+	entry->i2c_index = LEGACY_I2C_TV;
+	entry->heads = twoHeads ? 3 : 1;
+	entry->location = !DCB_LOC_ON_CHIP;	/* ie OFF CHIP */
+}
+
+static bool
+parse_dcb20_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb,
+		  uint32_t conn, uint32_t conf, struct dcb_entry *entry)
+{
+	entry->type = conn & 0xf;
+	entry->i2c_index = (conn >> 4) & 0xf;
+	entry->heads = (conn >> 8) & 0xf;
+	if (bdcb->version >= 0x40)
+		entry->connector = (conn >> 12) & 0xf;
+	entry->bus = (conn >> 16) & 0xf;
+	entry->location = (conn >> 20) & 0x3;
+	entry->or = (conn >> 24) & 0xf;
+	/*
+	 * Normal entries consist of a single bit, but dual link has the
+	 * next most significant bit set too
+	 */
+	entry->duallink_possible =
+			((1 << (ffs(entry->or) - 1)) * 3 == entry->or);
+
+	switch (entry->type) {
+	case OUTPUT_ANALOG:
+		/*
+		 * Although the rest of a CRT conf dword is usually
+		 * zeros, mac biosen have stuff there so we must mask
+		 */
+		entry->crtconf.maxfreq = (bdcb->version < 0x30) ?
+					 (conf & 0xffff) * 10 :
+					 (conf & 0xff) * 10000;
+		break;
+	case OUTPUT_LVDS:
+		{
+		uint32_t mask;
+		if (conf & 0x1)
+			entry->lvdsconf.use_straps_for_mode = true;
+		if (bdcb->version < 0x22) {
+			mask = ~0xd;
+			/*
+			 * The laptop in bug 14567 lies and claims to not use
+			 * straps when it does, so assume all DCB 2.0 laptops
+			 * use straps, until a broken EDID using one is produced
+			 */
+			entry->lvdsconf.use_straps_for_mode = true;
+			/*
+			 * Both 0x4 and 0x8 show up in v2.0 tables; assume they
+			 * mean the same thing (probably wrong, but might work)
+			 */
+			if (conf & 0x4 || conf & 0x8)
+				entry->lvdsconf.use_power_scripts = true;
+		} else {
+			mask = ~0x5;
+			if (conf & 0x4)
+				entry->lvdsconf.use_power_scripts = true;
+		}
+		if (conf & mask) {
+			/*
+			 * Until we even try to use these on G8x, it's
+			 * useless reporting unknown bits.  They all are.
+			 */
+			if (bdcb->version >= 0x40)
+				break;
+
+			NV_ERROR(dev, "Unknown LVDS configuration bits, "
+				      "please report\n");
+		}
+		break;
+		}
+	case OUTPUT_TV:
+	{
+		if (bdcb->version >= 0x30)
+			entry->tvconf.has_component_output = conf & (0x8 << 4);
+		else
+			entry->tvconf.has_component_output = false;
+
+		break;
+	}
+	case OUTPUT_DP:
+		entry->dpconf.sor.link = (conf & 0x00000030) >> 4;
+		entry->dpconf.link_bw = (conf & 0x00e00000) >> 21;
+		switch ((conf & 0x0f000000) >> 24) {
+		case 0xf:
+			entry->dpconf.link_nr = 4;
+			break;
+		case 0x3:
+			entry->dpconf.link_nr = 2;
+			break;
+		default:
+			entry->dpconf.link_nr = 1;
+			break;
+		}
+		break;
+	case OUTPUT_TMDS:
+		entry->tmdsconf.sor.link = (conf & 0x00000030) >> 4;
+		break;
+	case 0xe:
+		/* weird g80 mobile type that "nv" treats as a terminator */
+		bdcb->dcb.entries--;
+		return false;
+	}
+
+	/* unsure what DCB version introduces this, 3.0? */
+	if (conf & 0x100000)
+		entry->i2c_upper_default = true;
+
+	return true;
+}
+
+static bool
+parse_dcb15_entry(struct drm_device *dev, struct parsed_dcb *dcb,
+		  uint32_t conn, uint32_t conf, struct dcb_entry *entry)
+{
+	if (conn != 0xf0003f00 && conn != 0xf2247f10 && conn != 0xf2204001 &&
+	    conn != 0xf2204301 && conn != 0xf2204311 && conn != 0xf2208001 &&
+	    conn != 0xf2244001 && conn != 0xf2244301 && conn != 0xf2244311 &&
+	    conn != 0xf4204011 && conn != 0xf4208011 && conn != 0xf4248011 &&
+	    conn != 0xf2045ff2 && conn != 0xf2045f14 && conn != 0xf207df14 &&
+	    conn != 0xf2205004 && conn != 0xf2209004) {
+		NV_ERROR(dev, "Unknown DCB 1.5 entry, please report\n");
+
+		/* cause output setting to fail for !TV, so message is seen */
+		if ((conn & 0xf) != 0x1)
+			dcb->entries = 0;
+
+		return false;
+	}
+	/* most of the below is a "best guess" atm */
+	entry->type = conn & 0xf;
+	if (entry->type == 2)
+		/* another way of specifying straps based lvds... */
+		entry->type = OUTPUT_LVDS;
+	if (entry->type == 4) { /* digital */
+		if (conn & 0x10)
+			entry->type = OUTPUT_LVDS;
+		else
+			entry->type = OUTPUT_TMDS;
+	}
+	/* what's in bits 5-13? could be some encoder maker thing, in tv case */
+	entry->i2c_index = (conn >> 14) & 0xf;
+	/* raw heads field is in range 0-1, so move to 1-2 */
+	entry->heads = ((conn >> 18) & 0x7) + 1;
+	entry->location = (conn >> 21) & 0xf;
+	/* unused: entry->bus = (conn >> 25) & 0x7; */
+	/* set or to be same as heads -- hopefully safe enough */
+	entry->or = entry->heads;
+	entry->duallink_possible = false;
+
+	switch (entry->type) {
+	case OUTPUT_ANALOG:
+		entry->crtconf.maxfreq = (conf & 0xffff) * 10;
+		break;
+	case OUTPUT_LVDS:
+		/*
+		 * This is probably buried in conn's unknown bits.
+		 * This will upset EDID-ful models, if they exist
+		 */
+		entry->lvdsconf.use_straps_for_mode = true;
+		entry->lvdsconf.use_power_scripts = true;
+		break;
+	case OUTPUT_TMDS:
+		/*
+		 * Invent a DVI-A output, by copying the fields of the DVI-D
+		 * output; reported to work by math_b on an NV20(!).
+		 */
+		fabricate_vga_output(dcb, entry->i2c_index, entry->heads);
+		break;
+	case OUTPUT_TV:
+		entry->tvconf.has_component_output = false;
+		break;
+	}
+
+	return true;
+}
+
+static bool parse_dcb_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb,
+			    uint32_t conn, uint32_t conf)
+{
+	struct dcb_entry *entry = new_dcb_entry(&bdcb->dcb);
+	bool ret;
+
+	if (bdcb->version >= 0x20)
+		ret = parse_dcb20_entry(dev, bdcb, conn, conf, entry);
+	else
+		ret = parse_dcb15_entry(dev, &bdcb->dcb, conn, conf, entry);
+	if (!ret)
+		return ret;
+
+	read_dcb_i2c_entry(dev, bdcb->version, bdcb->i2c_table,
+			   entry->i2c_index, &bdcb->dcb.i2c[entry->i2c_index]);
+
+	return true;
+}
+
+static
+void merge_like_dcb_entries(struct drm_device *dev, struct parsed_dcb *dcb)
+{
+	/*
+	 * DCB v2.0 lists each output combination separately.
+	 * Here we merge compatible entries to have fewer outputs, with
+	 * more options
+	 */
+
+	int i, newentries = 0;
+
+	for (i = 0; i < dcb->entries; i++) {
+		struct dcb_entry *ient = &dcb->entry[i];
+		int j;
+
+		for (j = i + 1; j < dcb->entries; j++) {
+			struct dcb_entry *jent = &dcb->entry[j];
+
+			if (jent->type == 100) /* already merged entry */
+				continue;
+
+			/* merge heads field when all other fields the same */
+			if (jent->i2c_index == ient->i2c_index &&
+			    jent->type == ient->type &&
+			    jent->location == ient->location &&
+			    jent->or == ient->or) {
+				NV_TRACE(dev, "Merging DCB entries %d and %d\n",
+					 i, j);
+				ient->heads |= jent->heads;
+				jent->type = 100; /* dummy value */
+			}
+		}
+	}
+
+	/* Compact entries merged into others out of dcb */
+	for (i = 0; i < dcb->entries; i++) {
+		if (dcb->entry[i].type == 100)
+			continue;
+
+		if (newentries != i) {
+			dcb->entry[newentries] = dcb->entry[i];
+			dcb->entry[newentries].index = newentries;
+		}
+		newentries++;
+	}
+
+	dcb->entries = newentries;
+}
+
+static int parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads)
+{
+	struct bios_parsed_dcb *bdcb = &bios->bdcb;
+	struct parsed_dcb *dcb;
+	uint16_t dcbptr, i2ctabptr = 0;
+	uint8_t *dcbtable;
+	uint8_t headerlen = 0x4, entries = DCB_MAX_NUM_ENTRIES;
+	bool configblock = true;
+	int recordlength = 8, confofs = 4;
+	int i;
+
+	dcb = bios->pub.dcb = &bdcb->dcb;
+	dcb->entries = 0;
+
+	/* get the offset from 0x36 */
+	dcbptr = ROM16(bios->data[0x36]);
+
+	if (dcbptr == 0x0) {
+		NV_WARN(dev, "No output data (DCB) found in BIOS, "
+			       "assuming a CRT output exists\n");
+		/* this situation likely means a really old card, pre DCB */
+		fabricate_vga_output(dcb, LEGACY_I2C_CRT, 1);
+
+		if (nv04_tv_identify(dev,
+				     bios->legacy.i2c_indices.tv) >= 0)
+			fabricate_tv_output(dcb, twoHeads);
+
+		return 0;
+	}
+
+	dcbtable = &bios->data[dcbptr];
+
+	/* get DCB version */
+	bdcb->version = dcbtable[0];
+	NV_TRACE(dev, "Found Display Configuration Block version %d.%d\n",
+		 bdcb->version >> 4, bdcb->version & 0xf);
+
+	if (bdcb->version >= 0x20) { /* NV17+ */
+		uint32_t sig;
+
+		if (bdcb->version >= 0x30) { /* NV40+ */
+			headerlen = dcbtable[1];
+			entries = dcbtable[2];
+			recordlength = dcbtable[3];
+			i2ctabptr = ROM16(dcbtable[4]);
+			sig = ROM32(dcbtable[6]);
+			bdcb->gpio_table_ptr = ROM16(dcbtable[10]);
+			bdcb->connector_table_ptr = ROM16(dcbtable[20]);
+		} else {
+			i2ctabptr = ROM16(dcbtable[2]);
+			sig = ROM32(dcbtable[4]);
+			headerlen = 8;
+		}
+
+		if (sig != 0x4edcbdcb) {
+			NV_ERROR(dev, "Bad Display Configuration Block "
+					"signature (%08X)\n", sig);
+			return -EINVAL;
+		}
+	} else if (bdcb->version >= 0x15) { /* some NV11 and NV20 */
+		char sig[8] = { 0 };
+
+		strncpy(sig, (char *)&dcbtable[-7], 7);
+		i2ctabptr = ROM16(dcbtable[2]);
+		recordlength = 10;
+		confofs = 6;
+
+		if (strcmp(sig, "DEV_REC")) {
+			NV_ERROR(dev, "Bad Display Configuration Block "
+					"signature (%s)\n", sig);
+			return -EINVAL;
+		}
+	} else {
+		/*
+		 * v1.4 (some NV15/16, NV11+) seems the same as v1.5, but always
+		 * has the same single (crt) entry, even when tv-out present, so
+		 * the conclusion is this version cannot really be used.
+		 * v1.2 tables (some NV6/10, and NV15+) normally have the same
+		 * 5 entries, which are not specific to the card and so no use.
+		 * v1.2 does have an I2C table that read_dcb_i2c_table can
+		 * handle, but cards exist (nv11 in #14821) with a bad i2c table
+		 * pointer, so use the indices parsed in parse_bmp_structure.
+		 * v1.1 (NV5+, maybe some NV4) is entirely unhelpful
+		 */
+		NV_TRACEWARN(dev, "No useful information in BIOS output table; "
+				  "adding all possible outputs\n");
+		fabricate_vga_output(dcb, LEGACY_I2C_CRT, 1);
+
+		/*
+		 * Attempt to detect TV before DVI because the test
+		 * for the former is more accurate and it rules the
+		 * latter out.
+		 */
+		if (nv04_tv_identify(dev,
+				     bios->legacy.i2c_indices.tv) >= 0)
+			fabricate_tv_output(dcb, twoHeads);
+
+		else if (bios->tmds.output0_script_ptr ||
+			 bios->tmds.output1_script_ptr)
+			fabricate_dvi_i_output(dcb, twoHeads);
+
+		return 0;
+	}
+
+	if (!i2ctabptr)
+		NV_WARN(dev, "No pointer to DCB I2C port table\n");
+	else {
+		bdcb->i2c_table = &bios->data[i2ctabptr];
+		if (bdcb->version >= 0x30)
+			bdcb->i2c_default_indices = bdcb->i2c_table[4];
+	}
+
+	parse_dcb_gpio_table(bios);
+	parse_dcb_connector_table(bios);
+
+	if (entries > DCB_MAX_NUM_ENTRIES)
+		entries = DCB_MAX_NUM_ENTRIES;
+
+	for (i = 0; i < entries; i++) {
+		uint32_t connection, config = 0;
+
+		connection = ROM32(dcbtable[headerlen + recordlength * i]);
+		if (configblock)
+			config = ROM32(dcbtable[headerlen + confofs + recordlength * i]);
+
+		/* seen on an NV11 with DCB v1.5 */
+		if (connection == 0x00000000)
+			break;
+
+		/* seen on an NV17 with DCB v2.0 */
+		if (connection == 0xffffffff)
+			break;
+
+		if ((connection & 0x0000000f) == 0x0000000f)
+			continue;
+
+		NV_TRACEWARN(dev, "Raw DCB entry %d: %08x %08x\n",
+			     dcb->entries, connection, config);
+
+		if (!parse_dcb_entry(dev, bdcb, connection, config))
+			break;
+	}
+
+	/*
+	 * apart for v2.1+ not being known for requiring merging, this
+	 * guarantees dcbent->index is the index of the entry in the rom image
+	 */
+	if (bdcb->version < 0x21)
+		merge_like_dcb_entries(dev, dcb);
+
+	return dcb->entries ? 0 : -ENXIO;
+}
+
+static void
+fixup_legacy_connector(struct nvbios *bios)
+{
+	struct bios_parsed_dcb *bdcb = &bios->bdcb;
+	struct parsed_dcb *dcb = &bdcb->dcb;
+	int high = 0, i;
+
+	/*
+	 * DCB 3.0 also has the table in most cases, but there are some cards
+	 * where the table is filled with stub entries, and the DCB entriy
+	 * indices are all 0.  We don't need the connector indices on pre-G80
+	 * chips (yet?) so limit the use to DCB 4.0 and above.
+	 */
+	if (bdcb->version >= 0x40)
+		return;
+
+	/*
+	 * No known connector info before v3.0, so make it up.  the rule here
+	 * is: anything on the same i2c bus is considered to be on the same
+	 * connector.  any output without an associated i2c bus is assigned
+	 * its own unique connector index.
+	 */
+	for (i = 0; i < dcb->entries; i++) {
+		if (dcb->entry[i].i2c_index == 0xf)
+			continue;
+
+		/*
+		 * Ignore the I2C index for on-chip TV-out, as there
+		 * are cards with bogus values (nv31m in bug 23212),
+		 * and it's otherwise useless.
+		 */
+		if (dcb->entry[i].type == OUTPUT_TV &&
+		    dcb->entry[i].location == DCB_LOC_ON_CHIP) {
+			dcb->entry[i].i2c_index = 0xf;
+			continue;
+		}
+
+		dcb->entry[i].connector = dcb->entry[i].i2c_index;
+		if (dcb->entry[i].connector > high)
+			high = dcb->entry[i].connector;
+	}
+
+	for (i = 0; i < dcb->entries; i++) {
+		if (dcb->entry[i].i2c_index != 0xf)
+			continue;
+
+		dcb->entry[i].connector = ++high;
+	}
+}
+
+static void
+fixup_legacy_i2c(struct nvbios *bios)
+{
+	struct parsed_dcb *dcb = &bios->bdcb.dcb;
+	int i;
+
+	for (i = 0; i < dcb->entries; i++) {
+		if (dcb->entry[i].i2c_index == LEGACY_I2C_CRT)
+			dcb->entry[i].i2c_index = bios->legacy.i2c_indices.crt;
+		if (dcb->entry[i].i2c_index == LEGACY_I2C_PANEL)
+			dcb->entry[i].i2c_index = bios->legacy.i2c_indices.panel;
+		if (dcb->entry[i].i2c_index == LEGACY_I2C_TV)
+			dcb->entry[i].i2c_index = bios->legacy.i2c_indices.tv;
+	}
+}
+
+static int load_nv17_hwsq_ucode_entry(struct drm_device *dev, struct nvbios *bios, uint16_t hwsq_offset, int entry)
+{
+	/*
+	 * The header following the "HWSQ" signature has the number of entries,
+	 * and the entry size
+	 *
+	 * An entry consists of a dword to write to the sequencer control reg
+	 * (0x00001304), followed by the ucode bytes, written sequentially,
+	 * starting at reg 0x00001400
+	 */
+
+	uint8_t bytes_to_write;
+	uint16_t hwsq_entry_offset;
+	int i;
+
+	if (bios->data[hwsq_offset] <= entry) {
+		NV_ERROR(dev, "Too few entries in HW sequencer table for "
+				"requested entry\n");
+		return -ENOENT;
+	}
+
+	bytes_to_write = bios->data[hwsq_offset + 1];
+
+	if (bytes_to_write != 36) {
+		NV_ERROR(dev, "Unknown HW sequencer entry size\n");
+		return -EINVAL;
+	}
+
+	NV_TRACE(dev, "Loading NV17 power sequencing microcode\n");
+
+	hwsq_entry_offset = hwsq_offset + 2 + entry * bytes_to_write;
+
+	/* set sequencer control */
+	bios_wr32(bios, 0x00001304, ROM32(bios->data[hwsq_entry_offset]));
+	bytes_to_write -= 4;
+
+	/* write ucode */
+	for (i = 0; i < bytes_to_write; i += 4)
+		bios_wr32(bios, 0x00001400 + i, ROM32(bios->data[hwsq_entry_offset + i + 4]));
+
+	/* twiddle NV_PBUS_DEBUG_4 */
+	bios_wr32(bios, NV_PBUS_DEBUG_4, bios_rd32(bios, NV_PBUS_DEBUG_4) | 0x18);
+
+	return 0;
+}
+
+static int load_nv17_hw_sequencer_ucode(struct drm_device *dev,
+					struct nvbios *bios)
+{
+	/*
+	 * BMP based cards, from NV17, need a microcode loading to correctly
+	 * control the GPIO etc for LVDS panels
+	 *
+	 * BIT based cards seem to do this directly in the init scripts
+	 *
+	 * The microcode entries are found by the "HWSQ" signature.
+	 */
+
+	const uint8_t hwsq_signature[] = { 'H', 'W', 'S', 'Q' };
+	const int sz = sizeof(hwsq_signature);
+	int hwsq_offset;
+
+	hwsq_offset = findstr(bios->data, bios->length, hwsq_signature, sz);
+	if (!hwsq_offset)
+		return 0;
+
+	/* always use entry 0? */
+	return load_nv17_hwsq_ucode_entry(dev, bios, hwsq_offset + sz, 0);
+}
+
+uint8_t *nouveau_bios_embedded_edid(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nvbios *bios = &dev_priv->VBIOS;
+	const uint8_t edid_sig[] = {
+			0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 };
+	uint16_t offset = 0;
+	uint16_t newoffset;
+	int searchlen = NV_PROM_SIZE;
+
+	if (bios->fp.edid)
+		return bios->fp.edid;
+
+	while (searchlen) {
+		newoffset = findstr(&bios->data[offset], searchlen,
+								edid_sig, 8);
+		if (!newoffset)
+			return NULL;
+		offset += newoffset;
+		if (!nv_cksum(&bios->data[offset], EDID1_LEN))
+			break;
+
+		searchlen -= offset;
+		offset++;
+	}
+
+	NV_TRACE(dev, "Found EDID in BIOS\n");
+
+	return bios->fp.edid = &bios->data[offset];
+}
+
+void
+nouveau_bios_run_init_table(struct drm_device *dev, uint16_t table,
+			    struct dcb_entry *dcbent)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nvbios *bios = &dev_priv->VBIOS;
+	struct init_exec iexec = { true, false };
+
+	bios->display.output = dcbent;
+	parse_init_table(bios, table, &iexec);
+	bios->display.output = NULL;
+}
+
+static bool NVInitVBIOS(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nvbios *bios = &dev_priv->VBIOS;
+
+	memset(bios, 0, sizeof(struct nvbios));
+	bios->dev = dev;
+
+	if (!NVShadowVBIOS(dev, bios->data))
+		return false;
+
+	bios->length = NV_PROM_SIZE;
+	return true;
+}
+
+static int nouveau_parse_vbios_struct(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nvbios *bios = &dev_priv->VBIOS;
+	const uint8_t bit_signature[] = { 0xff, 0xb8, 'B', 'I', 'T' };
+	const uint8_t bmp_signature[] = { 0xff, 0x7f, 'N', 'V', 0x0 };
+	int offset;
+
+	offset = findstr(bios->data, bios->length,
+					bit_signature, sizeof(bit_signature));
+	if (offset) {
+		NV_TRACE(dev, "BIT BIOS found\n");
+		return parse_bit_structure(bios, offset + 6);
+	}
+
+	offset = findstr(bios->data, bios->length,
+					bmp_signature, sizeof(bmp_signature));
+	if (offset) {
+		NV_TRACE(dev, "BMP BIOS found\n");
+		return parse_bmp_structure(dev, bios, offset);
+	}
+
+	NV_ERROR(dev, "No known BIOS signature found\n");
+	return -ENODEV;
+}
+
+int
+nouveau_run_vbios_init(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nvbios *bios = &dev_priv->VBIOS;
+	int i, ret = 0;
+
+	NVLockVgaCrtcs(dev, false);
+	if (nv_two_heads(dev))
+		NVSetOwner(dev, bios->state.crtchead);
+
+	if (bios->major_version < 5)	/* BMP only */
+		load_nv17_hw_sequencer_ucode(dev, bios);
+
+	if (bios->execute) {
+		bios->fp.last_script_invoc = 0;
+		bios->fp.lvds_init_run = false;
+	}
+
+	parse_init_tables(bios);
+
+	/*
+	 * Runs some additional script seen on G8x VBIOSen.  The VBIOS'
+	 * parser will run this right after the init tables, the binary
+	 * driver appears to run it at some point later.
+	 */
+	if (bios->some_script_ptr) {
+		struct init_exec iexec = {true, false};
+
+		NV_INFO(dev, "Parsing VBIOS init table at offset 0x%04X\n",
+			bios->some_script_ptr);
+		parse_init_table(bios, bios->some_script_ptr, &iexec);
+	}
+
+	if (dev_priv->card_type >= NV_50) {
+		for (i = 0; i < bios->bdcb.dcb.entries; i++) {
+			nouveau_bios_run_display_table(dev,
+						       &bios->bdcb.dcb.entry[i],
+						       0, 0);
+		}
+	}
+
+	NVLockVgaCrtcs(dev, true);
+
+	return ret;
+}
+
+static void
+nouveau_bios_i2c_devices_takedown(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nvbios *bios = &dev_priv->VBIOS;
+	struct dcb_i2c_entry *entry;
+	int i;
+
+	entry = &bios->bdcb.dcb.i2c[0];
+	for (i = 0; i < DCB_MAX_NUM_I2C_ENTRIES; i++, entry++)
+		nouveau_i2c_fini(dev, entry);
+}
+
+int
+nouveau_bios_init(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nvbios *bios = &dev_priv->VBIOS;
+	uint32_t saved_nv_pextdev_boot_0;
+	bool was_locked;
+	int ret;
+
+	dev_priv->vbios = &bios->pub;
+
+	if (!NVInitVBIOS(dev))
+		return -ENODEV;
+
+	ret = nouveau_parse_vbios_struct(dev);
+	if (ret)
+		return ret;
+
+	ret = parse_dcb_table(dev, bios, nv_two_heads(dev));
+	if (ret)
+		return ret;
+
+	fixup_legacy_i2c(bios);
+	fixup_legacy_connector(bios);
+
+	if (!bios->major_version)	/* we don't run version 0 bios */
+		return 0;
+
+	/* these will need remembering across a suspend */
+	saved_nv_pextdev_boot_0 = bios_rd32(bios, NV_PEXTDEV_BOOT_0);
+	bios->state.saved_nv_pfb_cfg0 = bios_rd32(bios, NV_PFB_CFG0);
+
+	/* init script execution disabled */
+	bios->execute = false;
+
+	/* ... unless card isn't POSTed already */
+	if (dev_priv->card_type >= NV_10 &&
+	    NVReadVgaCrtc(dev, 0, 0x00) == 0 &&
+	    NVReadVgaCrtc(dev, 0, 0x1a) == 0) {
+		NV_INFO(dev, "Adaptor not initialised\n");
+		if (dev_priv->card_type < NV_50) {
+			NV_ERROR(dev, "Unable to POST this chipset\n");
+			return -ENODEV;
+		}
+
+		NV_INFO(dev, "Running VBIOS init tables\n");
+		bios->execute = true;
+	}
+
+	bios_wr32(bios, NV_PEXTDEV_BOOT_0, saved_nv_pextdev_boot_0);
+
+	ret = nouveau_run_vbios_init(dev);
+	if (ret) {
+		dev_priv->vbios = NULL;
+		return ret;
+	}
+
+	/* feature_byte on BMP is poor, but init always sets CR4B */
+	was_locked = NVLockVgaCrtcs(dev, false);
+	if (bios->major_version < 5)
+		bios->is_mobile = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_4B) & 0x40;
+
+	/* all BIT systems need p_f_m_t for digital_min_front_porch */
+	if (bios->is_mobile || bios->major_version >= 5)
+		ret = parse_fp_mode_table(dev, bios);
+	NVLockVgaCrtcs(dev, was_locked);
+
+	/* allow subsequent scripts to execute */
+	bios->execute = true;
+
+	return 0;
+}
+
+void
+nouveau_bios_takedown(struct drm_device *dev)
+{
+	nouveau_bios_i2c_devices_takedown(dev);
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h
new file mode 100644
index 0000000..1d5f10b
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.h
@@ -0,0 +1,289 @@
+/*
+ * Copyright 2007-2008 Nouveau Project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __NOUVEAU_BIOS_H__
+#define __NOUVEAU_BIOS_H__
+
+#include "nvreg.h"
+#include "nouveau_i2c.h"
+
+#define DCB_MAX_NUM_ENTRIES 16
+#define DCB_MAX_NUM_I2C_ENTRIES 16
+#define DCB_MAX_NUM_GPIO_ENTRIES 32
+#define DCB_MAX_NUM_CONNECTOR_ENTRIES 16
+
+#define DCB_LOC_ON_CHIP 0
+
+struct dcb_entry {
+	int index;	/* may not be raw dcb index if merging has happened */
+	uint8_t type;
+	uint8_t i2c_index;
+	uint8_t heads;
+	uint8_t connector;
+	uint8_t bus;
+	uint8_t location;
+	uint8_t or;
+	bool duallink_possible;
+	union {
+		struct sor_conf {
+			int link;
+		} sorconf;
+		struct {
+			int maxfreq;
+		} crtconf;
+		struct {
+			struct sor_conf sor;
+			bool use_straps_for_mode;
+			bool use_power_scripts;
+		} lvdsconf;
+		struct {
+			bool has_component_output;
+		} tvconf;
+		struct {
+			struct sor_conf sor;
+			int link_nr;
+			int link_bw;
+		} dpconf;
+		struct {
+			struct sor_conf sor;
+		} tmdsconf;
+	};
+	bool i2c_upper_default;
+};
+
+struct dcb_i2c_entry {
+	uint8_t port_type;
+	uint8_t read, write;
+	struct nouveau_i2c_chan *chan;
+};
+
+struct parsed_dcb {
+	int entries;
+	struct dcb_entry entry[DCB_MAX_NUM_ENTRIES];
+	struct dcb_i2c_entry i2c[DCB_MAX_NUM_I2C_ENTRIES];
+};
+
+enum dcb_gpio_tag {
+	DCB_GPIO_TVDAC0 = 0xc,
+	DCB_GPIO_TVDAC1 = 0x2d,
+};
+
+struct dcb_gpio_entry {
+	enum dcb_gpio_tag tag;
+	int line;
+	bool invert;
+};
+
+struct parsed_dcb_gpio {
+	int entries;
+	struct dcb_gpio_entry entry[DCB_MAX_NUM_GPIO_ENTRIES];
+};
+
+struct dcb_connector_table_entry {
+	uint32_t entry;
+	uint8_t type;
+	uint8_t index;
+	uint8_t gpio_tag;
+};
+
+struct dcb_connector_table {
+	int entries;
+	struct dcb_connector_table_entry entry[DCB_MAX_NUM_CONNECTOR_ENTRIES];
+};
+
+struct bios_parsed_dcb {
+	uint8_t version;
+
+	struct parsed_dcb dcb;
+
+	uint8_t *i2c_table;
+	uint8_t i2c_default_indices;
+
+	uint16_t gpio_table_ptr;
+	struct parsed_dcb_gpio gpio;
+	uint16_t connector_table_ptr;
+	struct dcb_connector_table connector;
+};
+
+enum nouveau_encoder_type {
+	OUTPUT_ANALOG = 0,
+	OUTPUT_TV = 1,
+	OUTPUT_TMDS = 2,
+	OUTPUT_LVDS = 3,
+	OUTPUT_DP = 6,
+	OUTPUT_ANY = -1
+};
+
+enum nouveau_or {
+	OUTPUT_A = (1 << 0),
+	OUTPUT_B = (1 << 1),
+	OUTPUT_C = (1 << 2)
+};
+
+enum LVDS_script {
+	/* Order *does* matter here */
+	LVDS_INIT = 1,
+	LVDS_RESET,
+	LVDS_BACKLIGHT_ON,
+	LVDS_BACKLIGHT_OFF,
+	LVDS_PANEL_ON,
+	LVDS_PANEL_OFF
+};
+
+/* changing these requires matching changes to reg tables in nv_get_clock */
+#define MAX_PLL_TYPES	4
+enum pll_types {
+	NVPLL,
+	MPLL,
+	VPLL1,
+	VPLL2
+};
+
+struct pll_lims {
+	struct {
+		int minfreq;
+		int maxfreq;
+		int min_inputfreq;
+		int max_inputfreq;
+
+		uint8_t min_m;
+		uint8_t max_m;
+		uint8_t min_n;
+		uint8_t max_n;
+	} vco1, vco2;
+
+	uint8_t max_log2p;
+	/*
+	 * for most pre nv50 cards setting a log2P of 7 (the common max_log2p
+	 * value) is no different to 6 (at least for vplls) so allowing the MNP
+	 * calc to use 7 causes the generated clock to be out by a factor of 2.
+	 * however, max_log2p cannot be fixed-up during parsing as the
+	 * unmodified max_log2p value is still needed for setting mplls, hence
+	 * an additional max_usable_log2p member
+	 */
+	uint8_t max_usable_log2p;
+	uint8_t log2p_bias;
+
+	uint8_t min_p;
+	uint8_t max_p;
+
+	int refclk;
+};
+
+struct nouveau_bios_info {
+	struct parsed_dcb *dcb;
+
+	uint8_t chip_version;
+
+	uint32_t dactestval;
+	uint32_t tvdactestval;
+	uint8_t digital_min_front_porch;
+	bool fp_no_ddc;
+};
+
+struct nvbios {
+	struct drm_device *dev;
+	struct nouveau_bios_info pub;
+
+	uint8_t data[NV_PROM_SIZE];
+	unsigned int length;
+	bool execute;
+
+	uint8_t major_version;
+	uint8_t feature_byte;
+	bool is_mobile;
+
+	uint32_t fmaxvco, fminvco;
+
+	bool old_style_init;
+	uint16_t init_script_tbls_ptr;
+	uint16_t extra_init_script_tbl_ptr;
+	uint16_t macro_index_tbl_ptr;
+	uint16_t macro_tbl_ptr;
+	uint16_t condition_tbl_ptr;
+	uint16_t io_condition_tbl_ptr;
+	uint16_t io_flag_condition_tbl_ptr;
+	uint16_t init_function_tbl_ptr;
+
+	uint16_t pll_limit_tbl_ptr;
+	uint16_t ram_restrict_tbl_ptr;
+
+	uint16_t some_script_ptr; /* BIT I + 14 */
+	uint16_t init96_tbl_ptr; /* BIT I + 16 */
+
+	struct bios_parsed_dcb bdcb;
+
+	struct {
+		int crtchead;
+		/* these need remembering across suspend */
+		uint32_t saved_nv_pfb_cfg0;
+	} state;
+
+	struct {
+		struct dcb_entry *output;
+		uint16_t script_table_ptr;
+		uint16_t dp_table_ptr;
+	} display;
+
+	struct {
+		uint16_t fptablepointer;	/* also used by tmds */
+		uint16_t fpxlatetableptr;
+		int xlatwidth;
+		uint16_t lvdsmanufacturerpointer;
+		uint16_t fpxlatemanufacturertableptr;
+		uint16_t mode_ptr;
+		uint16_t xlated_entry;
+		bool power_off_for_reset;
+		bool reset_after_pclk_change;
+		bool dual_link;
+		bool link_c_increment;
+		bool BITbit1;
+		bool if_is_24bit;
+		int duallink_transition_clk;
+		uint8_t strapless_is_24bit;
+		uint8_t *edid;
+
+		/* will need resetting after suspend */
+		int last_script_invoc;
+		bool lvds_init_run;
+	} fp;
+
+	struct {
+		uint16_t output0_script_ptr;
+		uint16_t output1_script_ptr;
+	} tmds;
+
+	struct {
+		uint16_t mem_init_tbl_ptr;
+		uint16_t sdr_seq_tbl_ptr;
+		uint16_t ddr_seq_tbl_ptr;
+
+		struct {
+			uint8_t crt, tv, panel;
+		} i2c_indices;
+
+		uint16_t lvds_single_a_script_ptr;
+	} legacy;
+};
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c
new file mode 100644
index 0000000..320a14b
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_bo.c
@@ -0,0 +1,671 @@
+/*
+ * Copyright 2007 Dave Airlied
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+/*
+ * Authors: Dave Airlied <airlied@linux.ie>
+ *	    Ben Skeggs   <darktama@iinet.net.au>
+ *	    Jeremy Kolb  <jkolb@brandeis.edu>
+ */
+
+#include "drmP.h"
+
+#include "nouveau_drm.h"
+#include "nouveau_drv.h"
+#include "nouveau_dma.h"
+
+static void
+nouveau_bo_del_ttm(struct ttm_buffer_object *bo)
+{
+	struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev);
+	struct nouveau_bo *nvbo = nouveau_bo(bo);
+
+	ttm_bo_kunmap(&nvbo->kmap);
+
+	if (unlikely(nvbo->gem))
+		DRM_ERROR("bo %p still attached to GEM object\n", bo);
+
+	spin_lock(&dev_priv->ttm.bo_list_lock);
+	list_del(&nvbo->head);
+	spin_unlock(&dev_priv->ttm.bo_list_lock);
+	kfree(nvbo);
+}
+
+int
+nouveau_bo_new(struct drm_device *dev, struct nouveau_channel *chan,
+	       int size, int align, uint32_t flags, uint32_t tile_mode,
+	       uint32_t tile_flags, bool no_vm, bool mappable,
+	       struct nouveau_bo **pnvbo)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_bo *nvbo;
+	int ret, n = 0;
+
+	nvbo = kzalloc(sizeof(struct nouveau_bo), GFP_KERNEL);
+	if (!nvbo)
+		return -ENOMEM;
+	INIT_LIST_HEAD(&nvbo->head);
+	INIT_LIST_HEAD(&nvbo->entry);
+	nvbo->mappable = mappable;
+	nvbo->no_vm = no_vm;
+	nvbo->tile_mode = tile_mode;
+	nvbo->tile_flags = tile_flags;
+
+	/*
+	 * Some of the tile_flags have a periodic structure of N*4096 bytes,
+	 * align to to that as well as the page size. Overallocate memory to
+	 * avoid corruption of other buffer objects.
+	 */
+	switch (tile_flags) {
+	case 0x1800:
+	case 0x2800:
+	case 0x4800:
+	case 0x7a00:
+		if (dev_priv->chipset >= 0xA0) {
+			/* This is based on high end cards with 448 bits
+			 * memory bus, could be different elsewhere.*/
+			size += 6 * 28672;
+			/* 8 * 28672 is the actual alignment requirement,
+			 * but we must also align to page size. */
+			align = 2 * 8 * 28672;
+		} else if (dev_priv->chipset >= 0x90) {
+			size += 3 * 16384;
+			align = 12 * 16834;
+		} else {
+			size += 3 * 8192;
+			/* 12 * 8192 is the actual alignment requirement,
+			 * but we must also align to page size. */
+			align = 2 * 12 * 8192;
+		}
+		break;
+	default:
+		break;
+	}
+
+	align >>= PAGE_SHIFT;
+
+	size = (size + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
+	if (dev_priv->card_type == NV_50) {
+		size = (size + 65535) & ~65535;
+		if (align < (65536 / PAGE_SIZE))
+			align = (65536 / PAGE_SIZE);
+	}
+
+	if (flags & TTM_PL_FLAG_VRAM)
+		nvbo->placements[n++] = TTM_PL_FLAG_VRAM | TTM_PL_MASK_CACHING;
+	if (flags & TTM_PL_FLAG_TT)
+		nvbo->placements[n++] = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING;
+	nvbo->placement.fpfn = 0;
+	nvbo->placement.lpfn = mappable ? dev_priv->fb_mappable_pages : 0;
+	nvbo->placement.placement = nvbo->placements;
+	nvbo->placement.busy_placement = nvbo->placements;
+	nvbo->placement.num_placement = n;
+	nvbo->placement.num_busy_placement = n;
+
+	nvbo->channel = chan;
+	nouveau_bo_placement_set(nvbo, flags);
+	ret = ttm_bo_init(&dev_priv->ttm.bdev, &nvbo->bo, size,
+			  ttm_bo_type_device, &nvbo->placement, align, 0,
+			  false, NULL, size, nouveau_bo_del_ttm);
+	nvbo->channel = NULL;
+	if (ret) {
+		/* ttm will call nouveau_bo_del_ttm if it fails.. */
+		return ret;
+	}
+
+	spin_lock(&dev_priv->ttm.bo_list_lock);
+	list_add_tail(&nvbo->head, &dev_priv->ttm.bo_list);
+	spin_unlock(&dev_priv->ttm.bo_list_lock);
+	*pnvbo = nvbo;
+	return 0;
+}
+
+void
+nouveau_bo_placement_set(struct nouveau_bo *nvbo, uint32_t memtype)
+{
+	int n = 0;
+
+	if (memtype & TTM_PL_FLAG_VRAM)
+		nvbo->placements[n++] = TTM_PL_FLAG_VRAM | TTM_PL_MASK_CACHING;
+	if (memtype & TTM_PL_FLAG_TT)
+		nvbo->placements[n++] = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING;
+	if (memtype & TTM_PL_FLAG_SYSTEM)
+		nvbo->placements[n++] = TTM_PL_FLAG_SYSTEM | TTM_PL_MASK_CACHING;
+	nvbo->placement.placement = nvbo->placements;
+	nvbo->placement.busy_placement = nvbo->placements;
+	nvbo->placement.num_placement = n;
+	nvbo->placement.num_busy_placement = n;
+}
+
+int
+nouveau_bo_pin(struct nouveau_bo *nvbo, uint32_t memtype)
+{
+	struct drm_nouveau_private *dev_priv = nouveau_bdev(nvbo->bo.bdev);
+	struct ttm_buffer_object *bo = &nvbo->bo;
+	int ret, i;
+
+	if (nvbo->pin_refcnt && !(memtype & (1 << bo->mem.mem_type))) {
+		NV_ERROR(nouveau_bdev(bo->bdev)->dev,
+			 "bo %p pinned elsewhere: 0x%08x vs 0x%08x\n", bo,
+			 1 << bo->mem.mem_type, memtype);
+		return -EINVAL;
+	}
+
+	if (nvbo->pin_refcnt++)
+		return 0;
+
+	ret = ttm_bo_reserve(bo, false, false, false, 0);
+	if (ret)
+		goto out;
+
+	nouveau_bo_placement_set(nvbo, memtype);
+	for (i = 0; i < nvbo->placement.num_placement; i++)
+		nvbo->placements[i] |= TTM_PL_FLAG_NO_EVICT;
+
+	ret = ttm_bo_validate(bo, &nvbo->placement, false, false);
+	if (ret == 0) {
+		switch (bo->mem.mem_type) {
+		case TTM_PL_VRAM:
+			dev_priv->fb_aper_free -= bo->mem.size;
+			break;
+		case TTM_PL_TT:
+			dev_priv->gart_info.aper_free -= bo->mem.size;
+			break;
+		default:
+			break;
+		}
+	}
+	ttm_bo_unreserve(bo);
+out:
+	if (unlikely(ret))
+		nvbo->pin_refcnt--;
+	return ret;
+}
+
+int
+nouveau_bo_unpin(struct nouveau_bo *nvbo)
+{
+	struct drm_nouveau_private *dev_priv = nouveau_bdev(nvbo->bo.bdev);
+	struct ttm_buffer_object *bo = &nvbo->bo;
+	int ret, i;
+
+	if (--nvbo->pin_refcnt)
+		return 0;
+
+	ret = ttm_bo_reserve(bo, false, false, false, 0);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < nvbo->placement.num_placement; i++)
+		nvbo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT;
+
+	ret = ttm_bo_validate(bo, &nvbo->placement, false, false);
+	if (ret == 0) {
+		switch (bo->mem.mem_type) {
+		case TTM_PL_VRAM:
+			dev_priv->fb_aper_free += bo->mem.size;
+			break;
+		case TTM_PL_TT:
+			dev_priv->gart_info.aper_free += bo->mem.size;
+			break;
+		default:
+			break;
+		}
+	}
+
+	ttm_bo_unreserve(bo);
+	return ret;
+}
+
+int
+nouveau_bo_map(struct nouveau_bo *nvbo)
+{
+	int ret;
+
+	ret = ttm_bo_reserve(&nvbo->bo, false, false, false, 0);
+	if (ret)
+		return ret;
+
+	ret = ttm_bo_kmap(&nvbo->bo, 0, nvbo->bo.mem.num_pages, &nvbo->kmap);
+	ttm_bo_unreserve(&nvbo->bo);
+	return ret;
+}
+
+void
+nouveau_bo_unmap(struct nouveau_bo *nvbo)
+{
+	ttm_bo_kunmap(&nvbo->kmap);
+}
+
+u16
+nouveau_bo_rd16(struct nouveau_bo *nvbo, unsigned index)
+{
+	bool is_iomem;
+	u16 *mem = ttm_kmap_obj_virtual(&nvbo->kmap, &is_iomem);
+	mem = &mem[index];
+	if (is_iomem)
+		return ioread16_native((void __force __iomem *)mem);
+	else
+		return *mem;
+}
+
+void
+nouveau_bo_wr16(struct nouveau_bo *nvbo, unsigned index, u16 val)
+{
+	bool is_iomem;
+	u16 *mem = ttm_kmap_obj_virtual(&nvbo->kmap, &is_iomem);
+	mem = &mem[index];
+	if (is_iomem)
+		iowrite16_native(val, (void __force __iomem *)mem);
+	else
+		*mem = val;
+}
+
+u32
+nouveau_bo_rd32(struct nouveau_bo *nvbo, unsigned index)
+{
+	bool is_iomem;
+	u32 *mem = ttm_kmap_obj_virtual(&nvbo->kmap, &is_iomem);
+	mem = &mem[index];
+	if (is_iomem)
+		return ioread32_native((void __force __iomem *)mem);
+	else
+		return *mem;
+}
+
+void
+nouveau_bo_wr32(struct nouveau_bo *nvbo, unsigned index, u32 val)
+{
+	bool is_iomem;
+	u32 *mem = ttm_kmap_obj_virtual(&nvbo->kmap, &is_iomem);
+	mem = &mem[index];
+	if (is_iomem)
+		iowrite32_native(val, (void __force __iomem *)mem);
+	else
+		*mem = val;
+}
+
+static struct ttm_backend *
+nouveau_bo_create_ttm_backend_entry(struct ttm_bo_device *bdev)
+{
+	struct drm_nouveau_private *dev_priv = nouveau_bdev(bdev);
+	struct drm_device *dev = dev_priv->dev;
+
+	switch (dev_priv->gart_info.type) {
+	case NOUVEAU_GART_AGP:
+		return ttm_agp_backend_init(bdev, dev->agp->bridge);
+	case NOUVEAU_GART_SGDMA:
+		return nouveau_sgdma_init_ttm(dev);
+	default:
+		NV_ERROR(dev, "Unknown GART type %d\n",
+			 dev_priv->gart_info.type);
+		break;
+	}
+
+	return NULL;
+}
+
+static int
+nouveau_bo_invalidate_caches(struct ttm_bo_device *bdev, uint32_t flags)
+{
+	/* We'll do this from user space. */
+	return 0;
+}
+
+static int
+nouveau_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
+			 struct ttm_mem_type_manager *man)
+{
+	struct drm_nouveau_private *dev_priv = nouveau_bdev(bdev);
+	struct drm_device *dev = dev_priv->dev;
+
+	switch (type) {
+	case TTM_PL_SYSTEM:
+		man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
+		man->available_caching = TTM_PL_MASK_CACHING;
+		man->default_caching = TTM_PL_FLAG_CACHED;
+		break;
+	case TTM_PL_VRAM:
+		man->flags = TTM_MEMTYPE_FLAG_FIXED |
+			     TTM_MEMTYPE_FLAG_MAPPABLE |
+			     TTM_MEMTYPE_FLAG_NEEDS_IOREMAP;
+		man->available_caching = TTM_PL_FLAG_UNCACHED |
+					 TTM_PL_FLAG_WC;
+		man->default_caching = TTM_PL_FLAG_WC;
+
+		man->io_addr = NULL;
+		man->io_offset = drm_get_resource_start(dev, 1);
+		man->io_size = drm_get_resource_len(dev, 1);
+		if (man->io_size > nouveau_mem_fb_amount(dev))
+			man->io_size = nouveau_mem_fb_amount(dev);
+
+		man->gpu_offset = dev_priv->vm_vram_base;
+		break;
+	case TTM_PL_TT:
+		switch (dev_priv->gart_info.type) {
+		case NOUVEAU_GART_AGP:
+			man->flags = TTM_MEMTYPE_FLAG_MAPPABLE |
+				     TTM_MEMTYPE_FLAG_NEEDS_IOREMAP;
+			man->available_caching = TTM_PL_FLAG_UNCACHED;
+			man->default_caching = TTM_PL_FLAG_UNCACHED;
+			break;
+		case NOUVEAU_GART_SGDMA:
+			man->flags = TTM_MEMTYPE_FLAG_MAPPABLE |
+				     TTM_MEMTYPE_FLAG_CMA;
+			man->available_caching = TTM_PL_MASK_CACHING;
+			man->default_caching = TTM_PL_FLAG_CACHED;
+			break;
+		default:
+			NV_ERROR(dev, "Unknown GART type: %d\n",
+				 dev_priv->gart_info.type);
+			return -EINVAL;
+		}
+
+		man->io_offset  = dev_priv->gart_info.aper_base;
+		man->io_size    = dev_priv->gart_info.aper_size;
+		man->io_addr   = NULL;
+		man->gpu_offset = dev_priv->vm_gart_base;
+		break;
+	default:
+		NV_ERROR(dev, "Unsupported memory type %u\n", (unsigned)type);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static void
+nouveau_bo_evict_flags(struct ttm_buffer_object *bo, struct ttm_placement *pl)
+{
+	struct nouveau_bo *nvbo = nouveau_bo(bo);
+
+	switch (bo->mem.mem_type) {
+	default:
+		nouveau_bo_placement_set(nvbo, TTM_PL_FLAG_SYSTEM);
+		break;
+	}
+}
+
+
+/* GPU-assisted copy using NV_MEMORY_TO_MEMORY_FORMAT, can access
+ * TTM_PL_{VRAM,TT} directly.
+ */
+static int
+nouveau_bo_move_accel_cleanup(struct nouveau_channel *chan,
+			      struct nouveau_bo *nvbo, bool evict, bool no_wait,
+			      struct ttm_mem_reg *new_mem)
+{
+	struct nouveau_fence *fence = NULL;
+	int ret;
+
+	ret = nouveau_fence_new(chan, &fence, true);
+	if (ret)
+		return ret;
+
+	ret = ttm_bo_move_accel_cleanup(&nvbo->bo, fence, NULL,
+					evict, no_wait, new_mem);
+	nouveau_fence_unref((void *)&fence);
+	return ret;
+}
+
+static inline uint32_t
+nouveau_bo_mem_ctxdma(struct nouveau_bo *nvbo, struct nouveau_channel *chan,
+		      struct ttm_mem_reg *mem)
+{
+	if (chan == nouveau_bdev(nvbo->bo.bdev)->channel) {
+		if (mem->mem_type == TTM_PL_TT)
+			return NvDmaGART;
+		return NvDmaVRAM;
+	}
+
+	if (mem->mem_type == TTM_PL_TT)
+		return chan->gart_handle;
+	return chan->vram_handle;
+}
+
+static int
+nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, int no_wait,
+		     struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem)
+{
+	struct nouveau_bo *nvbo = nouveau_bo(bo);
+	struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev);
+	struct nouveau_channel *chan;
+	uint64_t src_offset, dst_offset;
+	uint32_t page_count;
+	int ret;
+
+	chan = nvbo->channel;
+	if (!chan || nvbo->tile_flags || nvbo->no_vm) {
+		chan = dev_priv->channel;
+		if (!chan)
+			return -EINVAL;
+	}
+
+	src_offset = old_mem->mm_node->start << PAGE_SHIFT;
+	dst_offset = new_mem->mm_node->start << PAGE_SHIFT;
+	if (chan != dev_priv->channel) {
+		if (old_mem->mem_type == TTM_PL_TT)
+			src_offset += dev_priv->vm_gart_base;
+		else
+			src_offset += dev_priv->vm_vram_base;
+
+		if (new_mem->mem_type == TTM_PL_TT)
+			dst_offset += dev_priv->vm_gart_base;
+		else
+			dst_offset += dev_priv->vm_vram_base;
+	}
+
+	ret = RING_SPACE(chan, 3);
+	if (ret)
+		return ret;
+	BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_DMA_SOURCE, 2);
+	OUT_RING(chan, nouveau_bo_mem_ctxdma(nvbo, chan, old_mem));
+	OUT_RING(chan, nouveau_bo_mem_ctxdma(nvbo, chan, new_mem));
+
+	if (dev_priv->card_type >= NV_50) {
+		ret = RING_SPACE(chan, 4);
+		if (ret)
+			return ret;
+		BEGIN_RING(chan, NvSubM2MF, 0x0200, 1);
+		OUT_RING(chan, 1);
+		BEGIN_RING(chan, NvSubM2MF, 0x021c, 1);
+		OUT_RING(chan, 1);
+	}
+
+	page_count = new_mem->num_pages;
+	while (page_count) {
+		int line_count = (page_count > 2047) ? 2047 : page_count;
+
+		if (dev_priv->card_type >= NV_50) {
+			ret = RING_SPACE(chan, 3);
+			if (ret)
+				return ret;
+			BEGIN_RING(chan, NvSubM2MF, 0x0238, 2);
+			OUT_RING(chan, upper_32_bits(src_offset));
+			OUT_RING(chan, upper_32_bits(dst_offset));
+		}
+		ret = RING_SPACE(chan, 11);
+		if (ret)
+			return ret;
+		BEGIN_RING(chan, NvSubM2MF,
+				 NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8);
+		OUT_RING(chan, lower_32_bits(src_offset));
+		OUT_RING(chan, lower_32_bits(dst_offset));
+		OUT_RING(chan, PAGE_SIZE); /* src_pitch */
+		OUT_RING(chan, PAGE_SIZE); /* dst_pitch */
+		OUT_RING(chan, PAGE_SIZE); /* line_length */
+		OUT_RING(chan, line_count);
+		OUT_RING(chan, (1<<8)|(1<<0));
+		OUT_RING(chan, 0);
+		BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_NOP, 1);
+		OUT_RING(chan, 0);
+
+		page_count -= line_count;
+		src_offset += (PAGE_SIZE * line_count);
+		dst_offset += (PAGE_SIZE * line_count);
+	}
+
+	return nouveau_bo_move_accel_cleanup(chan, nvbo, evict, no_wait, new_mem);
+}
+
+static int
+nouveau_bo_move_flipd(struct ttm_buffer_object *bo, bool evict, bool intr,
+		      bool no_wait, struct ttm_mem_reg *new_mem)
+{
+	u32 placement_memtype = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING;
+	struct ttm_placement placement;
+	struct ttm_mem_reg tmp_mem;
+	int ret;
+
+	placement.fpfn = placement.lpfn = 0;
+	placement.num_placement = placement.num_busy_placement = 1;
+	placement.placement = &placement_memtype;
+
+	tmp_mem = *new_mem;
+	tmp_mem.mm_node = NULL;
+	ret = ttm_bo_mem_space(bo, &placement, &tmp_mem, intr, no_wait);
+	if (ret)
+		return ret;
+
+	ret = ttm_tt_bind(bo->ttm, &tmp_mem);
+	if (ret)
+		goto out;
+
+	ret = nouveau_bo_move_m2mf(bo, true, no_wait, &bo->mem, &tmp_mem);
+	if (ret)
+		goto out;
+
+	ret = ttm_bo_move_ttm(bo, evict, no_wait, new_mem);
+out:
+	if (tmp_mem.mm_node) {
+		spin_lock(&bo->bdev->glob->lru_lock);
+		drm_mm_put_block(tmp_mem.mm_node);
+		spin_unlock(&bo->bdev->glob->lru_lock);
+	}
+
+	return ret;
+}
+
+static int
+nouveau_bo_move_flips(struct ttm_buffer_object *bo, bool evict, bool intr,
+		      bool no_wait, struct ttm_mem_reg *new_mem)
+{
+	u32 placement_memtype = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING;
+	struct ttm_placement placement;
+	struct ttm_mem_reg tmp_mem;
+	int ret;
+
+	placement.fpfn = placement.lpfn = 0;
+	placement.num_placement = placement.num_busy_placement = 1;
+	placement.placement = &placement_memtype;
+
+	tmp_mem = *new_mem;
+	tmp_mem.mm_node = NULL;
+	ret = ttm_bo_mem_space(bo, &placement, &tmp_mem, intr, no_wait);
+	if (ret)
+		return ret;
+
+	ret = ttm_bo_move_ttm(bo, evict, no_wait, &tmp_mem);
+	if (ret)
+		goto out;
+
+	ret = nouveau_bo_move_m2mf(bo, true, no_wait, &bo->mem, new_mem);
+	if (ret)
+		goto out;
+
+out:
+	if (tmp_mem.mm_node) {
+		spin_lock(&bo->bdev->glob->lru_lock);
+		drm_mm_put_block(tmp_mem.mm_node);
+		spin_unlock(&bo->bdev->glob->lru_lock);
+	}
+
+	return ret;
+}
+
+static int
+nouveau_bo_move(struct ttm_buffer_object *bo, bool evict, bool intr,
+		bool no_wait, struct ttm_mem_reg *new_mem)
+{
+	struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev);
+	struct nouveau_bo *nvbo = nouveau_bo(bo);
+	struct drm_device *dev = dev_priv->dev;
+	struct ttm_mem_reg *old_mem = &bo->mem;
+	int ret;
+
+	if (dev_priv->card_type == NV_50 && new_mem->mem_type == TTM_PL_VRAM &&
+	    !nvbo->no_vm) {
+		uint64_t offset = new_mem->mm_node->start << PAGE_SHIFT;
+
+		ret = nv50_mem_vm_bind_linear(dev,
+					      offset + dev_priv->vm_vram_base,
+					      new_mem->size, nvbo->tile_flags,
+					      offset);
+		if (ret)
+			return ret;
+	}
+
+	if (dev_priv->init_state != NOUVEAU_CARD_INIT_DONE)
+		return ttm_bo_move_memcpy(bo, evict, no_wait, new_mem);
+
+	if (old_mem->mem_type == TTM_PL_SYSTEM && !bo->ttm) {
+		BUG_ON(bo->mem.mm_node != NULL);
+		bo->mem = *new_mem;
+		new_mem->mm_node = NULL;
+		return 0;
+	}
+
+	if (new_mem->mem_type == TTM_PL_SYSTEM) {
+		if (old_mem->mem_type == TTM_PL_SYSTEM)
+			return ttm_bo_move_memcpy(bo, evict, no_wait, new_mem);
+		if (nouveau_bo_move_flipd(bo, evict, intr, no_wait, new_mem))
+			return ttm_bo_move_memcpy(bo, evict, no_wait, new_mem);
+	} else if (old_mem->mem_type == TTM_PL_SYSTEM) {
+		if (nouveau_bo_move_flips(bo, evict, intr, no_wait, new_mem))
+			return ttm_bo_move_memcpy(bo, evict, no_wait, new_mem);
+	} else {
+		if (nouveau_bo_move_m2mf(bo, evict, no_wait, old_mem, new_mem))
+			return ttm_bo_move_memcpy(bo, evict, no_wait, new_mem);
+	}
+
+	return 0;
+}
+
+static int
+nouveau_bo_verify_access(struct ttm_buffer_object *bo, struct file *filp)
+{
+	return 0;
+}
+
+struct ttm_bo_driver nouveau_bo_driver = {
+	.create_ttm_backend_entry = nouveau_bo_create_ttm_backend_entry,
+	.invalidate_caches = nouveau_bo_invalidate_caches,
+	.init_mem_type = nouveau_bo_init_mem_type,
+	.evict_flags = nouveau_bo_evict_flags,
+	.move = nouveau_bo_move,
+	.verify_access = nouveau_bo_verify_access,
+	.sync_obj_signaled = nouveau_fence_signalled,
+	.sync_obj_wait = nouveau_fence_wait,
+	.sync_obj_flush = nouveau_fence_flush,
+	.sync_obj_unref = nouveau_fence_unref,
+	.sync_obj_ref = nouveau_fence_ref,
+};
+
diff --git a/drivers/gpu/drm/nouveau/nouveau_calc.c b/drivers/gpu/drm/nouveau/nouveau_calc.c
new file mode 100644
index 0000000..ee2b845
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_calc.c
@@ -0,0 +1,478 @@
+/*
+ * Copyright 1993-2003 NVIDIA, Corporation
+ * Copyright 2007-2009 Stuart Bennett
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "drmP.h"
+#include "nouveau_drv.h"
+#include "nouveau_hw.h"
+
+/****************************************************************************\
+*                                                                            *
+* The video arbitration routines calculate some "magic" numbers.  Fixes      *
+* the snow seen when accessing the framebuffer without it.                   *
+* It just works (I hope).                                                    *
+*                                                                            *
+\****************************************************************************/
+
+struct nv_fifo_info {
+	int lwm;
+	int burst;
+};
+
+struct nv_sim_state {
+	int pclk_khz;
+	int mclk_khz;
+	int nvclk_khz;
+	int bpp;
+	int mem_page_miss;
+	int mem_latency;
+	int memory_type;
+	int memory_width;
+	int two_heads;
+};
+
+static void
+nv04_calc_arb(struct nv_fifo_info *fifo, struct nv_sim_state *arb)
+{
+	int pagemiss, cas, width, bpp;
+	int nvclks, mclks, pclks, crtpagemiss;
+	int found, mclk_extra, mclk_loop, cbs, m1, p1;
+	int mclk_freq, pclk_freq, nvclk_freq;
+	int us_m, us_n, us_p, crtc_drain_rate;
+	int cpm_us, us_crt, clwm;
+
+	pclk_freq = arb->pclk_khz;
+	mclk_freq = arb->mclk_khz;
+	nvclk_freq = arb->nvclk_khz;
+	pagemiss = arb->mem_page_miss;
+	cas = arb->mem_latency;
+	width = arb->memory_width >> 6;
+	bpp = arb->bpp;
+	cbs = 128;
+
+	pclks = 2;
+	nvclks = 10;
+	mclks = 13 + cas;
+	mclk_extra = 3;
+	found = 0;
+
+	while (!found) {
+		found = 1;
+
+		mclk_loop = mclks + mclk_extra;
+		us_m = mclk_loop * 1000 * 1000 / mclk_freq;
+		us_n = nvclks * 1000 * 1000 / nvclk_freq;
+		us_p = nvclks * 1000 * 1000 / pclk_freq;
+
+		crtc_drain_rate = pclk_freq * bpp / 8;
+		crtpagemiss = 2;
+		crtpagemiss += 1;
+		cpm_us = crtpagemiss * pagemiss * 1000 * 1000 / mclk_freq;
+		us_crt = cpm_us + us_m + us_n + us_p;
+		clwm = us_crt * crtc_drain_rate / (1000 * 1000);
+		clwm++;
+
+		m1 = clwm + cbs - 512;
+		p1 = m1 * pclk_freq / mclk_freq;
+		p1 = p1 * bpp / 8;
+		if ((p1 < m1 && m1 > 0) || clwm > 519) {
+			found = !mclk_extra;
+			mclk_extra--;
+		}
+		if (clwm < 384)
+			clwm = 384;
+
+		fifo->lwm = clwm;
+		fifo->burst = cbs;
+	}
+}
+
+static void
+nv10_calc_arb(struct nv_fifo_info *fifo, struct nv_sim_state *arb)
+{
+	int fill_rate, drain_rate;
+	int pclks, nvclks, mclks, xclks;
+	int pclk_freq, nvclk_freq, mclk_freq;
+	int fill_lat, extra_lat;
+	int max_burst_o, max_burst_l;
+	int fifo_len, min_lwm, max_lwm;
+	const int burst_lat = 80; /* Maximum allowable latency due
+				   * to the CRTC FIFO burst. (ns) */
+
+	pclk_freq = arb->pclk_khz;
+	nvclk_freq = arb->nvclk_khz;
+	mclk_freq = arb->mclk_khz;
+
+	fill_rate = mclk_freq * arb->memory_width / 8; /* kB/s */
+	drain_rate = pclk_freq * arb->bpp / 8; /* kB/s */
+
+	fifo_len = arb->two_heads ? 1536 : 1024; /* B */
+
+	/* Fixed FIFO refill latency. */
+
+	pclks = 4;	/* lwm detect. */
+
+	nvclks = 3	/* lwm -> sync. */
+		+ 2	/* fbi bus cycles (1 req + 1 busy) */
+		+ 1	/* 2 edge sync.  may be very close to edge so
+			 * just put one. */
+		+ 1	/* fbi_d_rdv_n */
+		+ 1	/* Fbi_d_rdata */
+		+ 1;	/* crtfifo load */
+
+	mclks = 1	/* 2 edge sync.  may be very close to edge so
+			 * just put one. */
+		+ 1	/* arb_hp_req */
+		+ 5	/* tiling pipeline */
+		+ 2	/* latency fifo */
+		+ 2	/* memory request to fbio block */
+		+ 7;	/* data returned from fbio block */
+
+	/* Need to accumulate 256 bits for read */
+	mclks += (arb->memory_type == 0 ? 2 : 1)
+		* arb->memory_width / 32;
+
+	fill_lat = mclks * 1000 * 1000 / mclk_freq   /* minimum mclk latency */
+		+ nvclks * 1000 * 1000 / nvclk_freq  /* nvclk latency */
+		+ pclks * 1000 * 1000 / pclk_freq;   /* pclk latency */
+
+	/* Conditional FIFO refill latency. */
+
+	xclks = 2 * arb->mem_page_miss + mclks /* Extra latency due to
+						* the overlay. */
+		+ 2 * arb->mem_page_miss       /* Extra pagemiss latency. */
+		+ (arb->bpp == 32 ? 8 : 4);    /* Margin of error. */
+
+	extra_lat = xclks * 1000 * 1000 / mclk_freq;
+
+	if (arb->two_heads)
+		/* Account for another CRTC. */
+		extra_lat += fill_lat + extra_lat + burst_lat;
+
+	/* FIFO burst */
+
+	/* Max burst not leading to overflows. */
+	max_burst_o = (1 + fifo_len - extra_lat * drain_rate / (1000 * 1000))
+		* (fill_rate / 1000) / ((fill_rate - drain_rate) / 1000);
+	fifo->burst = min(max_burst_o, 1024);
+
+	/* Max burst value with an acceptable latency. */
+	max_burst_l = burst_lat * fill_rate / (1000 * 1000);
+	fifo->burst = min(max_burst_l, fifo->burst);
+
+	fifo->burst = rounddown_pow_of_two(fifo->burst);
+
+	/* FIFO low watermark */
+
+	min_lwm = (fill_lat + extra_lat) * drain_rate / (1000 * 1000) + 1;
+	max_lwm = fifo_len - fifo->burst
+		+ fill_lat * drain_rate / (1000 * 1000)
+		+ fifo->burst * drain_rate / fill_rate;
+
+	fifo->lwm = min_lwm + 10 * (max_lwm - min_lwm) / 100; /* Empirical. */
+}
+
+static void
+nv04_update_arb(struct drm_device *dev, int VClk, int bpp,
+		int *burst, int *lwm)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nv_fifo_info fifo_data;
+	struct nv_sim_state sim_data;
+	int MClk = nouveau_hw_get_clock(dev, MPLL);
+	int NVClk = nouveau_hw_get_clock(dev, NVPLL);
+	uint32_t cfg1 = nvReadFB(dev, NV_PFB_CFG1);
+
+	sim_data.pclk_khz = VClk;
+	sim_data.mclk_khz = MClk;
+	sim_data.nvclk_khz = NVClk;
+	sim_data.bpp = bpp;
+	sim_data.two_heads = nv_two_heads(dev);
+	if ((dev->pci_device & 0xffff) == 0x01a0 /*CHIPSET_NFORCE*/ ||
+	    (dev->pci_device & 0xffff) == 0x01f0 /*CHIPSET_NFORCE2*/) {
+		uint32_t type;
+
+		pci_read_config_dword(pci_get_bus_and_slot(0, 1), 0x7c, &type);
+
+		sim_data.memory_type = (type >> 12) & 1;
+		sim_data.memory_width = 64;
+		sim_data.mem_latency = 3;
+		sim_data.mem_page_miss = 10;
+	} else {
+		sim_data.memory_type = nvReadFB(dev, NV_PFB_CFG0) & 0x1;
+		sim_data.memory_width = (nvReadEXTDEV(dev, NV_PEXTDEV_BOOT_0) & 0x10) ? 128 : 64;
+		sim_data.mem_latency = cfg1 & 0xf;
+		sim_data.mem_page_miss = ((cfg1 >> 4) & 0xf) + ((cfg1 >> 31) & 0x1);
+	}
+
+	if (dev_priv->card_type == NV_04)
+		nv04_calc_arb(&fifo_data, &sim_data);
+	else
+		nv10_calc_arb(&fifo_data, &sim_data);
+
+	*burst = ilog2(fifo_data.burst >> 4);
+	*lwm = fifo_data.lwm >> 3;
+}
+
+static void
+nv30_update_arb(int *burst, int *lwm)
+{
+	unsigned int fifo_size, burst_size, graphics_lwm;
+
+	fifo_size = 2048;
+	burst_size = 512;
+	graphics_lwm = fifo_size - burst_size;
+
+	*burst = ilog2(burst_size >> 5);
+	*lwm = graphics_lwm >> 3;
+}
+
+void
+nouveau_calc_arb(struct drm_device *dev, int vclk, int bpp, int *burst, int *lwm)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	if (dev_priv->card_type < NV_30)
+		nv04_update_arb(dev, vclk, bpp, burst, lwm);
+	else if ((dev->pci_device & 0xfff0) == 0x0240 /*CHIPSET_C51*/ ||
+		 (dev->pci_device & 0xfff0) == 0x03d0 /*CHIPSET_C512*/) {
+		*burst = 128;
+		*lwm = 0x0480;
+	} else
+		nv30_update_arb(burst, lwm);
+}
+
+static int
+getMNP_single(struct drm_device *dev, struct pll_lims *pll_lim, int clk,
+	      struct nouveau_pll_vals *bestpv)
+{
+	/* Find M, N and P for a single stage PLL
+	 *
+	 * Note that some bioses (NV3x) have lookup tables of precomputed MNP
+	 * values, but we're too lazy to use those atm
+	 *
+	 * "clk" parameter in kHz
+	 * returns calculated clock
+	 */
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	int cv = dev_priv->vbios->chip_version;
+	int minvco = pll_lim->vco1.minfreq, maxvco = pll_lim->vco1.maxfreq;
+	int minM = pll_lim->vco1.min_m, maxM = pll_lim->vco1.max_m;
+	int minN = pll_lim->vco1.min_n, maxN = pll_lim->vco1.max_n;
+	int minU = pll_lim->vco1.min_inputfreq;
+	int maxU = pll_lim->vco1.max_inputfreq;
+	int minP = pll_lim->max_p ? pll_lim->min_p : 0;
+	int maxP = pll_lim->max_p ? pll_lim->max_p : pll_lim->max_usable_log2p;
+	int crystal = pll_lim->refclk;
+	int M, N, thisP, P;
+	int clkP, calcclk;
+	int delta, bestdelta = INT_MAX;
+	int bestclk = 0;
+
+	/* this division verified for nv20, nv18, nv28 (Haiku), and nv34 */
+	/* possibly correlated with introduction of 27MHz crystal */
+	if (dev_priv->card_type < NV_50) {
+		if (cv < 0x17 || cv == 0x1a || cv == 0x20) {
+			if (clk > 250000)
+				maxM = 6;
+			if (clk > 340000)
+				maxM = 2;
+		} else if (cv < 0x40) {
+			if (clk > 150000)
+				maxM = 6;
+			if (clk > 200000)
+				maxM = 4;
+			if (clk > 340000)
+				maxM = 2;
+		}
+	}
+
+	P = pll_lim->max_p ? maxP : (1 << maxP);
+	if ((clk * P) < minvco) {
+		minvco = clk * maxP;
+		maxvco = minvco * 2;
+	}
+
+	if (clk + clk/200 > maxvco)	/* +0.5% */
+		maxvco = clk + clk/200;
+
+	/* NV34 goes maxlog2P->0, NV20 goes 0->maxlog2P */
+	for (thisP = minP; thisP <= maxP; thisP++) {
+		P = pll_lim->max_p ? thisP : (1 << thisP);
+		clkP = clk * P;
+
+		if (clkP < minvco)
+			continue;
+		if (clkP > maxvco)
+			return bestclk;
+
+		for (M = minM; M <= maxM; M++) {
+			if (crystal/M < minU)
+				return bestclk;
+			if (crystal/M > maxU)
+				continue;
+
+			/* add crystal/2 to round better */
+			N = (clkP * M + crystal/2) / crystal;
+
+			if (N < minN)
+				continue;
+			if (N > maxN)
+				break;
+
+			/* more rounding additions */
+			calcclk = ((N * crystal + P/2) / P + M/2) / M;
+			delta = abs(calcclk - clk);
+			/* we do an exhaustive search rather than terminating
+			 * on an optimality condition...
+			 */
+			if (delta < bestdelta) {
+				bestdelta = delta;
+				bestclk = calcclk;
+				bestpv->N1 = N;
+				bestpv->M1 = M;
+				bestpv->log2P = thisP;
+				if (delta == 0)	/* except this one */
+					return bestclk;
+			}
+		}
+	}
+
+	return bestclk;
+}
+
+static int
+getMNP_double(struct drm_device *dev, struct pll_lims *pll_lim, int clk,
+	      struct nouveau_pll_vals *bestpv)
+{
+	/* Find M, N and P for a two stage PLL
+	 *
+	 * Note that some bioses (NV30+) have lookup tables of precomputed MNP
+	 * values, but we're too lazy to use those atm
+	 *
+	 * "clk" parameter in kHz
+	 * returns calculated clock
+	 */
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	int chip_version = dev_priv->vbios->chip_version;
+	int minvco1 = pll_lim->vco1.minfreq, maxvco1 = pll_lim->vco1.maxfreq;
+	int minvco2 = pll_lim->vco2.minfreq, maxvco2 = pll_lim->vco2.maxfreq;
+	int minU1 = pll_lim->vco1.min_inputfreq, minU2 = pll_lim->vco2.min_inputfreq;
+	int maxU1 = pll_lim->vco1.max_inputfreq, maxU2 = pll_lim->vco2.max_inputfreq;
+	int minM1 = pll_lim->vco1.min_m, maxM1 = pll_lim->vco1.max_m;
+	int minN1 = pll_lim->vco1.min_n, maxN1 = pll_lim->vco1.max_n;
+	int minM2 = pll_lim->vco2.min_m, maxM2 = pll_lim->vco2.max_m;
+	int minN2 = pll_lim->vco2.min_n, maxN2 = pll_lim->vco2.max_n;
+	int maxlog2P = pll_lim->max_usable_log2p;
+	int crystal = pll_lim->refclk;
+	bool fixedgain2 = (minM2 == maxM2 && minN2 == maxN2);
+	int M1, N1, M2, N2, log2P;
+	int clkP, calcclk1, calcclk2, calcclkout;
+	int delta, bestdelta = INT_MAX;
+	int bestclk = 0;
+
+	int vco2 = (maxvco2 - maxvco2/200) / 2;
+	for (log2P = 0; clk && log2P < maxlog2P && clk <= (vco2 >> log2P); log2P++)
+		;
+	clkP = clk << log2P;
+
+	if (maxvco2 < clk + clk/200)	/* +0.5% */
+		maxvco2 = clk + clk/200;
+
+	for (M1 = minM1; M1 <= maxM1; M1++) {
+		if (crystal/M1 < minU1)
+			return bestclk;
+		if (crystal/M1 > maxU1)
+			continue;
+
+		for (N1 = minN1; N1 <= maxN1; N1++) {
+			calcclk1 = crystal * N1 / M1;
+			if (calcclk1 < minvco1)
+				continue;
+			if (calcclk1 > maxvco1)
+				break;
+
+			for (M2 = minM2; M2 <= maxM2; M2++) {
+				if (calcclk1/M2 < minU2)
+					break;
+				if (calcclk1/M2 > maxU2)
+					continue;
+
+				/* add calcclk1/2 to round better */
+				N2 = (clkP * M2 + calcclk1/2) / calcclk1;
+				if (N2 < minN2)
+					continue;
+				if (N2 > maxN2)
+					break;
+
+				if (!fixedgain2) {
+					if (chip_version < 0x60)
+						if (N2/M2 < 4 || N2/M2 > 10)
+							continue;
+
+					calcclk2 = calcclk1 * N2 / M2;
+					if (calcclk2 < minvco2)
+						break;
+					if (calcclk2 > maxvco2)
+						continue;
+				} else
+					calcclk2 = calcclk1;
+
+				calcclkout = calcclk2 >> log2P;
+				delta = abs(calcclkout - clk);
+				/* we do an exhaustive search rather than terminating
+				 * on an optimality condition...
+				 */
+				if (delta < bestdelta) {
+					bestdelta = delta;
+					bestclk = calcclkout;
+					bestpv->N1 = N1;
+					bestpv->M1 = M1;
+					bestpv->N2 = N2;
+					bestpv->M2 = M2;
+					bestpv->log2P = log2P;
+					if (delta == 0)	/* except this one */
+						return bestclk;
+				}
+			}
+		}
+	}
+
+	return bestclk;
+}
+
+int
+nouveau_calc_pll_mnp(struct drm_device *dev, struct pll_lims *pll_lim, int clk,
+		     struct nouveau_pll_vals *pv)
+{
+	int outclk;
+
+	if (!pll_lim->vco2.maxfreq)
+		outclk = getMNP_single(dev, pll_lim, clk, pv);
+	else
+		outclk = getMNP_double(dev, pll_lim, clk, pv);
+
+	if (!outclk)
+		NV_ERROR(dev, "Could not find a compatible set of PLL values\n");
+
+	return outclk;
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_channel.c b/drivers/gpu/drm/nouveau/nouveau_channel.c
new file mode 100644
index 0000000..9aaa972
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_channel.c
@@ -0,0 +1,468 @@
+/*
+ * Copyright 2005-2006 Stephane Marchesin
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "drmP.h"
+#include "drm.h"
+#include "nouveau_drv.h"
+#include "nouveau_drm.h"
+#include "nouveau_dma.h"
+
+static int
+nouveau_channel_pushbuf_ctxdma_init(struct nouveau_channel *chan)
+{
+	struct drm_device *dev = chan->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_bo *pb = chan->pushbuf_bo;
+	struct nouveau_gpuobj *pushbuf = NULL;
+	uint32_t start = pb->bo.mem.mm_node->start << PAGE_SHIFT;
+	int ret;
+
+	if (pb->bo.mem.mem_type == TTM_PL_TT) {
+		ret = nouveau_gpuobj_gart_dma_new(chan, 0,
+						  dev_priv->gart_info.aper_size,
+						  NV_DMA_ACCESS_RO, &pushbuf,
+						  NULL);
+		chan->pushbuf_base = start;
+	} else
+	if (dev_priv->card_type != NV_04) {
+		ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, 0,
+					     dev_priv->fb_available_size,
+					     NV_DMA_ACCESS_RO,
+					     NV_DMA_TARGET_VIDMEM, &pushbuf);
+		chan->pushbuf_base = start;
+	} else {
+		/* NV04 cmdbuf hack, from original ddx.. not sure of it's
+		 * exact reason for existing :)  PCI access to cmdbuf in
+		 * VRAM.
+		 */
+		ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY,
+					     drm_get_resource_start(dev, 1),
+					     dev_priv->fb_available_size,
+					     NV_DMA_ACCESS_RO,
+					     NV_DMA_TARGET_PCI, &pushbuf);
+		chan->pushbuf_base = start;
+	}
+
+	ret = nouveau_gpuobj_ref_add(dev, chan, 0, pushbuf, &chan->pushbuf);
+	if (ret) {
+		NV_ERROR(dev, "Error referencing pushbuf ctxdma: %d\n", ret);
+		if (pushbuf != dev_priv->gart_info.sg_ctxdma)
+			nouveau_gpuobj_del(dev, &pushbuf);
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct nouveau_bo *
+nouveau_channel_user_pushbuf_alloc(struct drm_device *dev)
+{
+	struct nouveau_bo *pushbuf = NULL;
+	int location, ret;
+
+	if (nouveau_vram_pushbuf)
+		location = TTM_PL_FLAG_VRAM;
+	else
+		location = TTM_PL_FLAG_TT;
+
+	ret = nouveau_bo_new(dev, NULL, 65536, 0, location, 0, 0x0000, false,
+			     true, &pushbuf);
+	if (ret) {
+		NV_ERROR(dev, "error allocating DMA push buffer: %d\n", ret);
+		return NULL;
+	}
+
+	ret = nouveau_bo_pin(pushbuf, location);
+	if (ret) {
+		NV_ERROR(dev, "error pinning DMA push buffer: %d\n", ret);
+		nouveau_bo_ref(NULL, &pushbuf);
+		return NULL;
+	}
+
+	return pushbuf;
+}
+
+/* allocates and initializes a fifo for user space consumption */
+int
+nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret,
+		      struct drm_file *file_priv,
+		      uint32_t vram_handle, uint32_t tt_handle)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
+	struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
+	struct nouveau_channel *chan;
+	int channel, user;
+	int ret;
+
+	/*
+	 * Alright, here is the full story
+	 * Nvidia cards have multiple hw fifo contexts (praise them for that,
+	 * no complicated crash-prone context switches)
+	 * We allocate a new context for each app and let it write to it
+	 * directly (woo, full userspace command submission !)
+	 * When there are no more contexts, you lost
+	 */
+	for (channel = 0; channel < pfifo->channels; channel++) {
+		if (dev_priv->fifos[channel] == NULL)
+			break;
+	}
+
+	/* no more fifos. you lost. */
+	if (channel == pfifo->channels)
+		return -EINVAL;
+
+	dev_priv->fifos[channel] = kzalloc(sizeof(struct nouveau_channel),
+					   GFP_KERNEL);
+	if (!dev_priv->fifos[channel])
+		return -ENOMEM;
+	dev_priv->fifo_alloc_count++;
+	chan = dev_priv->fifos[channel];
+	INIT_LIST_HEAD(&chan->nvsw.vbl_wait);
+	INIT_LIST_HEAD(&chan->fence.pending);
+	chan->dev = dev;
+	chan->id = channel;
+	chan->file_priv = file_priv;
+	chan->vram_handle = vram_handle;
+	chan->gart_handle = tt_handle;
+
+	NV_INFO(dev, "Allocating FIFO number %d\n", channel);
+
+	/* Allocate DMA push buffer */
+	chan->pushbuf_bo = nouveau_channel_user_pushbuf_alloc(dev);
+	if (!chan->pushbuf_bo) {
+		ret = -ENOMEM;
+		NV_ERROR(dev, "pushbuf %d\n", ret);
+		nouveau_channel_free(chan);
+		return ret;
+	}
+
+	/* Locate channel's user control regs */
+	if (dev_priv->card_type < NV_40)
+		user = NV03_USER(channel);
+	else
+	if (dev_priv->card_type < NV_50)
+		user = NV40_USER(channel);
+	else
+		user = NV50_USER(channel);
+
+	chan->user = ioremap(pci_resource_start(dev->pdev, 0) + user,
+								PAGE_SIZE);
+	if (!chan->user) {
+		NV_ERROR(dev, "ioremap of regs failed.\n");
+		nouveau_channel_free(chan);
+		return -ENOMEM;
+	}
+	chan->user_put = 0x40;
+	chan->user_get = 0x44;
+
+	/* Allocate space for per-channel fixed notifier memory */
+	ret = nouveau_notifier_init_channel(chan);
+	if (ret) {
+		NV_ERROR(dev, "ntfy %d\n", ret);
+		nouveau_channel_free(chan);
+		return ret;
+	}
+
+	/* Setup channel's default objects */
+	ret = nouveau_gpuobj_channel_init(chan, vram_handle, tt_handle);
+	if (ret) {
+		NV_ERROR(dev, "gpuobj %d\n", ret);
+		nouveau_channel_free(chan);
+		return ret;
+	}
+
+	/* Create a dma object for the push buffer */
+	ret = nouveau_channel_pushbuf_ctxdma_init(chan);
+	if (ret) {
+		NV_ERROR(dev, "pbctxdma %d\n", ret);
+		nouveau_channel_free(chan);
+		return ret;
+	}
+
+	/* disable the fifo caches */
+	pfifo->reassign(dev, false);
+
+	/* Create a graphics context for new channel */
+	ret = pgraph->create_context(chan);
+	if (ret) {
+		nouveau_channel_free(chan);
+		return ret;
+	}
+
+	/* Construct inital RAMFC for new channel */
+	ret = pfifo->create_context(chan);
+	if (ret) {
+		nouveau_channel_free(chan);
+		return ret;
+	}
+
+	pfifo->reassign(dev, true);
+
+	ret = nouveau_dma_init(chan);
+	if (!ret)
+		ret = nouveau_fence_init(chan);
+	if (ret) {
+		nouveau_channel_free(chan);
+		return ret;
+	}
+
+	nouveau_debugfs_channel_init(chan);
+
+	NV_INFO(dev, "%s: initialised FIFO %d\n", __func__, channel);
+	*chan_ret = chan;
+	return 0;
+}
+
+int
+nouveau_channel_idle(struct nouveau_channel *chan)
+{
+	struct drm_device *dev = chan->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_engine *engine = &dev_priv->engine;
+	uint32_t caches;
+	int idle;
+
+	if (!chan) {
+		NV_ERROR(dev, "no channel...\n");
+		return 1;
+	}
+
+	caches = nv_rd32(dev, NV03_PFIFO_CACHES);
+	nv_wr32(dev, NV03_PFIFO_CACHES, caches & ~1);
+
+	if (engine->fifo.channel_id(dev) != chan->id) {
+		struct nouveau_gpuobj *ramfc =
+			chan->ramfc ? chan->ramfc->gpuobj : NULL;
+
+		if (!ramfc) {
+			NV_ERROR(dev, "No RAMFC for channel %d\n", chan->id);
+			return 1;
+		}
+
+		engine->instmem.prepare_access(dev, false);
+		if (nv_ro32(dev, ramfc, 0) != nv_ro32(dev, ramfc, 1))
+			idle = 0;
+		else
+			idle = 1;
+		engine->instmem.finish_access(dev);
+	} else {
+		idle = (nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET) ==
+			nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUT));
+	}
+
+	nv_wr32(dev, NV03_PFIFO_CACHES, caches);
+	return idle;
+}
+
+/* stops a fifo */
+void
+nouveau_channel_free(struct nouveau_channel *chan)
+{
+	struct drm_device *dev = chan->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
+	struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
+	unsigned long flags;
+	int ret;
+
+	NV_INFO(dev, "%s: freeing fifo %d\n", __func__, chan->id);
+
+	nouveau_debugfs_channel_fini(chan);
+
+	/* Give outstanding push buffers a chance to complete */
+	spin_lock_irqsave(&chan->fence.lock, flags);
+	nouveau_fence_update(chan);
+	spin_unlock_irqrestore(&chan->fence.lock, flags);
+	if (chan->fence.sequence != chan->fence.sequence_ack) {
+		struct nouveau_fence *fence = NULL;
+
+		ret = nouveau_fence_new(chan, &fence, true);
+		if (ret == 0) {
+			ret = nouveau_fence_wait(fence, NULL, false, false);
+			nouveau_fence_unref((void *)&fence);
+		}
+
+		if (ret)
+			NV_ERROR(dev, "Failed to idle channel %d.\n", chan->id);
+	}
+
+	/* Ensure all outstanding fences are signaled.  They should be if the
+	 * above attempts at idling were OK, but if we failed this'll tell TTM
+	 * we're done with the buffers.
+	 */
+	nouveau_fence_fini(chan);
+
+	/* Ensure the channel is no longer active on the GPU */
+	pfifo->reassign(dev, false);
+
+	if (pgraph->channel(dev) == chan) {
+		pgraph->fifo_access(dev, false);
+		pgraph->unload_context(dev);
+		pgraph->fifo_access(dev, true);
+	}
+	pgraph->destroy_context(chan);
+
+	if (pfifo->channel_id(dev) == chan->id) {
+		pfifo->disable(dev);
+		pfifo->unload_context(dev);
+		pfifo->enable(dev);
+	}
+	pfifo->destroy_context(chan);
+
+	pfifo->reassign(dev, true);
+
+	/* Release the channel's resources */
+	nouveau_gpuobj_ref_del(dev, &chan->pushbuf);
+	if (chan->pushbuf_bo) {
+		nouveau_bo_unpin(chan->pushbuf_bo);
+		nouveau_bo_ref(NULL, &chan->pushbuf_bo);
+	}
+	nouveau_gpuobj_channel_takedown(chan);
+	nouveau_notifier_takedown_channel(chan);
+	if (chan->user)
+		iounmap(chan->user);
+
+	dev_priv->fifos[chan->id] = NULL;
+	dev_priv->fifo_alloc_count--;
+	kfree(chan);
+}
+
+/* cleans up all the fifos from file_priv */
+void
+nouveau_channel_cleanup(struct drm_device *dev, struct drm_file *file_priv)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_engine *engine = &dev_priv->engine;
+	int i;
+
+	NV_DEBUG(dev, "clearing FIFO enables from file_priv\n");
+	for (i = 0; i < engine->fifo.channels; i++) {
+		struct nouveau_channel *chan = dev_priv->fifos[i];
+
+		if (chan && chan->file_priv == file_priv)
+			nouveau_channel_free(chan);
+	}
+}
+
+int
+nouveau_channel_owner(struct drm_device *dev, struct drm_file *file_priv,
+		      int channel)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_engine *engine = &dev_priv->engine;
+
+	if (channel >= engine->fifo.channels)
+		return 0;
+	if (dev_priv->fifos[channel] == NULL)
+		return 0;
+
+	return (dev_priv->fifos[channel]->file_priv == file_priv);
+}
+
+/***********************************
+ * ioctls wrapping the functions
+ ***********************************/
+
+static int
+nouveau_ioctl_fifo_alloc(struct drm_device *dev, void *data,
+			 struct drm_file *file_priv)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct drm_nouveau_channel_alloc *init = data;
+	struct nouveau_channel *chan;
+	int ret;
+
+	NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
+
+	if (dev_priv->engine.graph.accel_blocked)
+		return -ENODEV;
+
+	if (init->fb_ctxdma_handle == ~0 || init->tt_ctxdma_handle == ~0)
+		return -EINVAL;
+
+	ret = nouveau_channel_alloc(dev, &chan, file_priv,
+				    init->fb_ctxdma_handle,
+				    init->tt_ctxdma_handle);
+	if (ret)
+		return ret;
+	init->channel  = chan->id;
+
+	init->subchan[0].handle = NvM2MF;
+	if (dev_priv->card_type < NV_50)
+		init->subchan[0].grclass = 0x0039;
+	else
+		init->subchan[0].grclass = 0x5039;
+	init->nr_subchan = 1;
+
+	/* Named memory object area */
+	ret = drm_gem_handle_create(file_priv, chan->notifier_bo->gem,
+				    &init->notifier_handle);
+	if (ret) {
+		nouveau_channel_free(chan);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int
+nouveau_ioctl_fifo_free(struct drm_device *dev, void *data,
+			struct drm_file *file_priv)
+{
+	struct drm_nouveau_channel_free *cfree = data;
+	struct nouveau_channel *chan;
+
+	NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
+	NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(cfree->channel, file_priv, chan);
+
+	nouveau_channel_free(chan);
+	return 0;
+}
+
+/***********************************
+ * finally, the ioctl table
+ ***********************************/
+
+struct drm_ioctl_desc nouveau_ioctls[] = {
+	DRM_IOCTL_DEF(DRM_NOUVEAU_CARD_INIT, nouveau_ioctl_card_init, DRM_AUTH),
+	DRM_IOCTL_DEF(DRM_NOUVEAU_GETPARAM, nouveau_ioctl_getparam, DRM_AUTH),
+	DRM_IOCTL_DEF(DRM_NOUVEAU_SETPARAM, nouveau_ioctl_setparam, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+	DRM_IOCTL_DEF(DRM_NOUVEAU_CHANNEL_ALLOC, nouveau_ioctl_fifo_alloc, DRM_AUTH),
+	DRM_IOCTL_DEF(DRM_NOUVEAU_CHANNEL_FREE, nouveau_ioctl_fifo_free, DRM_AUTH),
+	DRM_IOCTL_DEF(DRM_NOUVEAU_GROBJ_ALLOC, nouveau_ioctl_grobj_alloc, DRM_AUTH),
+	DRM_IOCTL_DEF(DRM_NOUVEAU_NOTIFIEROBJ_ALLOC, nouveau_ioctl_notifier_alloc, DRM_AUTH),
+	DRM_IOCTL_DEF(DRM_NOUVEAU_GPUOBJ_FREE, nouveau_ioctl_gpuobj_free, DRM_AUTH),
+	DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_NEW, nouveau_gem_ioctl_new, DRM_AUTH),
+	DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_PUSHBUF, nouveau_gem_ioctl_pushbuf, DRM_AUTH),
+	DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_PUSHBUF_CALL, nouveau_gem_ioctl_pushbuf_call, DRM_AUTH),
+	DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_PIN, nouveau_gem_ioctl_pin, DRM_AUTH),
+	DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_UNPIN, nouveau_gem_ioctl_unpin, DRM_AUTH),
+	DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_CPU_PREP, nouveau_gem_ioctl_cpu_prep, DRM_AUTH),
+	DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_CPU_FINI, nouveau_gem_ioctl_cpu_fini, DRM_AUTH),
+	DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_INFO, nouveau_gem_ioctl_info, DRM_AUTH),
+	DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_PUSHBUF_CALL2, nouveau_gem_ioctl_pushbuf_call2, DRM_AUTH),
+};
+
+int nouveau_max_ioctl = DRM_ARRAY_SIZE(nouveau_ioctls);
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
new file mode 100644
index 0000000..032cf09
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -0,0 +1,824 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "drmP.h"
+#include "drm_edid.h"
+#include "drm_crtc_helper.h"
+#include "nouveau_reg.h"
+#include "nouveau_drv.h"
+#include "nouveau_encoder.h"
+#include "nouveau_crtc.h"
+#include "nouveau_connector.h"
+#include "nouveau_hw.h"
+
+static inline struct drm_encoder_slave_funcs *
+get_slave_funcs(struct nouveau_encoder *enc)
+{
+	return to_encoder_slave(to_drm_encoder(enc))->slave_funcs;
+}
+
+static struct nouveau_encoder *
+find_encoder_by_type(struct drm_connector *connector, int type)
+{
+	struct drm_device *dev = connector->dev;
+	struct nouveau_encoder *nv_encoder;
+	struct drm_mode_object *obj;
+	int i, id;
+
+	for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
+		id = connector->encoder_ids[i];
+		if (!id)
+			break;
+
+		obj = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER);
+		if (!obj)
+			continue;
+		nv_encoder = nouveau_encoder(obj_to_encoder(obj));
+
+		if (type == OUTPUT_ANY || nv_encoder->dcb->type == type)
+			return nv_encoder;
+	}
+
+	return NULL;
+}
+
+struct nouveau_connector *
+nouveau_encoder_connector_get(struct nouveau_encoder *encoder)
+{
+	struct drm_device *dev = to_drm_encoder(encoder)->dev;
+	struct drm_connector *drm_connector;
+
+	list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) {
+		if (drm_connector->encoder == to_drm_encoder(encoder))
+			return nouveau_connector(drm_connector);
+	}
+
+	return NULL;
+}
+
+
+static void
+nouveau_connector_destroy(struct drm_connector *drm_connector)
+{
+	struct nouveau_connector *connector = nouveau_connector(drm_connector);
+	struct drm_device *dev = connector->base.dev;
+
+	NV_DEBUG(dev, "\n");
+
+	if (!connector)
+		return;
+
+	drm_sysfs_connector_remove(drm_connector);
+	drm_connector_cleanup(drm_connector);
+	kfree(drm_connector);
+}
+
+static void
+nouveau_connector_ddc_prepare(struct drm_connector *connector, int *flags)
+{
+	struct drm_nouveau_private *dev_priv = connector->dev->dev_private;
+
+	if (dev_priv->card_type >= NV_50)
+		return;
+
+	*flags = 0;
+	if (NVLockVgaCrtcs(dev_priv->dev, false))
+		*flags |= 1;
+	if (nv_heads_tied(dev_priv->dev))
+		*flags |= 2;
+
+	if (*flags & 2)
+		NVSetOwner(dev_priv->dev, 0); /* necessary? */
+}
+
+static void
+nouveau_connector_ddc_finish(struct drm_connector *connector, int flags)
+{
+	struct drm_nouveau_private *dev_priv = connector->dev->dev_private;
+
+	if (dev_priv->card_type >= NV_50)
+		return;
+
+	if (flags & 2)
+		NVSetOwner(dev_priv->dev, 4);
+	if (flags & 1)
+		NVLockVgaCrtcs(dev_priv->dev, true);
+}
+
+static struct nouveau_i2c_chan *
+nouveau_connector_ddc_detect(struct drm_connector *connector,
+			     struct nouveau_encoder **pnv_encoder)
+{
+	struct drm_device *dev = connector->dev;
+	uint8_t out_buf[] = { 0x0, 0x0}, buf[2];
+	int ret, flags, i;
+
+	struct i2c_msg msgs[] = {
+		{
+			.addr = 0x50,
+			.flags = 0,
+			.len = 1,
+			.buf = out_buf,
+		},
+		{
+			.addr = 0x50,
+			.flags = I2C_M_RD,
+			.len = 1,
+			.buf = buf,
+		}
+	};
+
+	for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
+		struct nouveau_i2c_chan *i2c = NULL;
+		struct nouveau_encoder *nv_encoder;
+		struct drm_mode_object *obj;
+		int id;
+
+		id = connector->encoder_ids[i];
+		if (!id)
+			break;
+
+		obj = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER);
+		if (!obj)
+			continue;
+		nv_encoder = nouveau_encoder(obj_to_encoder(obj));
+
+		if (nv_encoder->dcb->i2c_index < 0xf)
+			i2c = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index);
+		if (!i2c)
+			continue;
+
+		nouveau_connector_ddc_prepare(connector, &flags);
+		ret = i2c_transfer(&i2c->adapter, msgs, 2);
+		nouveau_connector_ddc_finish(connector, flags);
+
+		if (ret == 2) {
+			*pnv_encoder = nv_encoder;
+			return i2c;
+		}
+	}
+
+	return NULL;
+}
+
+static void
+nouveau_connector_set_encoder(struct drm_connector *connector,
+			      struct nouveau_encoder *nv_encoder)
+{
+	struct nouveau_connector *nv_connector = nouveau_connector(connector);
+	struct drm_nouveau_private *dev_priv = connector->dev->dev_private;
+	struct drm_device *dev = connector->dev;
+
+	if (nv_connector->detected_encoder == nv_encoder)
+		return;
+	nv_connector->detected_encoder = nv_encoder;
+
+	if (nv_encoder->dcb->type == OUTPUT_LVDS ||
+	    nv_encoder->dcb->type == OUTPUT_TMDS) {
+		connector->doublescan_allowed = false;
+		connector->interlace_allowed = false;
+	} else {
+		connector->doublescan_allowed = true;
+		if (dev_priv->card_type == NV_20 ||
+		   (dev_priv->card_type == NV_10 &&
+		    (dev->pci_device & 0x0ff0) != 0x0100 &&
+		    (dev->pci_device & 0x0ff0) != 0x0150))
+			/* HW is broken */
+			connector->interlace_allowed = false;
+		else
+			connector->interlace_allowed = true;
+	}
+
+	if (connector->connector_type == DRM_MODE_CONNECTOR_DVII) {
+		drm_connector_property_set_value(connector,
+			dev->mode_config.dvi_i_subconnector_property,
+			nv_encoder->dcb->type == OUTPUT_TMDS ?
+			DRM_MODE_SUBCONNECTOR_DVID :
+			DRM_MODE_SUBCONNECTOR_DVIA);
+	}
+}
+
+static enum drm_connector_status
+nouveau_connector_detect(struct drm_connector *connector)
+{
+	struct drm_device *dev = connector->dev;
+	struct nouveau_connector *nv_connector = nouveau_connector(connector);
+	struct nouveau_encoder *nv_encoder = NULL;
+	struct nouveau_i2c_chan *i2c;
+	int type, flags;
+
+	if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
+		nv_encoder = find_encoder_by_type(connector, OUTPUT_LVDS);
+	if (nv_encoder && nv_connector->native_mode) {
+		nouveau_connector_set_encoder(connector, nv_encoder);
+		return connector_status_connected;
+	}
+
+	i2c = nouveau_connector_ddc_detect(connector, &nv_encoder);
+	if (i2c) {
+		nouveau_connector_ddc_prepare(connector, &flags);
+		nv_connector->edid = drm_get_edid(connector, &i2c->adapter);
+		nouveau_connector_ddc_finish(connector, flags);
+		drm_mode_connector_update_edid_property(connector,
+							nv_connector->edid);
+		if (!nv_connector->edid) {
+			NV_ERROR(dev, "DDC responded, but no EDID for %s\n",
+				 drm_get_connector_name(connector));
+			return connector_status_disconnected;
+		}
+
+		if (nv_encoder->dcb->type == OUTPUT_DP &&
+		    !nouveau_dp_detect(to_drm_encoder(nv_encoder))) {
+			NV_ERROR(dev, "Detected %s, but failed init\n",
+				 drm_get_connector_name(connector));
+			return connector_status_disconnected;
+		}
+
+		/* Override encoder type for DVI-I based on whether EDID
+		 * says the display is digital or analog, both use the
+		 * same i2c channel so the value returned from ddc_detect
+		 * isn't necessarily correct.
+		 */
+		if (connector->connector_type == DRM_MODE_CONNECTOR_DVII) {
+			if (nv_connector->edid->input & DRM_EDID_INPUT_DIGITAL)
+				type = OUTPUT_TMDS;
+			else
+				type = OUTPUT_ANALOG;
+
+			nv_encoder = find_encoder_by_type(connector, type);
+			if (!nv_encoder) {
+				NV_ERROR(dev, "Detected %d encoder on %s, "
+					      "but no object!\n", type,
+					 drm_get_connector_name(connector));
+				return connector_status_disconnected;
+			}
+		}
+
+		nouveau_connector_set_encoder(connector, nv_encoder);
+		return connector_status_connected;
+	}
+
+	nv_encoder = find_encoder_by_type(connector, OUTPUT_ANALOG);
+	if (!nv_encoder)
+		nv_encoder = find_encoder_by_type(connector, OUTPUT_TV);
+	if (nv_encoder) {
+		struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
+		struct drm_encoder_helper_funcs *helper =
+						encoder->helper_private;
+
+		if (helper->detect(encoder, connector) ==
+						connector_status_connected) {
+			nouveau_connector_set_encoder(connector, nv_encoder);
+			return connector_status_connected;
+		}
+
+	}
+
+	return connector_status_disconnected;
+}
+
+static void
+nouveau_connector_force(struct drm_connector *connector)
+{
+	struct drm_device *dev = connector->dev;
+	struct nouveau_encoder *nv_encoder;
+	int type;
+
+	if (connector->connector_type == DRM_MODE_CONNECTOR_DVII) {
+		if (connector->force == DRM_FORCE_ON_DIGITAL)
+			type = OUTPUT_TMDS;
+		else
+			type = OUTPUT_ANALOG;
+	} else
+		type = OUTPUT_ANY;
+
+	nv_encoder = find_encoder_by_type(connector, type);
+	if (!nv_encoder) {
+		NV_ERROR(dev, "can't find encoder to force %s on!\n",
+			 drm_get_connector_name(connector));
+		connector->status = connector_status_disconnected;
+		return;
+	}
+
+	nouveau_connector_set_encoder(connector, nv_encoder);
+}
+
+static int
+nouveau_connector_set_property(struct drm_connector *connector,
+			       struct drm_property *property, uint64_t value)
+{
+	struct nouveau_connector *nv_connector = nouveau_connector(connector);
+	struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
+	struct drm_device *dev = connector->dev;
+	int ret;
+
+	/* Scaling mode */
+	if (property == dev->mode_config.scaling_mode_property) {
+		struct nouveau_crtc *nv_crtc = NULL;
+		bool modeset = false;
+
+		switch (value) {
+		case DRM_MODE_SCALE_NONE:
+		case DRM_MODE_SCALE_FULLSCREEN:
+		case DRM_MODE_SCALE_CENTER:
+		case DRM_MODE_SCALE_ASPECT:
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		/* LVDS always needs gpu scaling */
+		if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS &&
+		    value == DRM_MODE_SCALE_NONE)
+			return -EINVAL;
+
+		/* Changing between GPU and panel scaling requires a full
+		 * modeset
+		 */
+		if ((nv_connector->scaling_mode == DRM_MODE_SCALE_NONE) ||
+		    (value == DRM_MODE_SCALE_NONE))
+			modeset = true;
+		nv_connector->scaling_mode = value;
+
+		if (connector->encoder && connector->encoder->crtc)
+			nv_crtc = nouveau_crtc(connector->encoder->crtc);
+		if (!nv_crtc)
+			return 0;
+
+		if (modeset || !nv_crtc->set_scale) {
+			ret = drm_crtc_helper_set_mode(&nv_crtc->base,
+							&nv_crtc->base.mode,
+							nv_crtc->base.x,
+							nv_crtc->base.y, NULL);
+			if (!ret)
+				return -EINVAL;
+		} else {
+			ret = nv_crtc->set_scale(nv_crtc, value, true);
+			if (ret)
+				return ret;
+		}
+
+		return 0;
+	}
+
+	/* Dithering */
+	if (property == dev->mode_config.dithering_mode_property) {
+		struct nouveau_crtc *nv_crtc = NULL;
+
+		if (value == DRM_MODE_DITHERING_ON)
+			nv_connector->use_dithering = true;
+		else
+			nv_connector->use_dithering = false;
+
+		if (connector->encoder && connector->encoder->crtc)
+			nv_crtc = nouveau_crtc(connector->encoder->crtc);
+
+		if (!nv_crtc || !nv_crtc->set_dither)
+			return 0;
+
+		return nv_crtc->set_dither(nv_crtc, nv_connector->use_dithering,
+					   true);
+	}
+
+	if (nv_encoder && nv_encoder->dcb->type == OUTPUT_TV)
+		return get_slave_funcs(nv_encoder)->
+			set_property(to_drm_encoder(nv_encoder), connector, property, value);
+
+	return -EINVAL;
+}
+
+static struct drm_display_mode *
+nouveau_connector_native_mode(struct nouveau_connector *connector)
+{
+	struct drm_device *dev = connector->base.dev;
+	struct drm_display_mode *mode, *largest = NULL;
+	int high_w = 0, high_h = 0, high_v = 0;
+
+	/* Use preferred mode if there is one.. */
+	list_for_each_entry(mode, &connector->base.probed_modes, head) {
+		if (mode->type & DRM_MODE_TYPE_PREFERRED) {
+			NV_DEBUG(dev, "native mode from preferred\n");
+			return drm_mode_duplicate(dev, mode);
+		}
+	}
+
+	/* Otherwise, take the resolution with the largest width, then height,
+	 * then vertical refresh
+	 */
+	list_for_each_entry(mode, &connector->base.probed_modes, head) {
+		if (mode->hdisplay < high_w)
+			continue;
+
+		if (mode->hdisplay == high_w && mode->vdisplay < high_h)
+			continue;
+
+		if (mode->hdisplay == high_w && mode->vdisplay == high_h &&
+		    mode->vrefresh < high_v)
+			continue;
+
+		high_w = mode->hdisplay;
+		high_h = mode->vdisplay;
+		high_v = mode->vrefresh;
+		largest = mode;
+	}
+
+	NV_DEBUG(dev, "native mode from largest: %dx%d@%d\n",
+		      high_w, high_h, high_v);
+	return largest ? drm_mode_duplicate(dev, largest) : NULL;
+}
+
+struct moderec {
+	int hdisplay;
+	int vdisplay;
+};
+
+static struct moderec scaler_modes[] = {
+	{ 1920, 1200 },
+	{ 1920, 1080 },
+	{ 1680, 1050 },
+	{ 1600, 1200 },
+	{ 1400, 1050 },
+	{ 1280, 1024 },
+	{ 1280, 960 },
+	{ 1152, 864 },
+	{ 1024, 768 },
+	{ 800, 600 },
+	{ 720, 400 },
+	{ 640, 480 },
+	{ 640, 400 },
+	{ 640, 350 },
+	{}
+};
+
+static int
+nouveau_connector_scaler_modes_add(struct drm_connector *connector)
+{
+	struct nouveau_connector *nv_connector = nouveau_connector(connector);
+	struct drm_display_mode *native = nv_connector->native_mode, *m;
+	struct drm_device *dev = connector->dev;
+	struct moderec *mode = &scaler_modes[0];
+	int modes = 0;
+
+	if (!native)
+		return 0;
+
+	while (mode->hdisplay) {
+		if (mode->hdisplay <= native->hdisplay &&
+		    mode->vdisplay <= native->vdisplay) {
+			m = drm_cvt_mode(dev, mode->hdisplay, mode->vdisplay,
+					 drm_mode_vrefresh(native), false,
+					 false, false);
+			if (!m)
+				continue;
+
+			m->type |= DRM_MODE_TYPE_DRIVER;
+
+			drm_mode_probed_add(connector, m);
+			modes++;
+		}
+
+		mode++;
+	}
+
+	return modes;
+}
+
+static int
+nouveau_connector_get_modes(struct drm_connector *connector)
+{
+	struct drm_device *dev = connector->dev;
+	struct nouveau_connector *nv_connector = nouveau_connector(connector);
+	struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
+	int ret = 0;
+
+	/* If we're not LVDS, destroy the previous native mode, the attached
+	 * monitor could have changed.
+	 */
+	if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS &&
+	    nv_connector->native_mode) {
+		drm_mode_destroy(dev, nv_connector->native_mode);
+		nv_connector->native_mode = NULL;
+	}
+
+	if (nv_connector->edid)
+		ret = drm_add_edid_modes(connector, nv_connector->edid);
+
+	/* Find the native mode if this is a digital panel, if we didn't
+	 * find any modes through DDC previously add the native mode to
+	 * the list of modes.
+	 */
+	if (!nv_connector->native_mode)
+		nv_connector->native_mode =
+			nouveau_connector_native_mode(nv_connector);
+	if (ret == 0 && nv_connector->native_mode) {
+		struct drm_display_mode *mode;
+
+		mode = drm_mode_duplicate(dev, nv_connector->native_mode);
+		drm_mode_probed_add(connector, mode);
+		ret = 1;
+	}
+
+	if (nv_encoder->dcb->type == OUTPUT_TV)
+		ret = get_slave_funcs(nv_encoder)->
+			get_modes(to_drm_encoder(nv_encoder), connector);
+
+	if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
+		ret += nouveau_connector_scaler_modes_add(connector);
+
+	return ret;
+}
+
+static int
+nouveau_connector_mode_valid(struct drm_connector *connector,
+			     struct drm_display_mode *mode)
+{
+	struct drm_nouveau_private *dev_priv = connector->dev->dev_private;
+	struct nouveau_connector *nv_connector = nouveau_connector(connector);
+	struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
+	unsigned min_clock = 25000, max_clock = min_clock;
+	unsigned clock = mode->clock;
+
+	switch (nv_encoder->dcb->type) {
+	case OUTPUT_LVDS:
+		BUG_ON(!nv_connector->native_mode);
+		if (mode->hdisplay > nv_connector->native_mode->hdisplay ||
+		    mode->vdisplay > nv_connector->native_mode->vdisplay)
+			return MODE_PANEL;
+
+		min_clock = 0;
+		max_clock = 400000;
+		break;
+	case OUTPUT_TMDS:
+		if ((dev_priv->card_type >= NV_50 && !nouveau_duallink) ||
+		    (dev_priv->card_type < NV_50 &&
+		     !nv_encoder->dcb->duallink_possible))
+			max_clock = 165000;
+		else
+			max_clock = 330000;
+		break;
+	case OUTPUT_ANALOG:
+		max_clock = nv_encoder->dcb->crtconf.maxfreq;
+		if (!max_clock)
+			max_clock = 350000;
+		break;
+	case OUTPUT_TV:
+		return get_slave_funcs(nv_encoder)->
+			mode_valid(to_drm_encoder(nv_encoder), mode);
+	case OUTPUT_DP:
+		if (nv_encoder->dp.link_bw == DP_LINK_BW_2_7)
+			max_clock = nv_encoder->dp.link_nr * 270000;
+		else
+			max_clock = nv_encoder->dp.link_nr * 162000;
+
+		clock *= 3;
+		break;
+	}
+
+	if (clock < min_clock)
+		return MODE_CLOCK_LOW;
+
+	if (clock > max_clock)
+		return MODE_CLOCK_HIGH;
+
+	return MODE_OK;
+}
+
+static struct drm_encoder *
+nouveau_connector_best_encoder(struct drm_connector *connector)
+{
+	struct nouveau_connector *nv_connector = nouveau_connector(connector);
+
+	if (nv_connector->detected_encoder)
+		return to_drm_encoder(nv_connector->detected_encoder);
+
+	return NULL;
+}
+
+static const struct drm_connector_helper_funcs
+nouveau_connector_helper_funcs = {
+	.get_modes = nouveau_connector_get_modes,
+	.mode_valid = nouveau_connector_mode_valid,
+	.best_encoder = nouveau_connector_best_encoder,
+};
+
+static const struct drm_connector_funcs
+nouveau_connector_funcs = {
+	.dpms = drm_helper_connector_dpms,
+	.save = NULL,
+	.restore = NULL,
+	.detect = nouveau_connector_detect,
+	.destroy = nouveau_connector_destroy,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.set_property = nouveau_connector_set_property,
+	.force = nouveau_connector_force
+};
+
+static int
+nouveau_connector_create_lvds(struct drm_device *dev,
+			      struct drm_connector *connector)
+{
+	struct nouveau_connector *nv_connector = nouveau_connector(connector);
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_i2c_chan *i2c = NULL;
+	struct nouveau_encoder *nv_encoder;
+	struct drm_display_mode native, *mode, *temp;
+	bool dummy, if_is_24bit = false;
+	int ret, flags;
+
+	nv_encoder = find_encoder_by_type(connector, OUTPUT_LVDS);
+	if (!nv_encoder)
+		return -ENODEV;
+
+	ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &if_is_24bit);
+	if (ret) {
+		NV_ERROR(dev, "Error parsing LVDS table, disabling LVDS\n");
+		return ret;
+	}
+	nv_connector->use_dithering = !if_is_24bit;
+
+	/* Firstly try getting EDID over DDC, if allowed and I2C channel
+	 * is available.
+	 */
+	if (!dev_priv->VBIOS.pub.fp_no_ddc && nv_encoder->dcb->i2c_index < 0xf)
+		i2c = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index);
+
+	if (i2c) {
+		nouveau_connector_ddc_prepare(connector, &flags);
+		nv_connector->edid = drm_get_edid(connector, &i2c->adapter);
+		nouveau_connector_ddc_finish(connector, flags);
+	}
+
+	/* If no EDID found above, and the VBIOS indicates a hardcoded
+	 * modeline is avalilable for the panel, set it as the panel's
+	 * native mode and exit.
+	 */
+	if (!nv_connector->edid && nouveau_bios_fp_mode(dev, &native) &&
+	     (nv_encoder->dcb->lvdsconf.use_straps_for_mode ||
+	      dev_priv->VBIOS.pub.fp_no_ddc)) {
+		nv_connector->native_mode = drm_mode_duplicate(dev, &native);
+		goto out;
+	}
+
+	/* Still nothing, some VBIOS images have a hardcoded EDID block
+	 * stored for the panel stored in them.
+	 */
+	if (!nv_connector->edid && !nv_connector->native_mode &&
+	    !dev_priv->VBIOS.pub.fp_no_ddc) {
+		nv_connector->edid =
+			(struct edid *)nouveau_bios_embedded_edid(dev);
+	}
+
+	if (!nv_connector->edid)
+		goto out;
+
+	/* We didn't find/use a panel mode from the VBIOS, so parse the EDID
+	 * block and look for the preferred mode there.
+	 */
+	ret = drm_add_edid_modes(connector, nv_connector->edid);
+	if (ret == 0)
+		goto out;
+	nv_connector->detected_encoder = nv_encoder;
+	nv_connector->native_mode = nouveau_connector_native_mode(nv_connector);
+	list_for_each_entry_safe(mode, temp, &connector->probed_modes, head)
+		drm_mode_remove(connector, mode);
+
+out:
+	if (!nv_connector->native_mode) {
+		NV_ERROR(dev, "LVDS present in DCB table, but couldn't "
+			      "determine its native mode.  Disabling.\n");
+		return -ENODEV;
+	}
+
+	drm_mode_connector_update_edid_property(connector, nv_connector->edid);
+	return 0;
+}
+
+int
+nouveau_connector_create(struct drm_device *dev, int index, int type)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_connector *nv_connector = NULL;
+	struct drm_connector *connector;
+	struct drm_encoder *encoder;
+	int ret;
+
+	NV_DEBUG(dev, "\n");
+
+	nv_connector = kzalloc(sizeof(*nv_connector), GFP_KERNEL);
+	if (!nv_connector)
+		return -ENOMEM;
+	nv_connector->dcb = nouveau_bios_connector_entry(dev, index);
+	connector = &nv_connector->base;
+
+	switch (type) {
+	case DRM_MODE_CONNECTOR_VGA:
+		NV_INFO(dev, "Detected a VGA connector\n");
+		break;
+	case DRM_MODE_CONNECTOR_DVID:
+		NV_INFO(dev, "Detected a DVI-D connector\n");
+		break;
+	case DRM_MODE_CONNECTOR_DVII:
+		NV_INFO(dev, "Detected a DVI-I connector\n");
+		break;
+	case DRM_MODE_CONNECTOR_LVDS:
+		NV_INFO(dev, "Detected a LVDS connector\n");
+		break;
+	case DRM_MODE_CONNECTOR_TV:
+		NV_INFO(dev, "Detected a TV connector\n");
+		break;
+	case DRM_MODE_CONNECTOR_DisplayPort:
+		NV_INFO(dev, "Detected a DisplayPort connector\n");
+		break;
+	default:
+		NV_ERROR(dev, "Unknown connector, this is not good.\n");
+		break;
+	}
+
+	/* defaults, will get overridden in detect() */
+	connector->interlace_allowed = false;
+	connector->doublescan_allowed = false;
+
+	drm_connector_init(dev, connector, &nouveau_connector_funcs, type);
+	drm_connector_helper_add(connector, &nouveau_connector_helper_funcs);
+
+	/* Init DVI-I specific properties */
+	if (type == DRM_MODE_CONNECTOR_DVII) {
+		drm_mode_create_dvi_i_properties(dev);
+		drm_connector_attach_property(connector, dev->mode_config.dvi_i_subconnector_property, 0);
+		drm_connector_attach_property(connector, dev->mode_config.dvi_i_select_subconnector_property, 0);
+	}
+
+	if (type != DRM_MODE_CONNECTOR_LVDS)
+		nv_connector->use_dithering = false;
+
+	if (type == DRM_MODE_CONNECTOR_DVID ||
+	    type == DRM_MODE_CONNECTOR_DVII ||
+	    type == DRM_MODE_CONNECTOR_LVDS ||
+	    type == DRM_MODE_CONNECTOR_DisplayPort) {
+		nv_connector->scaling_mode = DRM_MODE_SCALE_FULLSCREEN;
+
+		drm_connector_attach_property(connector, dev->mode_config.scaling_mode_property,
+					      nv_connector->scaling_mode);
+		drm_connector_attach_property(connector, dev->mode_config.dithering_mode_property,
+					      nv_connector->use_dithering ? DRM_MODE_DITHERING_ON
+					      : DRM_MODE_DITHERING_OFF);
+
+	} else {
+		nv_connector->scaling_mode = DRM_MODE_SCALE_NONE;
+
+		if (type == DRM_MODE_CONNECTOR_VGA  &&
+				dev_priv->card_type >= NV_50) {
+			drm_connector_attach_property(connector,
+					dev->mode_config.scaling_mode_property,
+					nv_connector->scaling_mode);
+		}
+	}
+
+	/* attach encoders */
+	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+		struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+
+		if (nv_encoder->dcb->connector != index)
+			continue;
+
+		if (get_slave_funcs(nv_encoder))
+			get_slave_funcs(nv_encoder)->create_resources(encoder, connector);
+
+		drm_mode_connector_attach_encoder(connector, encoder);
+	}
+
+	drm_sysfs_connector_add(connector);
+
+	if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
+		ret = nouveau_connector_create_lvds(dev, connector);
+		if (ret) {
+			connector->funcs->destroy(connector);
+			return ret;
+		}
+	}
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h
new file mode 100644
index 0000000..728b809
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef __NOUVEAU_CONNECTOR_H__
+#define __NOUVEAU_CONNECTOR_H__
+
+#include "drm_edid.h"
+#include "nouveau_i2c.h"
+
+struct nouveau_connector {
+	struct drm_connector base;
+
+	struct dcb_connector_table_entry *dcb;
+
+	int scaling_mode;
+	bool use_dithering;
+
+	struct nouveau_encoder *detected_encoder;
+	struct edid *edid;
+	struct drm_display_mode *native_mode;
+};
+
+static inline struct nouveau_connector *nouveau_connector(
+						struct drm_connector *con)
+{
+	return container_of(con, struct nouveau_connector, base);
+}
+
+int nouveau_connector_create(struct drm_device *dev, int i2c_index, int type);
+
+#endif /* __NOUVEAU_CONNECTOR_H__ */
diff --git a/drivers/gpu/drm/nouveau/nouveau_crtc.h b/drivers/gpu/drm/nouveau/nouveau_crtc.h
new file mode 100644
index 0000000..49fa7b2
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_crtc.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef __NOUVEAU_CRTC_H__
+#define __NOUVEAU_CRTC_H__
+
+struct nouveau_crtc {
+	struct drm_crtc base;
+
+	int index;
+
+	struct drm_display_mode *mode;
+
+	uint32_t dpms_saved_fp_control;
+	uint32_t fp_users;
+	int saturation;
+	int sharpness;
+	int last_dpms;
+
+	struct {
+		int cpp;
+		bool blanked;
+		uint32_t offset;
+		uint32_t tile_flags;
+	} fb;
+
+	struct {
+		struct nouveau_bo *nvbo;
+		bool visible;
+		uint32_t offset;
+		void (*set_offset)(struct nouveau_crtc *, uint32_t offset);
+		void (*set_pos)(struct nouveau_crtc *, int x, int y);
+		void (*hide)(struct nouveau_crtc *, bool update);
+		void (*show)(struct nouveau_crtc *, bool update);
+	} cursor;
+
+	struct {
+		struct nouveau_bo *nvbo;
+		uint16_t r[256];
+		uint16_t g[256];
+		uint16_t b[256];
+		int depth;
+	} lut;
+
+	int (*set_dither)(struct nouveau_crtc *crtc, bool on, bool update);
+	int (*set_scale)(struct nouveau_crtc *crtc, int mode, bool update);
+};
+
+static inline struct nouveau_crtc *nouveau_crtc(struct drm_crtc *crtc)
+{
+	return container_of(crtc, struct nouveau_crtc, base);
+}
+
+static inline struct drm_crtc *to_drm_crtc(struct nouveau_crtc *crtc)
+{
+	return &crtc->base;
+}
+
+int nv50_crtc_create(struct drm_device *dev, int index);
+int nv50_cursor_init(struct nouveau_crtc *);
+void nv50_cursor_fini(struct nouveau_crtc *);
+int nv50_crtc_cursor_set(struct drm_crtc *drm_crtc, struct drm_file *file_priv,
+			 uint32_t buffer_handle, uint32_t width,
+			 uint32_t height);
+int nv50_crtc_cursor_move(struct drm_crtc *drm_crtc, int x, int y);
+
+int nv04_cursor_init(struct nouveau_crtc *);
+
+struct nouveau_connector *
+nouveau_crtc_connector_get(struct nouveau_crtc *crtc);
+
+#endif /* __NOUVEAU_CRTC_H__ */
diff --git a/drivers/gpu/drm/nouveau/nouveau_debugfs.c b/drivers/gpu/drm/nouveau/nouveau_debugfs.c
new file mode 100644
index 0000000..d79db36
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_debugfs.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2009 Red Hat <bskeggs@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/*
+ * Authors:
+ *  Ben Skeggs <bskeggs@redhat.com>
+ */
+
+#include <linux/debugfs.h>
+
+#include "drmP.h"
+#include "nouveau_drv.h"
+
+static int
+nouveau_debugfs_channel_info(struct seq_file *m, void *data)
+{
+	struct drm_info_node *node = (struct drm_info_node *) m->private;
+	struct nouveau_channel *chan = node->info_ent->data;
+
+	seq_printf(m, "channel id    : %d\n", chan->id);
+
+	seq_printf(m, "cpu fifo state:\n");
+	seq_printf(m, "          base: 0x%08x\n", chan->pushbuf_base);
+	seq_printf(m, "           max: 0x%08x\n", chan->dma.max << 2);
+	seq_printf(m, "           cur: 0x%08x\n", chan->dma.cur << 2);
+	seq_printf(m, "           put: 0x%08x\n", chan->dma.put << 2);
+	seq_printf(m, "          free: 0x%08x\n", chan->dma.free << 2);
+
+	seq_printf(m, "gpu fifo state:\n");
+	seq_printf(m, "           get: 0x%08x\n",
+					nvchan_rd32(chan, chan->user_get));
+	seq_printf(m, "           put: 0x%08x\n",
+					nvchan_rd32(chan, chan->user_put));
+
+	seq_printf(m, "last fence    : %d\n", chan->fence.sequence);
+	seq_printf(m, "last signalled: %d\n", chan->fence.sequence_ack);
+	return 0;
+}
+
+int
+nouveau_debugfs_channel_init(struct nouveau_channel *chan)
+{
+	struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
+	struct drm_minor *minor = chan->dev->primary;
+	int ret;
+
+	if (!dev_priv->debugfs.channel_root) {
+		dev_priv->debugfs.channel_root =
+			debugfs_create_dir("channel", minor->debugfs_root);
+		if (!dev_priv->debugfs.channel_root)
+			return -ENOENT;
+	}
+
+	snprintf(chan->debugfs.name, 32, "%d", chan->id);
+	chan->debugfs.info.name = chan->debugfs.name;
+	chan->debugfs.info.show = nouveau_debugfs_channel_info;
+	chan->debugfs.info.driver_features = 0;
+	chan->debugfs.info.data = chan;
+
+	ret = drm_debugfs_create_files(&chan->debugfs.info, 1,
+				       dev_priv->debugfs.channel_root,
+				       chan->dev->primary);
+	if (ret == 0)
+		chan->debugfs.active = true;
+	return ret;
+}
+
+void
+nouveau_debugfs_channel_fini(struct nouveau_channel *chan)
+{
+	struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
+
+	if (!chan->debugfs.active)
+		return;
+
+	drm_debugfs_remove_files(&chan->debugfs.info, 1, chan->dev->primary);
+	chan->debugfs.active = false;
+
+	if (chan == dev_priv->channel) {
+		debugfs_remove(dev_priv->debugfs.channel_root);
+		dev_priv->debugfs.channel_root = NULL;
+	}
+}
+
+static int
+nouveau_debugfs_chipset_info(struct seq_file *m, void *data)
+{
+	struct drm_info_node *node = (struct drm_info_node *) m->private;
+	struct drm_minor *minor = node->minor;
+	struct drm_device *dev = minor->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	uint32_t ppci_0;
+
+	ppci_0 = nv_rd32(dev, dev_priv->chipset >= 0x40 ? 0x88000 : 0x1800);
+
+	seq_printf(m, "PMC_BOOT_0: 0x%08x\n", nv_rd32(dev, NV03_PMC_BOOT_0));
+	seq_printf(m, "PCI ID    : 0x%04x:0x%04x\n",
+		   ppci_0 & 0xffff, ppci_0 >> 16);
+	return 0;
+}
+
+static int
+nouveau_debugfs_memory_info(struct seq_file *m, void *data)
+{
+	struct drm_info_node *node = (struct drm_info_node *) m->private;
+	struct drm_minor *minor = node->minor;
+	struct drm_device *dev = minor->dev;
+
+	seq_printf(m, "VRAM total: %dKiB\n",
+		   (int)(nouveau_mem_fb_amount(dev) >> 10));
+	return 0;
+}
+
+static struct drm_info_list nouveau_debugfs_list[] = {
+	{ "chipset", nouveau_debugfs_chipset_info, 0, NULL },
+	{ "memory", nouveau_debugfs_memory_info, 0, NULL },
+};
+#define NOUVEAU_DEBUGFS_ENTRIES ARRAY_SIZE(nouveau_debugfs_list)
+
+int
+nouveau_debugfs_init(struct drm_minor *minor)
+{
+	drm_debugfs_create_files(nouveau_debugfs_list, NOUVEAU_DEBUGFS_ENTRIES,
+				 minor->debugfs_root, minor);
+	return 0;
+}
+
+void
+nouveau_debugfs_takedown(struct drm_minor *minor)
+{
+	drm_debugfs_remove_files(nouveau_debugfs_list, NOUVEAU_DEBUGFS_ENTRIES,
+				 minor);
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
new file mode 100644
index 0000000..dfc9439
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "drmP.h"
+#include "drm_crtc_helper.h"
+#include "nouveau_drv.h"
+#include "nouveau_fb.h"
+#include "nouveau_fbcon.h"
+
+static void
+nouveau_user_framebuffer_destroy(struct drm_framebuffer *drm_fb)
+{
+	struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb);
+	struct drm_device *dev = drm_fb->dev;
+
+	if (drm_fb->fbdev)
+		nouveau_fbcon_remove(dev, drm_fb);
+
+	if (fb->nvbo) {
+		mutex_lock(&dev->struct_mutex);
+		drm_gem_object_unreference(fb->nvbo->gem);
+		mutex_unlock(&dev->struct_mutex);
+	}
+
+	drm_framebuffer_cleanup(drm_fb);
+	kfree(fb);
+}
+
+static int
+nouveau_user_framebuffer_create_handle(struct drm_framebuffer *drm_fb,
+				       struct drm_file *file_priv,
+				       unsigned int *handle)
+{
+	struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb);
+
+	return drm_gem_handle_create(file_priv, fb->nvbo->gem, handle);
+}
+
+static const struct drm_framebuffer_funcs nouveau_framebuffer_funcs = {
+	.destroy = nouveau_user_framebuffer_destroy,
+	.create_handle = nouveau_user_framebuffer_create_handle,
+};
+
+struct drm_framebuffer *
+nouveau_framebuffer_create(struct drm_device *dev, struct nouveau_bo *nvbo,
+			   struct drm_mode_fb_cmd *mode_cmd)
+{
+	struct nouveau_framebuffer *fb;
+	int ret;
+
+	fb = kzalloc(sizeof(struct nouveau_framebuffer), GFP_KERNEL);
+	if (!fb)
+		return NULL;
+
+	ret = drm_framebuffer_init(dev, &fb->base, &nouveau_framebuffer_funcs);
+	if (ret) {
+		kfree(fb);
+		return NULL;
+	}
+
+	drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd);
+
+	fb->nvbo = nvbo;
+	return &fb->base;
+}
+
+static struct drm_framebuffer *
+nouveau_user_framebuffer_create(struct drm_device *dev,
+				struct drm_file *file_priv,
+				struct drm_mode_fb_cmd *mode_cmd)
+{
+	struct drm_framebuffer *fb;
+	struct drm_gem_object *gem;
+
+	gem = drm_gem_object_lookup(dev, file_priv, mode_cmd->handle);
+	if (!gem)
+		return NULL;
+
+	fb = nouveau_framebuffer_create(dev, nouveau_gem_object(gem), mode_cmd);
+	if (!fb) {
+		drm_gem_object_unreference(gem);
+		return NULL;
+	}
+
+	return fb;
+}
+
+const struct drm_mode_config_funcs nouveau_mode_config_funcs = {
+	.fb_create = nouveau_user_framebuffer_create,
+	.fb_changed = nouveau_fbcon_probe,
+};
+
diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.c b/drivers/gpu/drm/nouveau/nouveau_dma.c
new file mode 100644
index 0000000..7035536
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_dma.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2007 Ben Skeggs.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "drmP.h"
+#include "drm.h"
+#include "nouveau_drv.h"
+#include "nouveau_dma.h"
+
+int
+nouveau_dma_init(struct nouveau_channel *chan)
+{
+	struct drm_device *dev = chan->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_gpuobj *m2mf = NULL;
+	int ret, i;
+
+	/* Create NV_MEMORY_TO_MEMORY_FORMAT for buffer moves */
+	ret = nouveau_gpuobj_gr_new(chan, dev_priv->card_type < NV_50 ?
+				    0x0039 : 0x5039, &m2mf);
+	if (ret)
+		return ret;
+
+	ret = nouveau_gpuobj_ref_add(dev, chan, NvM2MF, m2mf, NULL);
+	if (ret)
+		return ret;
+
+	/* NV_MEMORY_TO_MEMORY_FORMAT requires a notifier object */
+	ret = nouveau_notifier_alloc(chan, NvNotify0, 32, &chan->m2mf_ntfy);
+	if (ret)
+		return ret;
+
+	/* Map push buffer */
+	ret = nouveau_bo_map(chan->pushbuf_bo);
+	if (ret)
+		return ret;
+
+	/* Map M2MF notifier object - fbcon. */
+	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+		ret = nouveau_bo_map(chan->notifier_bo);
+		if (ret)
+			return ret;
+	}
+
+	/* Initialise DMA vars */
+	chan->dma.max  = (chan->pushbuf_bo->bo.mem.size >> 2) - 2;
+	chan->dma.put  = 0;
+	chan->dma.cur  = chan->dma.put;
+	chan->dma.free = chan->dma.max - chan->dma.cur;
+
+	/* Insert NOPS for NOUVEAU_DMA_SKIPS */
+	ret = RING_SPACE(chan, NOUVEAU_DMA_SKIPS);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < NOUVEAU_DMA_SKIPS; i++)
+		OUT_RING(chan, 0);
+
+	/* Initialise NV_MEMORY_TO_MEMORY_FORMAT */
+	ret = RING_SPACE(chan, 4);
+	if (ret)
+		return ret;
+	BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_NAME, 1);
+	OUT_RING(chan, NvM2MF);
+	BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_DMA_NOTIFY, 1);
+	OUT_RING(chan, NvNotify0);
+
+	/* Sit back and pray the channel works.. */
+	FIRE_RING(chan);
+
+	return 0;
+}
+
+void
+OUT_RINGp(struct nouveau_channel *chan, const void *data, unsigned nr_dwords)
+{
+	bool is_iomem;
+	u32 *mem = ttm_kmap_obj_virtual(&chan->pushbuf_bo->kmap, &is_iomem);
+	mem = &mem[chan->dma.cur];
+	if (is_iomem)
+		memcpy_toio((void __force __iomem *)mem, data, nr_dwords * 4);
+	else
+		memcpy(mem, data, nr_dwords * 4);
+	chan->dma.cur += nr_dwords;
+}
+
+static inline bool
+READ_GET(struct nouveau_channel *chan, uint32_t *get)
+{
+	uint32_t val;
+
+	val = nvchan_rd32(chan, chan->user_get);
+	if (val < chan->pushbuf_base ||
+	    val >= chan->pushbuf_base + chan->pushbuf_bo->bo.mem.size) {
+		/* meaningless to dma_wait() except to know whether the
+		 * GPU has stalled or not
+		 */
+		*get = val;
+		return false;
+	}
+
+	*get = (val - chan->pushbuf_base) >> 2;
+	return true;
+}
+
+int
+nouveau_dma_wait(struct nouveau_channel *chan, int size)
+{
+	uint32_t get, prev_get = 0, cnt = 0;
+	bool get_valid;
+
+	while (chan->dma.free < size) {
+		/* reset counter as long as GET is still advancing, this is
+		 * to avoid misdetecting a GPU lockup if the GPU happens to
+		 * just be processing an operation that takes a long time
+		 */
+		get_valid = READ_GET(chan, &get);
+		if (get != prev_get) {
+			prev_get = get;
+			cnt = 0;
+		}
+
+		if ((++cnt & 0xff) == 0) {
+			DRM_UDELAY(1);
+			if (cnt > 100000)
+				return -EBUSY;
+		}
+
+		/* loop until we have a usable GET pointer.  the value
+		 * we read from the GPU may be outside the main ring if
+		 * PFIFO is processing a buffer called from the main ring,
+		 * discard these values until something sensible is seen.
+		 *
+		 * the other case we discard GET is while the GPU is fetching
+		 * from the SKIPS area, so the code below doesn't have to deal
+		 * with some fun corner cases.
+		 */
+		if (!get_valid || get < NOUVEAU_DMA_SKIPS)
+			continue;
+
+		if (get <= chan->dma.cur) {
+			/* engine is fetching behind us, or is completely
+			 * idle (GET == PUT) so we have free space up until
+			 * the end of the push buffer
+			 *
+			 * we can only hit that path once per call due to
+			 * looping back to the beginning of the push buffer,
+			 * we'll hit the fetching-ahead-of-us path from that
+			 * point on.
+			 *
+			 * the *one* exception to that rule is if we read
+			 * GET==PUT, in which case the below conditional will
+			 * always succeed and break us out of the wait loop.
+			 */
+			chan->dma.free = chan->dma.max - chan->dma.cur;
+			if (chan->dma.free >= size)
+				break;
+
+			/* not enough space left at the end of the push buffer,
+			 * instruct the GPU to jump back to the start right
+			 * after processing the currently pending commands.
+			 */
+			OUT_RING(chan, chan->pushbuf_base | 0x20000000);
+			WRITE_PUT(NOUVEAU_DMA_SKIPS);
+
+			/* we're now submitting commands at the start of
+			 * the push buffer.
+			 */
+			chan->dma.cur  =
+			chan->dma.put  = NOUVEAU_DMA_SKIPS;
+		}
+
+		/* engine fetching ahead of us, we have space up until the
+		 * current GET pointer.  the "- 1" is to ensure there's
+		 * space left to emit a jump back to the beginning of the
+		 * push buffer if we require it.  we can never get GET == PUT
+		 * here, so this is safe.
+		 */
+		chan->dma.free = get - chan->dma.cur - 1;
+	}
+
+	return 0;
+}
+
diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.h b/drivers/gpu/drm/nouveau/nouveau_dma.h
new file mode 100644
index 0000000..04e85d8
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_dma.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2007 Ben Skeggs.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef __NOUVEAU_DMA_H__
+#define __NOUVEAU_DMA_H__
+
+#ifndef NOUVEAU_DMA_DEBUG
+#define NOUVEAU_DMA_DEBUG 0
+#endif
+
+/*
+ * There's a hw race condition where you can't jump to your PUT offset,
+ * to avoid this we jump to offset + SKIPS and fill the difference with
+ * NOPs.
+ *
+ * xf86-video-nv configures the DMA fetch size to 32 bytes, and uses
+ * a SKIPS value of 8.  Lets assume that the race condition is to do
+ * with writing into the fetch area, we configure a fetch size of 128
+ * bytes so we need a larger SKIPS value.
+ */
+#define NOUVEAU_DMA_SKIPS (128 / 4)
+
+/* Hardcoded object assignments to subchannels (subchannel id). */
+enum {
+	NvSubM2MF	= 0,
+	NvSub2D		= 1,
+	NvSubCtxSurf2D  = 1,
+	NvSubGdiRect    = 2,
+	NvSubImageBlit  = 3
+};
+
+/* Object handles. */
+enum {
+	NvM2MF		= 0x80000001,
+	NvDmaFB		= 0x80000002,
+	NvDmaTT		= 0x80000003,
+	NvDmaVRAM	= 0x80000004,
+	NvDmaGART	= 0x80000005,
+	NvNotify0       = 0x80000006,
+	Nv2D		= 0x80000007,
+	NvCtxSurf2D	= 0x80000008,
+	NvRop		= 0x80000009,
+	NvImagePatt	= 0x8000000a,
+	NvClipRect	= 0x8000000b,
+	NvGdiRect	= 0x8000000c,
+	NvImageBlit	= 0x8000000d,
+
+	/* G80+ display objects */
+	NvEvoVRAM	= 0x01000000,
+	NvEvoFB16	= 0x01000001,
+	NvEvoFB32	= 0x01000002
+};
+
+#define NV_MEMORY_TO_MEMORY_FORMAT                                    0x00000039
+#define NV_MEMORY_TO_MEMORY_FORMAT_NAME                               0x00000000
+#define NV_MEMORY_TO_MEMORY_FORMAT_SET_REF                            0x00000050
+#define NV_MEMORY_TO_MEMORY_FORMAT_NOP                                0x00000100
+#define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY                             0x00000104
+#define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY_STYLE_WRITE                 0x00000000
+#define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY_STYLE_WRITE_LE_AWAKEN       0x00000001
+#define NV_MEMORY_TO_MEMORY_FORMAT_DMA_NOTIFY                         0x00000180
+#define NV_MEMORY_TO_MEMORY_FORMAT_DMA_SOURCE                         0x00000184
+#define NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN                          0x0000030c
+
+#define NV50_MEMORY_TO_MEMORY_FORMAT                                  0x00005039
+#define NV50_MEMORY_TO_MEMORY_FORMAT_UNK200                           0x00000200
+#define NV50_MEMORY_TO_MEMORY_FORMAT_UNK21C                           0x0000021c
+#define NV50_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN_HIGH                   0x00000238
+#define NV50_MEMORY_TO_MEMORY_FORMAT_OFFSET_OUT_HIGH                  0x0000023c
+
+static __must_check inline int
+RING_SPACE(struct nouveau_channel *chan, int size)
+{
+	if (chan->dma.free < size) {
+		int ret;
+
+		ret = nouveau_dma_wait(chan, size);
+		if (ret)
+			return ret;
+	}
+
+	chan->dma.free -= size;
+	return 0;
+}
+
+static inline void
+OUT_RING(struct nouveau_channel *chan, int data)
+{
+	if (NOUVEAU_DMA_DEBUG) {
+		NV_INFO(chan->dev, "Ch%d/0x%08x: 0x%08x\n",
+			chan->id, chan->dma.cur << 2, data);
+	}
+
+	nouveau_bo_wr32(chan->pushbuf_bo, chan->dma.cur++, data);
+}
+
+extern void
+OUT_RINGp(struct nouveau_channel *chan, const void *data, unsigned nr_dwords);
+
+static inline void
+BEGIN_RING(struct nouveau_channel *chan, int subc, int mthd, int size)
+{
+	OUT_RING(chan, (subc << 13) | (size << 18) | mthd);
+}
+
+#define WRITE_PUT(val) do {                                                    \
+	DRM_MEMORYBARRIER();                                                   \
+	nouveau_bo_rd32(chan->pushbuf_bo, 0);                                  \
+	nvchan_wr32(chan, chan->user_put, ((val) << 2) + chan->pushbuf_base);  \
+} while (0)
+
+static inline void
+FIRE_RING(struct nouveau_channel *chan)
+{
+	if (NOUVEAU_DMA_DEBUG) {
+		NV_INFO(chan->dev, "Ch%d/0x%08x: PUSH!\n",
+			chan->id, chan->dma.cur << 2);
+	}
+
+	if (chan->dma.cur == chan->dma.put)
+		return;
+	chan->accel_done = true;
+
+	WRITE_PUT(chan->dma.cur);
+	chan->dma.put = chan->dma.cur;
+}
+
+static inline void
+WIND_RING(struct nouveau_channel *chan)
+{
+	chan->dma.cur = chan->dma.put;
+}
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c
new file mode 100644
index 0000000..de61f46
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_dp.c
@@ -0,0 +1,569 @@
+/*
+ * Copyright 2009 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "drmP.h"
+#include "nouveau_drv.h"
+#include "nouveau_i2c.h"
+#include "nouveau_encoder.h"
+
+static int
+auxch_rd(struct drm_encoder *encoder, int address, uint8_t *buf, int size)
+{
+	struct drm_device *dev = encoder->dev;
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct nouveau_i2c_chan *auxch;
+	int ret;
+
+	auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index);
+	if (!auxch)
+		return -ENODEV;
+
+	ret = nouveau_dp_auxch(auxch, 9, address, buf, size);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int
+auxch_wr(struct drm_encoder *encoder, int address, uint8_t *buf, int size)
+{
+	struct drm_device *dev = encoder->dev;
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct nouveau_i2c_chan *auxch;
+	int ret;
+
+	auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index);
+	if (!auxch)
+		return -ENODEV;
+
+	ret = nouveau_dp_auxch(auxch, 8, address, buf, size);
+	return ret;
+}
+
+static int
+nouveau_dp_lane_count_set(struct drm_encoder *encoder, uint8_t cmd)
+{
+	struct drm_device *dev = encoder->dev;
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	uint32_t tmp;
+	int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1);
+
+	tmp  = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link));
+	tmp &= ~(NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED |
+		 NV50_SOR_DP_CTRL_LANE_MASK);
+	tmp |= ((1 << (cmd & DP_LANE_COUNT_MASK)) - 1) << 16;
+	if (cmd & DP_LANE_COUNT_ENHANCED_FRAME_EN)
+		tmp |= NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED;
+	nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp);
+
+	return auxch_wr(encoder, DP_LANE_COUNT_SET, &cmd, 1);
+}
+
+static int
+nouveau_dp_link_bw_set(struct drm_encoder *encoder, uint8_t cmd)
+{
+	struct drm_device *dev = encoder->dev;
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	uint32_t tmp;
+	int reg = 0x614300 + (nv_encoder->or * 0x800);
+
+	tmp  = nv_rd32(dev, reg);
+	tmp &= 0xfff3ffff;
+	if (cmd == DP_LINK_BW_2_7)
+		tmp |= 0x00040000;
+	nv_wr32(dev, reg, tmp);
+
+	return auxch_wr(encoder, DP_LINK_BW_SET, &cmd, 1);
+}
+
+static int
+nouveau_dp_link_train_set(struct drm_encoder *encoder, int pattern)
+{
+	struct drm_device *dev = encoder->dev;
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	uint32_t tmp;
+	uint8_t cmd;
+	int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1);
+	int ret;
+
+	tmp  = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link));
+	tmp &= ~NV50_SOR_DP_CTRL_TRAINING_PATTERN;
+	tmp |= (pattern << 24);
+	nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp);
+
+	ret = auxch_rd(encoder, DP_TRAINING_PATTERN_SET, &cmd, 1);
+	if (ret)
+		return ret;
+	cmd &= ~DP_TRAINING_PATTERN_MASK;
+	cmd |= (pattern & DP_TRAINING_PATTERN_MASK);
+	return auxch_wr(encoder, DP_TRAINING_PATTERN_SET, &cmd, 1);
+}
+
+static int
+nouveau_dp_max_voltage_swing(struct drm_encoder *encoder)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct drm_device *dev = encoder->dev;
+	struct bit_displayport_encoder_table_entry *dpse;
+	struct bit_displayport_encoder_table *dpe;
+	int i, dpe_headerlen, max_vs = 0;
+
+	dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen);
+	if (!dpe)
+		return false;
+	dpse = (void *)((char *)dpe + dpe_headerlen);
+
+	for (i = 0; i < dpe_headerlen; i++, dpse++) {
+		if (dpse->vs_level > max_vs)
+			max_vs = dpse->vs_level;
+	}
+
+	return max_vs;
+}
+
+static int
+nouveau_dp_max_pre_emphasis(struct drm_encoder *encoder, int vs)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct drm_device *dev = encoder->dev;
+	struct bit_displayport_encoder_table_entry *dpse;
+	struct bit_displayport_encoder_table *dpe;
+	int i, dpe_headerlen, max_pre = 0;
+
+	dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen);
+	if (!dpe)
+		return false;
+	dpse = (void *)((char *)dpe + dpe_headerlen);
+
+	for (i = 0; i < dpe_headerlen; i++, dpse++) {
+		if (dpse->vs_level != vs)
+			continue;
+
+		if (dpse->pre_level > max_pre)
+			max_pre = dpse->pre_level;
+	}
+
+	return max_pre;
+}
+
+static bool
+nouveau_dp_link_train_adjust(struct drm_encoder *encoder, uint8_t *config)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct drm_device *dev = encoder->dev;
+	struct bit_displayport_encoder_table_entry *dpse;
+	struct bit_displayport_encoder_table *dpe;
+	int ret, i, dpe_headerlen, vs = 0, pre = 0;
+	uint8_t request[2];
+
+	dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen);
+	if (!dpe)
+		return false;
+	dpse = (void *)((char *)dpe + dpe_headerlen);
+
+	ret = auxch_rd(encoder, DP_ADJUST_REQUEST_LANE0_1, request, 2);
+	if (ret)
+		return false;
+
+	NV_DEBUG(dev, "\t\tadjust 0x%02x 0x%02x\n", request[0], request[1]);
+
+	/* Keep all lanes at the same level.. */
+	for (i = 0; i < nv_encoder->dp.link_nr; i++) {
+		int lane_req = (request[i >> 1] >> ((i & 1) << 2)) & 0xf;
+		int lane_vs = lane_req & 3;
+		int lane_pre = (lane_req >> 2) & 3;
+
+		if (lane_vs > vs)
+			vs = lane_vs;
+		if (lane_pre > pre)
+			pre = lane_pre;
+	}
+
+	if (vs >= nouveau_dp_max_voltage_swing(encoder)) {
+		vs  = nouveau_dp_max_voltage_swing(encoder);
+		vs |= 4;
+	}
+
+	if (pre >= nouveau_dp_max_pre_emphasis(encoder, vs & 3)) {
+		pre  = nouveau_dp_max_pre_emphasis(encoder, vs & 3);
+		pre |= 4;
+	}
+
+	/* Update the configuration for all lanes.. */
+	for (i = 0; i < nv_encoder->dp.link_nr; i++)
+		config[i] = (pre << 3) | vs;
+
+	return true;
+}
+
+static bool
+nouveau_dp_link_train_commit(struct drm_encoder *encoder, uint8_t *config)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct drm_device *dev = encoder->dev;
+	struct bit_displayport_encoder_table_entry *dpse;
+	struct bit_displayport_encoder_table *dpe;
+	int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1);
+	int dpe_headerlen, ret, i;
+
+	NV_DEBUG(dev, "\t\tconfig 0x%02x 0x%02x 0x%02x 0x%02x\n",
+		 config[0], config[1], config[2], config[3]);
+
+	dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen);
+	if (!dpe)
+		return false;
+	dpse = (void *)((char *)dpe + dpe_headerlen);
+
+	for (i = 0; i < dpe->record_nr; i++, dpse++) {
+		if (dpse->vs_level == (config[0] & 3) &&
+		    dpse->pre_level == ((config[0] >> 3) & 3))
+			break;
+	}
+	BUG_ON(i == dpe->record_nr);
+
+	for (i = 0; i < nv_encoder->dp.link_nr; i++) {
+		const int shift[4] = { 16, 8, 0, 24 };
+		uint32_t mask = 0xff << shift[i];
+		uint32_t reg0, reg1, reg2;
+
+		reg0  = nv_rd32(dev, NV50_SOR_DP_UNK118(or, link)) & ~mask;
+		reg0 |= (dpse->reg0 << shift[i]);
+		reg1  = nv_rd32(dev, NV50_SOR_DP_UNK120(or, link)) & ~mask;
+		reg1 |= (dpse->reg1 << shift[i]);
+		reg2  = nv_rd32(dev, NV50_SOR_DP_UNK130(or, link)) & 0xffff00ff;
+		reg2 |= (dpse->reg2 << 8);
+		nv_wr32(dev, NV50_SOR_DP_UNK118(or, link), reg0);
+		nv_wr32(dev, NV50_SOR_DP_UNK120(or, link), reg1);
+		nv_wr32(dev, NV50_SOR_DP_UNK130(or, link), reg2);
+	}
+
+	ret = auxch_wr(encoder, DP_TRAINING_LANE0_SET, config, 4);
+	if (ret)
+		return false;
+
+	return true;
+}
+
+bool
+nouveau_dp_link_train(struct drm_encoder *encoder)
+{
+	struct drm_device *dev = encoder->dev;
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	uint8_t config[4];
+	uint8_t status[3];
+	bool cr_done, cr_max_vs, eq_done;
+	int ret = 0, i, tries, voltage;
+
+	NV_DEBUG(dev, "link training!!\n");
+train:
+	cr_done = eq_done = false;
+
+	/* set link configuration */
+	NV_DEBUG(dev, "\tbegin train: bw %d, lanes %d\n",
+		 nv_encoder->dp.link_bw, nv_encoder->dp.link_nr);
+
+	ret = nouveau_dp_link_bw_set(encoder, nv_encoder->dp.link_bw);
+	if (ret)
+		return false;
+
+	config[0] = nv_encoder->dp.link_nr;
+	if (nv_encoder->dp.dpcd_version >= 0x11)
+		config[0] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
+
+	ret = nouveau_dp_lane_count_set(encoder, config[0]);
+	if (ret)
+		return false;
+
+	/* clock recovery */
+	NV_DEBUG(dev, "\tbegin cr\n");
+	ret = nouveau_dp_link_train_set(encoder, DP_TRAINING_PATTERN_1);
+	if (ret)
+		goto stop;
+
+	tries = 0;
+	voltage = -1;
+	memset(config, 0x00, sizeof(config));
+	for (;;) {
+		if (!nouveau_dp_link_train_commit(encoder, config))
+			break;
+
+		udelay(100);
+
+		ret = auxch_rd(encoder, DP_LANE0_1_STATUS, status, 2);
+		if (ret)
+			break;
+		NV_DEBUG(dev, "\t\tstatus: 0x%02x 0x%02x\n",
+			 status[0], status[1]);
+
+		cr_done = true;
+		cr_max_vs = false;
+		for (i = 0; i < nv_encoder->dp.link_nr; i++) {
+			int lane = (status[i >> 1] >> ((i & 1) * 4)) & 0xf;
+
+			if (!(lane & DP_LANE_CR_DONE)) {
+				cr_done = false;
+				if (config[i] & DP_TRAIN_MAX_PRE_EMPHASIS_REACHED)
+					cr_max_vs = true;
+				break;
+			}
+		}
+
+		if ((config[0] & DP_TRAIN_VOLTAGE_SWING_MASK) != voltage) {
+			voltage = config[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
+			tries = 0;
+		}
+
+		if (cr_done || cr_max_vs || (++tries == 5))
+			break;
+
+		if (!nouveau_dp_link_train_adjust(encoder, config))
+			break;
+	}
+
+	if (!cr_done)
+		goto stop;
+
+	/* channel equalisation */
+	NV_DEBUG(dev, "\tbegin eq\n");
+	ret = nouveau_dp_link_train_set(encoder, DP_TRAINING_PATTERN_2);
+	if (ret)
+		goto stop;
+
+	for (tries = 0; tries <= 5; tries++) {
+		udelay(400);
+
+		ret = auxch_rd(encoder, DP_LANE0_1_STATUS, status, 3);
+		if (ret)
+			break;
+		NV_DEBUG(dev, "\t\tstatus: 0x%02x 0x%02x\n",
+			 status[0], status[1]);
+
+		eq_done = true;
+		if (!(status[2] & DP_INTERLANE_ALIGN_DONE))
+			eq_done = false;
+
+		for (i = 0; eq_done && i < nv_encoder->dp.link_nr; i++) {
+			int lane = (status[i >> 1] >> ((i & 1) * 4)) & 0xf;
+
+			if (!(lane & DP_LANE_CR_DONE)) {
+				cr_done = false;
+				break;
+			}
+
+			if (!(lane & DP_LANE_CHANNEL_EQ_DONE) ||
+			    !(lane & DP_LANE_SYMBOL_LOCKED)) {
+				eq_done = false;
+				break;
+			}
+		}
+
+		if (eq_done || !cr_done)
+			break;
+
+		if (!nouveau_dp_link_train_adjust(encoder, config) ||
+		    !nouveau_dp_link_train_commit(encoder, config))
+			break;
+	}
+
+stop:
+	/* end link training */
+	ret = nouveau_dp_link_train_set(encoder, DP_TRAINING_PATTERN_DISABLE);
+	if (ret)
+		return false;
+
+	/* retry at a lower setting, if possible */
+	if (!ret && !(eq_done && cr_done)) {
+		NV_DEBUG(dev, "\twe failed\n");
+		if (nv_encoder->dp.link_bw != DP_LINK_BW_1_62) {
+			NV_DEBUG(dev, "retry link training at low rate\n");
+			nv_encoder->dp.link_bw = DP_LINK_BW_1_62;
+			goto train;
+		}
+	}
+
+	return eq_done;
+}
+
+bool
+nouveau_dp_detect(struct drm_encoder *encoder)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct drm_device *dev = encoder->dev;
+	uint8_t dpcd[4];
+	int ret;
+
+	ret = auxch_rd(encoder, 0x0000, dpcd, 4);
+	if (ret)
+		return false;
+
+	NV_DEBUG(dev, "encoder: link_bw %d, link_nr %d\n"
+		      "display: link_bw %d, link_nr %d version 0x%02x\n",
+		 nv_encoder->dcb->dpconf.link_bw,
+		 nv_encoder->dcb->dpconf.link_nr,
+		 dpcd[1], dpcd[2] & 0x0f, dpcd[0]);
+
+	nv_encoder->dp.dpcd_version = dpcd[0];
+
+	nv_encoder->dp.link_bw = dpcd[1];
+	if (nv_encoder->dp.link_bw != DP_LINK_BW_1_62 &&
+	    !nv_encoder->dcb->dpconf.link_bw)
+		nv_encoder->dp.link_bw = DP_LINK_BW_1_62;
+
+	nv_encoder->dp.link_nr = dpcd[2] & 0xf;
+	if (nv_encoder->dp.link_nr > nv_encoder->dcb->dpconf.link_nr)
+		nv_encoder->dp.link_nr = nv_encoder->dcb->dpconf.link_nr;
+
+	return true;
+}
+
+int
+nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr,
+		 uint8_t *data, int data_nr)
+{
+	struct drm_device *dev = auxch->dev;
+	uint32_t tmp, ctrl, stat = 0, data32[4] = {};
+	int ret = 0, i, index = auxch->rd;
+
+	NV_DEBUG(dev, "ch %d cmd %d addr 0x%x len %d\n", index, cmd, addr, data_nr);
+
+	tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd));
+	nv_wr32(dev, NV50_AUXCH_CTRL(auxch->rd), tmp | 0x00100000);
+	tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd));
+	if (!(tmp & 0x01000000)) {
+		NV_ERROR(dev, "expected bit 24 == 1, got 0x%08x\n", tmp);
+		ret = -EIO;
+		goto out;
+	}
+
+	for (i = 0; i < 3; i++) {
+		tmp = nv_rd32(dev, NV50_AUXCH_STAT(auxch->rd));
+		if (tmp & NV50_AUXCH_STAT_STATE_READY)
+			break;
+		udelay(100);
+	}
+
+	if (i == 3) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	if (!(cmd & 1)) {
+		memcpy(data32, data, data_nr);
+		for (i = 0; i < 4; i++) {
+			NV_DEBUG(dev, "wr %d: 0x%08x\n", i, data32[i]);
+			nv_wr32(dev, NV50_AUXCH_DATA_OUT(index, i), data32[i]);
+		}
+	}
+
+	nv_wr32(dev, NV50_AUXCH_ADDR(index), addr);
+	ctrl  = nv_rd32(dev, NV50_AUXCH_CTRL(index));
+	ctrl &= ~(NV50_AUXCH_CTRL_CMD | NV50_AUXCH_CTRL_LEN);
+	ctrl |= (cmd << NV50_AUXCH_CTRL_CMD_SHIFT);
+	ctrl |= ((data_nr - 1) << NV50_AUXCH_CTRL_LEN_SHIFT);
+
+	for (;;) {
+		nv_wr32(dev, NV50_AUXCH_CTRL(index), ctrl | 0x80000000);
+		nv_wr32(dev, NV50_AUXCH_CTRL(index), ctrl);
+		nv_wr32(dev, NV50_AUXCH_CTRL(index), ctrl | 0x00010000);
+		if (!nv_wait(NV50_AUXCH_CTRL(index), 0x00010000, 0x00000000)) {
+			NV_ERROR(dev, "expected bit 16 == 0, got 0x%08x\n",
+				 nv_rd32(dev, NV50_AUXCH_CTRL(index)));
+			return -EBUSY;
+		}
+
+		udelay(400);
+
+		stat = nv_rd32(dev, NV50_AUXCH_STAT(index));
+		if ((stat & NV50_AUXCH_STAT_REPLY_AUX) !=
+			    NV50_AUXCH_STAT_REPLY_AUX_DEFER)
+			break;
+	}
+
+	if (cmd & 1) {
+		for (i = 0; i < 4; i++) {
+			data32[i] = nv_rd32(dev, NV50_AUXCH_DATA_IN(index, i));
+			NV_DEBUG(dev, "rd %d: 0x%08x\n", i, data32[i]);
+		}
+		memcpy(data, data32, data_nr);
+	}
+
+out:
+	tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd));
+	nv_wr32(dev, NV50_AUXCH_CTRL(auxch->rd), tmp & ~0x00100000);
+	tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd));
+	if (tmp & 0x01000000) {
+		NV_ERROR(dev, "expected bit 24 == 0, got 0x%08x\n", tmp);
+		ret = -EIO;
+	}
+
+	udelay(400);
+
+	return ret ? ret : (stat & NV50_AUXCH_STAT_REPLY);
+}
+
+int
+nouveau_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
+		      uint8_t write_byte, uint8_t *read_byte)
+{
+	struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
+	struct nouveau_i2c_chan *auxch = (struct nouveau_i2c_chan *)adapter;
+	struct drm_device *dev = auxch->dev;
+	int ret = 0, cmd, addr = algo_data->address;
+	uint8_t *buf;
+
+	if (mode == MODE_I2C_READ) {
+		cmd = AUX_I2C_READ;
+		buf = read_byte;
+	} else {
+		cmd = (mode & MODE_I2C_READ) ? AUX_I2C_READ : AUX_I2C_WRITE;
+		buf = &write_byte;
+	}
+
+	if (!(mode & MODE_I2C_STOP))
+		cmd |= AUX_I2C_MOT;
+
+	if (mode & MODE_I2C_START)
+		return 1;
+
+	for (;;) {
+		ret = nouveau_dp_auxch(auxch, cmd, addr, buf, 1);
+		if (ret < 0)
+			return ret;
+
+		switch (ret & NV50_AUXCH_STAT_REPLY_I2C) {
+		case NV50_AUXCH_STAT_REPLY_I2C_ACK:
+			return 1;
+		case NV50_AUXCH_STAT_REPLY_I2C_NACK:
+			return -EREMOTEIO;
+		case NV50_AUXCH_STAT_REPLY_I2C_DEFER:
+			udelay(100);
+			break;
+		default:
+			NV_ERROR(dev, "invalid auxch status: 0x%08x\n", ret);
+			return -EREMOTEIO;
+		}
+	}
+}
+
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c
new file mode 100644
index 0000000..35249c3
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.c
@@ -0,0 +1,405 @@
+/*
+ * Copyright 2005 Stephane Marchesin.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <linux/console.h>
+
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc_helper.h"
+#include "nouveau_drv.h"
+#include "nouveau_hw.h"
+#include "nouveau_fb.h"
+#include "nouveau_fbcon.h"
+#include "nv50_display.h"
+
+#include "drm_pciids.h"
+
+MODULE_PARM_DESC(noagp, "Disable AGP");
+int nouveau_noagp;
+module_param_named(noagp, nouveau_noagp, int, 0400);
+
+MODULE_PARM_DESC(modeset, "Enable kernel modesetting");
+static int nouveau_modeset = -1; /* kms */
+module_param_named(modeset, nouveau_modeset, int, 0400);
+
+MODULE_PARM_DESC(vbios, "Override default VBIOS location");
+char *nouveau_vbios;
+module_param_named(vbios, nouveau_vbios, charp, 0400);
+
+MODULE_PARM_DESC(vram_pushbuf, "Force DMA push buffers to be in VRAM");
+int nouveau_vram_pushbuf;
+module_param_named(vram_pushbuf, nouveau_vram_pushbuf, int, 0400);
+
+MODULE_PARM_DESC(vram_notify, "Force DMA notifiers to be in VRAM");
+int nouveau_vram_notify;
+module_param_named(vram_notify, nouveau_vram_notify, int, 0400);
+
+MODULE_PARM_DESC(duallink, "Allow dual-link TMDS (>=GeForce 8)");
+int nouveau_duallink = 1;
+module_param_named(duallink, nouveau_duallink, int, 0400);
+
+MODULE_PARM_DESC(uscript_lvds, "LVDS output script table ID (>=GeForce 8)");
+int nouveau_uscript_lvds = -1;
+module_param_named(uscript_lvds, nouveau_uscript_lvds, int, 0400);
+
+MODULE_PARM_DESC(uscript_tmds, "TMDS output script table ID (>=GeForce 8)");
+int nouveau_uscript_tmds = -1;
+module_param_named(uscript_tmds, nouveau_uscript_tmds, int, 0400);
+
+MODULE_PARM_DESC(tv_norm, "Default TV norm.\n"
+		 "\t\tSupported: PAL, PAL-M, PAL-N, PAL-Nc, NTSC-M, NTSC-J,\n"
+		 "\t\t\thd480i, hd480p, hd576i, hd576p, hd720p, hd1080i.\n"
+		 "\t\tDefault: PAL\n"
+		 "\t\t*NOTE* Ignored for cards with external TV encoders.");
+char *nouveau_tv_norm;
+module_param_named(tv_norm, nouveau_tv_norm, charp, 0400);
+
+MODULE_PARM_DESC(reg_debug, "Register access debug bitmask:\n"
+		"\t\t0x1 mc, 0x2 video, 0x4 fb, 0x8 extdev,\n"
+		"\t\t0x10 crtc, 0x20 ramdac, 0x40 vgacrtc, 0x80 rmvio,\n"
+		"\t\t0x100 vgaattr, 0x200 EVO (G80+). ");
+int nouveau_reg_debug;
+module_param_named(reg_debug, nouveau_reg_debug, int, 0600);
+
+int nouveau_fbpercrtc;
+#if 0
+module_param_named(fbpercrtc, nouveau_fbpercrtc, int, 0400);
+#endif
+
+static struct pci_device_id pciidlist[] = {
+	{
+		PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID),
+		.class = PCI_BASE_CLASS_DISPLAY << 16,
+		.class_mask  = 0xff << 16,
+	},
+	{
+		PCI_DEVICE(PCI_VENDOR_ID_NVIDIA_SGS, PCI_ANY_ID),
+		.class = PCI_BASE_CLASS_DISPLAY << 16,
+		.class_mask  = 0xff << 16,
+	},
+	{}
+};
+
+MODULE_DEVICE_TABLE(pci, pciidlist);
+
+static struct drm_driver driver;
+
+static int __devinit
+nouveau_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	return drm_get_dev(pdev, ent, &driver);
+}
+
+static void
+nouveau_pci_remove(struct pci_dev *pdev)
+{
+	struct drm_device *dev = pci_get_drvdata(pdev);
+
+	drm_put_dev(dev);
+}
+
+static int
+nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state)
+{
+	struct drm_device *dev = pci_get_drvdata(pdev);
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_instmem_engine *pinstmem = &dev_priv->engine.instmem;
+	struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
+	struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
+	struct nouveau_channel *chan;
+	struct drm_crtc *crtc;
+	uint32_t fbdev_flags;
+	int ret, i;
+
+	if (!drm_core_check_feature(dev, DRIVER_MODESET))
+		return -ENODEV;
+
+	if (pm_state.event == PM_EVENT_PRETHAW)
+		return 0;
+
+	fbdev_flags = dev_priv->fbdev_info->flags;
+	dev_priv->fbdev_info->flags |= FBINFO_HWACCEL_DISABLED;
+
+	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+		struct nouveau_framebuffer *nouveau_fb;
+
+		nouveau_fb = nouveau_framebuffer(crtc->fb);
+		if (!nouveau_fb || !nouveau_fb->nvbo)
+			continue;
+
+		nouveau_bo_unpin(nouveau_fb->nvbo);
+	}
+
+	NV_INFO(dev, "Evicting buffers...\n");
+	ttm_bo_evict_mm(&dev_priv->ttm.bdev, TTM_PL_VRAM);
+
+	NV_INFO(dev, "Idling channels...\n");
+	for (i = 0; i < pfifo->channels; i++) {
+		struct nouveau_fence *fence = NULL;
+
+		chan = dev_priv->fifos[i];
+		if (!chan || (dev_priv->card_type >= NV_50 &&
+			      chan == dev_priv->fifos[0]))
+			continue;
+
+		ret = nouveau_fence_new(chan, &fence, true);
+		if (ret == 0) {
+			ret = nouveau_fence_wait(fence, NULL, false, false);
+			nouveau_fence_unref((void *)&fence);
+		}
+
+		if (ret) {
+			NV_ERROR(dev, "Failed to idle channel %d for suspend\n",
+				 chan->id);
+		}
+	}
+
+	pgraph->fifo_access(dev, false);
+	nouveau_wait_for_idle(dev);
+	pfifo->reassign(dev, false);
+	pfifo->disable(dev);
+	pfifo->unload_context(dev);
+	pgraph->unload_context(dev);
+
+	NV_INFO(dev, "Suspending GPU objects...\n");
+	ret = nouveau_gpuobj_suspend(dev);
+	if (ret) {
+		NV_ERROR(dev, "... failed: %d\n", ret);
+		goto out_abort;
+	}
+
+	ret = pinstmem->suspend(dev);
+	if (ret) {
+		NV_ERROR(dev, "... failed: %d\n", ret);
+		nouveau_gpuobj_suspend_cleanup(dev);
+		goto out_abort;
+	}
+
+	NV_INFO(dev, "And we're gone!\n");
+	pci_save_state(pdev);
+	if (pm_state.event == PM_EVENT_SUSPEND) {
+		pci_disable_device(pdev);
+		pci_set_power_state(pdev, PCI_D3hot);
+	}
+
+	acquire_console_sem();
+	fb_set_suspend(dev_priv->fbdev_info, 1);
+	release_console_sem();
+	dev_priv->fbdev_info->flags = fbdev_flags;
+	return 0;
+
+out_abort:
+	NV_INFO(dev, "Re-enabling acceleration..\n");
+	pfifo->enable(dev);
+	pfifo->reassign(dev, true);
+	pgraph->fifo_access(dev, true);
+	return ret;
+}
+
+static int
+nouveau_pci_resume(struct pci_dev *pdev)
+{
+	struct drm_device *dev = pci_get_drvdata(pdev);
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_engine *engine = &dev_priv->engine;
+	struct drm_crtc *crtc;
+	uint32_t fbdev_flags;
+	int ret, i;
+
+	if (!drm_core_check_feature(dev, DRIVER_MODESET))
+		return -ENODEV;
+
+	fbdev_flags = dev_priv->fbdev_info->flags;
+	dev_priv->fbdev_info->flags |= FBINFO_HWACCEL_DISABLED;
+
+	NV_INFO(dev, "We're back, enabling device...\n");
+	pci_set_power_state(pdev, PCI_D0);
+	pci_restore_state(pdev);
+	if (pci_enable_device(pdev))
+		return -1;
+	pci_set_master(dev->pdev);
+
+	NV_INFO(dev, "POSTing device...\n");
+	ret = nouveau_run_vbios_init(dev);
+	if (ret)
+		return ret;
+
+	if (dev_priv->gart_info.type == NOUVEAU_GART_AGP) {
+		ret = nouveau_mem_init_agp(dev);
+		if (ret) {
+			NV_ERROR(dev, "error reinitialising AGP: %d\n", ret);
+			return ret;
+		}
+	}
+
+	NV_INFO(dev, "Reinitialising engines...\n");
+	engine->instmem.resume(dev);
+	engine->mc.init(dev);
+	engine->timer.init(dev);
+	engine->fb.init(dev);
+	engine->graph.init(dev);
+	engine->fifo.init(dev);
+
+	NV_INFO(dev, "Restoring GPU objects...\n");
+	nouveau_gpuobj_resume(dev);
+
+	nouveau_irq_postinstall(dev);
+
+	/* Re-write SKIPS, they'll have been lost over the suspend */
+	if (nouveau_vram_pushbuf) {
+		struct nouveau_channel *chan;
+		int j;
+
+		for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
+			chan = dev_priv->fifos[i];
+			if (!chan)
+				continue;
+
+			for (j = 0; j < NOUVEAU_DMA_SKIPS; j++)
+				nouveau_bo_wr32(chan->pushbuf_bo, i, 0);
+		}
+	}
+
+	NV_INFO(dev, "Restoring mode...\n");
+	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+		struct nouveau_framebuffer *nouveau_fb;
+
+		nouveau_fb = nouveau_framebuffer(crtc->fb);
+		if (!nouveau_fb || !nouveau_fb->nvbo)
+			continue;
+
+		nouveau_bo_pin(nouveau_fb->nvbo, TTM_PL_FLAG_VRAM);
+	}
+
+	if (dev_priv->card_type < NV_50) {
+		nv04_display_restore(dev);
+		NVLockVgaCrtcs(dev, false);
+	} else
+		nv50_display_init(dev);
+
+	/* Force CLUT to get re-loaded during modeset */
+	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+		struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+
+		nv_crtc->lut.depth = 0;
+	}
+
+	acquire_console_sem();
+	fb_set_suspend(dev_priv->fbdev_info, 0);
+	release_console_sem();
+
+	nouveau_fbcon_zfill(dev);
+
+	drm_helper_resume_force_mode(dev);
+	dev_priv->fbdev_info->flags = fbdev_flags;
+	return 0;
+}
+
+static struct drm_driver driver = {
+	.driver_features =
+		DRIVER_USE_AGP | DRIVER_PCI_DMA | DRIVER_SG |
+		DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_GEM,
+	.load = nouveau_load,
+	.firstopen = nouveau_firstopen,
+	.lastclose = nouveau_lastclose,
+	.unload = nouveau_unload,
+	.preclose = nouveau_preclose,
+#if defined(CONFIG_DRM_NOUVEAU_DEBUG)
+	.debugfs_init = nouveau_debugfs_init,
+	.debugfs_cleanup = nouveau_debugfs_takedown,
+#endif
+	.irq_preinstall = nouveau_irq_preinstall,
+	.irq_postinstall = nouveau_irq_postinstall,
+	.irq_uninstall = nouveau_irq_uninstall,
+	.irq_handler = nouveau_irq_handler,
+	.reclaim_buffers = drm_core_reclaim_buffers,
+	.get_map_ofs = drm_core_get_map_ofs,
+	.get_reg_ofs = drm_core_get_reg_ofs,
+	.ioctls = nouveau_ioctls,
+	.fops = {
+		.owner = THIS_MODULE,
+		.open = drm_open,
+		.release = drm_release,
+		.ioctl = drm_ioctl,
+		.mmap = nouveau_ttm_mmap,
+		.poll = drm_poll,
+		.fasync = drm_fasync,
+#if defined(CONFIG_COMPAT)
+		.compat_ioctl = nouveau_compat_ioctl,
+#endif
+	},
+	.pci_driver = {
+		.name = DRIVER_NAME,
+		.id_table = pciidlist,
+		.probe = nouveau_pci_probe,
+		.remove = nouveau_pci_remove,
+		.suspend = nouveau_pci_suspend,
+		.resume = nouveau_pci_resume
+	},
+
+	.gem_init_object = nouveau_gem_object_new,
+	.gem_free_object = nouveau_gem_object_del,
+
+	.name = DRIVER_NAME,
+	.desc = DRIVER_DESC,
+#ifdef GIT_REVISION
+	.date = GIT_REVISION,
+#else
+	.date = DRIVER_DATE,
+#endif
+	.major = DRIVER_MAJOR,
+	.minor = DRIVER_MINOR,
+	.patchlevel = DRIVER_PATCHLEVEL,
+};
+
+static int __init nouveau_init(void)
+{
+	driver.num_ioctls = nouveau_max_ioctl;
+
+	if (nouveau_modeset == -1) {
+#ifdef CONFIG_VGA_CONSOLE
+		if (vgacon_text_force())
+			nouveau_modeset = 0;
+		else
+#endif
+			nouveau_modeset = 1;
+	}
+
+	if (nouveau_modeset == 1)
+		driver.driver_features |= DRIVER_MODESET;
+
+	return drm_init(&driver);
+}
+
+static void __exit nouveau_exit(void)
+{
+	drm_exit(&driver);
+}
+
+module_init(nouveau_init);
+module_exit(nouveau_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
new file mode 100644
index 0000000..88b4c7b
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -0,0 +1,1286 @@
+/*
+ * Copyright 2005 Stephane Marchesin.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __NOUVEAU_DRV_H__
+#define __NOUVEAU_DRV_H__
+
+#define DRIVER_AUTHOR		"Stephane Marchesin"
+#define DRIVER_EMAIL		"dri-devel@lists.sourceforge.net"
+
+#define DRIVER_NAME		"nouveau"
+#define DRIVER_DESC		"nVidia Riva/TNT/GeForce"
+#define DRIVER_DATE		"20090420"
+
+#define DRIVER_MAJOR		0
+#define DRIVER_MINOR		0
+#define DRIVER_PATCHLEVEL	15
+
+#define NOUVEAU_FAMILY   0x0000FFFF
+#define NOUVEAU_FLAGS    0xFFFF0000
+
+#include "ttm/ttm_bo_api.h"
+#include "ttm/ttm_bo_driver.h"
+#include "ttm/ttm_placement.h"
+#include "ttm/ttm_memory.h"
+#include "ttm/ttm_module.h"
+
+struct nouveau_fpriv {
+	struct ttm_object_file *tfile;
+};
+
+#define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT)
+
+#include "nouveau_drm.h"
+#include "nouveau_reg.h"
+#include "nouveau_bios.h"
+
+#define MAX_NUM_DCB_ENTRIES 16
+
+#define NOUVEAU_MAX_CHANNEL_NR 128
+
+#define NV50_VM_MAX_VRAM (2*1024*1024*1024ULL)
+#define NV50_VM_BLOCK    (512*1024*1024ULL)
+#define NV50_VM_VRAM_NR  (NV50_VM_MAX_VRAM / NV50_VM_BLOCK)
+
+struct nouveau_bo {
+	struct ttm_buffer_object bo;
+	struct ttm_placement placement;
+	u32 placements[3];
+	struct ttm_bo_kmap_obj kmap;
+	struct list_head head;
+
+	/* protected by ttm_bo_reserve() */
+	struct drm_file *reserved_by;
+	struct list_head entry;
+	int pbbo_index;
+
+	struct nouveau_channel *channel;
+
+	bool mappable;
+	bool no_vm;
+
+	uint32_t tile_mode;
+	uint32_t tile_flags;
+
+	struct drm_gem_object *gem;
+	struct drm_file *cpu_filp;
+	int pin_refcnt;
+};
+
+static inline struct nouveau_bo *
+nouveau_bo(struct ttm_buffer_object *bo)
+{
+	return container_of(bo, struct nouveau_bo, bo);
+}
+
+static inline struct nouveau_bo *
+nouveau_gem_object(struct drm_gem_object *gem)
+{
+	return gem ? gem->driver_private : NULL;
+}
+
+/* TODO: submit equivalent to TTM generic API upstream? */
+static inline void __iomem *
+nvbo_kmap_obj_iovirtual(struct nouveau_bo *nvbo)
+{
+	bool is_iomem;
+	void __iomem *ioptr = (void __force __iomem *)ttm_kmap_obj_virtual(
+						&nvbo->kmap, &is_iomem);
+	WARN_ON_ONCE(ioptr && !is_iomem);
+	return ioptr;
+}
+
+struct mem_block {
+	struct mem_block *next;
+	struct mem_block *prev;
+	uint64_t start;
+	uint64_t size;
+	struct drm_file *file_priv; /* NULL: free, -1: heap, other: real files */
+};
+
+enum nouveau_flags {
+	NV_NFORCE   = 0x10000000,
+	NV_NFORCE2  = 0x20000000
+};
+
+#define NVOBJ_ENGINE_SW		0
+#define NVOBJ_ENGINE_GR		1
+#define NVOBJ_ENGINE_DISPLAY	2
+#define NVOBJ_ENGINE_INT	0xdeadbeef
+
+#define NVOBJ_FLAG_ALLOW_NO_REFS	(1 << 0)
+#define NVOBJ_FLAG_ZERO_ALLOC		(1 << 1)
+#define NVOBJ_FLAG_ZERO_FREE		(1 << 2)
+#define NVOBJ_FLAG_FAKE			(1 << 3)
+struct nouveau_gpuobj {
+	struct list_head list;
+
+	struct nouveau_channel *im_channel;
+	struct mem_block *im_pramin;
+	struct nouveau_bo *im_backing;
+	uint32_t im_backing_start;
+	uint32_t *im_backing_suspend;
+	int im_bound;
+
+	uint32_t flags;
+	int refcount;
+
+	uint32_t engine;
+	uint32_t class;
+
+	void (*dtor)(struct drm_device *, struct nouveau_gpuobj *);
+	void *priv;
+};
+
+struct nouveau_gpuobj_ref {
+	struct list_head list;
+
+	struct nouveau_gpuobj *gpuobj;
+	uint32_t instance;
+
+	struct nouveau_channel *channel;
+	int handle;
+};
+
+struct nouveau_channel {
+	struct drm_device *dev;
+	int id;
+
+	/* owner of this fifo */
+	struct drm_file *file_priv;
+	/* mapping of the fifo itself */
+	struct drm_local_map *map;
+
+	/* mapping of the regs controling the fifo */
+	void __iomem *user;
+	uint32_t user_get;
+	uint32_t user_put;
+
+	/* Fencing */
+	struct {
+		/* lock protects the pending list only */
+		spinlock_t lock;
+		struct list_head pending;
+		uint32_t sequence;
+		uint32_t sequence_ack;
+		uint32_t last_sequence_irq;
+	} fence;
+
+	/* DMA push buffer */
+	struct nouveau_gpuobj_ref *pushbuf;
+	struct nouveau_bo         *pushbuf_bo;
+	uint32_t                   pushbuf_base;
+
+	/* Notifier memory */
+	struct nouveau_bo *notifier_bo;
+	struct mem_block *notifier_heap;
+
+	/* PFIFO context */
+	struct nouveau_gpuobj_ref *ramfc;
+	struct nouveau_gpuobj_ref *cache;
+
+	/* PGRAPH context */
+	/* XXX may be merge 2 pointers as private data ??? */
+	struct nouveau_gpuobj_ref *ramin_grctx;
+	void *pgraph_ctx;
+
+	/* NV50 VM */
+	struct nouveau_gpuobj     *vm_pd;
+	struct nouveau_gpuobj_ref *vm_gart_pt;
+	struct nouveau_gpuobj_ref *vm_vram_pt[NV50_VM_VRAM_NR];
+
+	/* Objects */
+	struct nouveau_gpuobj_ref *ramin; /* Private instmem */
+	struct mem_block          *ramin_heap; /* Private PRAMIN heap */
+	struct nouveau_gpuobj_ref *ramht; /* Hash table */
+	struct list_head           ramht_refs; /* Objects referenced by RAMHT */
+
+	/* GPU object info for stuff used in-kernel (mm_enabled) */
+	uint32_t m2mf_ntfy;
+	uint32_t vram_handle;
+	uint32_t gart_handle;
+	bool accel_done;
+
+	/* Push buffer state (only for drm's channel on !mm_enabled) */
+	struct {
+		int max;
+		int free;
+		int cur;
+		int put;
+		/* access via pushbuf_bo */
+	} dma;
+
+	uint32_t sw_subchannel[8];
+
+	struct {
+		struct nouveau_gpuobj *vblsem;
+		uint32_t vblsem_offset;
+		uint32_t vblsem_rval;
+		struct list_head vbl_wait;
+	} nvsw;
+
+	struct {
+		bool active;
+		char name[32];
+		struct drm_info_list info;
+	} debugfs;
+};
+
+struct nouveau_instmem_engine {
+	void	*priv;
+
+	int	(*init)(struct drm_device *dev);
+	void	(*takedown)(struct drm_device *dev);
+	int	(*suspend)(struct drm_device *dev);
+	void	(*resume)(struct drm_device *dev);
+
+	int	(*populate)(struct drm_device *, struct nouveau_gpuobj *,
+			    uint32_t *size);
+	void	(*clear)(struct drm_device *, struct nouveau_gpuobj *);
+	int	(*bind)(struct drm_device *, struct nouveau_gpuobj *);
+	int	(*unbind)(struct drm_device *, struct nouveau_gpuobj *);
+	void	(*prepare_access)(struct drm_device *, bool write);
+	void	(*finish_access)(struct drm_device *);
+};
+
+struct nouveau_mc_engine {
+	int  (*init)(struct drm_device *dev);
+	void (*takedown)(struct drm_device *dev);
+};
+
+struct nouveau_timer_engine {
+	int      (*init)(struct drm_device *dev);
+	void     (*takedown)(struct drm_device *dev);
+	uint64_t (*read)(struct drm_device *dev);
+};
+
+struct nouveau_fb_engine {
+	int  (*init)(struct drm_device *dev);
+	void (*takedown)(struct drm_device *dev);
+};
+
+struct nouveau_fifo_engine {
+	void *priv;
+
+	int  channels;
+
+	int  (*init)(struct drm_device *);
+	void (*takedown)(struct drm_device *);
+
+	void (*disable)(struct drm_device *);
+	void (*enable)(struct drm_device *);
+	bool (*reassign)(struct drm_device *, bool enable);
+
+	int  (*channel_id)(struct drm_device *);
+
+	int  (*create_context)(struct nouveau_channel *);
+	void (*destroy_context)(struct nouveau_channel *);
+	int  (*load_context)(struct nouveau_channel *);
+	int  (*unload_context)(struct drm_device *);
+};
+
+struct nouveau_pgraph_object_method {
+	int id;
+	int (*exec)(struct nouveau_channel *chan, int grclass, int mthd,
+		      uint32_t data);
+};
+
+struct nouveau_pgraph_object_class {
+	int id;
+	bool software;
+	struct nouveau_pgraph_object_method *methods;
+};
+
+struct nouveau_pgraph_engine {
+	struct nouveau_pgraph_object_class *grclass;
+	bool accel_blocked;
+	void *ctxprog;
+	void *ctxvals;
+
+	int  (*init)(struct drm_device *);
+	void (*takedown)(struct drm_device *);
+
+	void (*fifo_access)(struct drm_device *, bool);
+
+	struct nouveau_channel *(*channel)(struct drm_device *);
+	int  (*create_context)(struct nouveau_channel *);
+	void (*destroy_context)(struct nouveau_channel *);
+	int  (*load_context)(struct nouveau_channel *);
+	int  (*unload_context)(struct drm_device *);
+};
+
+struct nouveau_engine {
+	struct nouveau_instmem_engine instmem;
+	struct nouveau_mc_engine      mc;
+	struct nouveau_timer_engine   timer;
+	struct nouveau_fb_engine      fb;
+	struct nouveau_pgraph_engine  graph;
+	struct nouveau_fifo_engine    fifo;
+};
+
+struct nouveau_pll_vals {
+	union {
+		struct {
+#ifdef __BIG_ENDIAN
+			uint8_t N1, M1, N2, M2;
+#else
+			uint8_t M1, N1, M2, N2;
+#endif
+		};
+		struct {
+			uint16_t NM1, NM2;
+		} __attribute__((packed));
+	};
+	int log2P;
+
+	int refclk;
+};
+
+enum nv04_fp_display_regs {
+	FP_DISPLAY_END,
+	FP_TOTAL,
+	FP_CRTC,
+	FP_SYNC_START,
+	FP_SYNC_END,
+	FP_VALID_START,
+	FP_VALID_END
+};
+
+struct nv04_crtc_reg {
+	unsigned char MiscOutReg;     /* */
+	uint8_t CRTC[0x9f];
+	uint8_t CR58[0x10];
+	uint8_t Sequencer[5];
+	uint8_t Graphics[9];
+	uint8_t Attribute[21];
+	unsigned char DAC[768];       /* Internal Colorlookuptable */
+
+	/* PCRTC regs */
+	uint32_t fb_start;
+	uint32_t crtc_cfg;
+	uint32_t cursor_cfg;
+	uint32_t gpio_ext;
+	uint32_t crtc_830;
+	uint32_t crtc_834;
+	uint32_t crtc_850;
+	uint32_t crtc_eng_ctrl;
+
+	/* PRAMDAC regs */
+	uint32_t nv10_cursync;
+	struct nouveau_pll_vals pllvals;
+	uint32_t ramdac_gen_ctrl;
+	uint32_t ramdac_630;
+	uint32_t ramdac_634;
+	uint32_t tv_setup;
+	uint32_t tv_vtotal;
+	uint32_t tv_vskew;
+	uint32_t tv_vsync_delay;
+	uint32_t tv_htotal;
+	uint32_t tv_hskew;
+	uint32_t tv_hsync_delay;
+	uint32_t tv_hsync_delay2;
+	uint32_t fp_horiz_regs[7];
+	uint32_t fp_vert_regs[7];
+	uint32_t dither;
+	uint32_t fp_control;
+	uint32_t dither_regs[6];
+	uint32_t fp_debug_0;
+	uint32_t fp_debug_1;
+	uint32_t fp_debug_2;
+	uint32_t fp_margin_color;
+	uint32_t ramdac_8c0;
+	uint32_t ramdac_a20;
+	uint32_t ramdac_a24;
+	uint32_t ramdac_a34;
+	uint32_t ctv_regs[38];
+};
+
+struct nv04_output_reg {
+	uint32_t output;
+	int head;
+};
+
+struct nv04_mode_state {
+	uint32_t bpp;
+	uint32_t width;
+	uint32_t height;
+	uint32_t interlace;
+	uint32_t repaint0;
+	uint32_t repaint1;
+	uint32_t screen;
+	uint32_t scale;
+	uint32_t dither;
+	uint32_t extra;
+	uint32_t fifo;
+	uint32_t pixel;
+	uint32_t horiz;
+	int arbitration0;
+	int arbitration1;
+	uint32_t pll;
+	uint32_t pllB;
+	uint32_t vpll;
+	uint32_t vpll2;
+	uint32_t vpllB;
+	uint32_t vpll2B;
+	uint32_t pllsel;
+	uint32_t sel_clk;
+	uint32_t general;
+	uint32_t crtcOwner;
+	uint32_t head;
+	uint32_t head2;
+	uint32_t cursorConfig;
+	uint32_t cursor0;
+	uint32_t cursor1;
+	uint32_t cursor2;
+	uint32_t timingH;
+	uint32_t timingV;
+	uint32_t displayV;
+	uint32_t crtcSync;
+
+	struct nv04_crtc_reg crtc_reg[2];
+};
+
+enum nouveau_card_type {
+	NV_04      = 0x00,
+	NV_10      = 0x10,
+	NV_20      = 0x20,
+	NV_30      = 0x30,
+	NV_40      = 0x40,
+	NV_50      = 0x50,
+};
+
+struct drm_nouveau_private {
+	struct drm_device *dev;
+	enum {
+		NOUVEAU_CARD_INIT_DOWN,
+		NOUVEAU_CARD_INIT_DONE,
+		NOUVEAU_CARD_INIT_FAILED
+	} init_state;
+
+	/* the card type, takes NV_* as values */
+	enum nouveau_card_type card_type;
+	/* exact chipset, derived from NV_PMC_BOOT_0 */
+	int chipset;
+	int flags;
+
+	void __iomem *mmio;
+	void __iomem *ramin;
+	uint32_t ramin_size;
+
+	struct workqueue_struct *wq;
+	struct work_struct irq_work;
+
+	struct list_head vbl_waiting;
+
+	struct {
+		struct ttm_global_reference mem_global_ref;
+		struct ttm_bo_global_ref bo_global_ref;
+		struct ttm_bo_device bdev;
+		spinlock_t bo_list_lock;
+		struct list_head bo_list;
+		atomic_t validate_sequence;
+	} ttm;
+
+	struct fb_info *fbdev_info;
+
+	int fifo_alloc_count;
+	struct nouveau_channel *fifos[NOUVEAU_MAX_CHANNEL_NR];
+
+	struct nouveau_engine engine;
+	struct nouveau_channel *channel;
+
+	/* RAMIN configuration, RAMFC, RAMHT and RAMRO offsets */
+	struct nouveau_gpuobj *ramht;
+	uint32_t ramin_rsvd_vram;
+	uint32_t ramht_offset;
+	uint32_t ramht_size;
+	uint32_t ramht_bits;
+	uint32_t ramfc_offset;
+	uint32_t ramfc_size;
+	uint32_t ramro_offset;
+	uint32_t ramro_size;
+
+	/* base physical adresses */
+	uint64_t fb_phys;
+	uint64_t fb_available_size;
+	uint64_t fb_mappable_pages;
+	uint64_t fb_aper_free;
+
+	struct {
+		enum {
+			NOUVEAU_GART_NONE = 0,
+			NOUVEAU_GART_AGP,
+			NOUVEAU_GART_SGDMA
+		} type;
+		uint64_t aper_base;
+		uint64_t aper_size;
+		uint64_t aper_free;
+
+		struct nouveau_gpuobj *sg_ctxdma;
+		struct page *sg_dummy_page;
+		dma_addr_t sg_dummy_bus;
+
+		/* nottm hack */
+		struct drm_ttm_backend *sg_be;
+		unsigned long sg_handle;
+	} gart_info;
+
+	/* G8x/G9x virtual address space */
+	uint64_t vm_gart_base;
+	uint64_t vm_gart_size;
+	uint64_t vm_vram_base;
+	uint64_t vm_vram_size;
+	uint64_t vm_end;
+	struct nouveau_gpuobj *vm_vram_pt[NV50_VM_VRAM_NR];
+	int vm_vram_pt_nr;
+
+	/* the mtrr covering the FB */
+	int fb_mtrr;
+
+	struct mem_block *ramin_heap;
+
+	/* context table pointed to be NV_PGRAPH_CHANNEL_CTX_TABLE (0x400780) */
+	uint32_t ctx_table_size;
+	struct nouveau_gpuobj_ref *ctx_table;
+
+	struct list_head gpuobj_list;
+
+	struct nvbios VBIOS;
+	struct nouveau_bios_info *vbios;
+
+	struct nv04_mode_state mode_reg;
+	struct nv04_mode_state saved_reg;
+	uint32_t saved_vga_font[4][16384];
+	uint32_t crtc_owner;
+	uint32_t dac_users[4];
+
+	struct nouveau_suspend_resume {
+		uint32_t fifo_mode;
+		uint32_t graph_ctx_control;
+		uint32_t graph_state;
+		uint32_t *ramin_copy;
+		uint64_t ramin_size;
+	} susres;
+
+	struct backlight_device *backlight;
+	bool acpi_dsm;
+
+	struct nouveau_channel *evo;
+
+	struct {
+		struct dentry *channel_root;
+	} debugfs;
+};
+
+static inline struct drm_nouveau_private *
+nouveau_bdev(struct ttm_bo_device *bd)
+{
+	return container_of(bd, struct drm_nouveau_private, ttm.bdev);
+}
+
+static inline int
+nouveau_bo_ref(struct nouveau_bo *ref, struct nouveau_bo **pnvbo)
+{
+	struct nouveau_bo *prev;
+
+	if (!pnvbo)
+		return -EINVAL;
+	prev = *pnvbo;
+
+	*pnvbo = ref ? nouveau_bo(ttm_bo_reference(&ref->bo)) : NULL;
+	if (prev) {
+		struct ttm_buffer_object *bo = &prev->bo;
+
+		ttm_bo_unref(&bo);
+	}
+
+	return 0;
+}
+
+#define NOUVEAU_CHECK_INITIALISED_WITH_RETURN do {            \
+	struct drm_nouveau_private *nv = dev->dev_private;    \
+	if (nv->init_state != NOUVEAU_CARD_INIT_DONE) {       \
+		NV_ERROR(dev, "called without init\n");       \
+		return -EINVAL;                               \
+	}                                                     \
+} while (0)
+
+#define NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(id, cl, ch) do {    \
+	struct drm_nouveau_private *nv = dev->dev_private;       \
+	if (!nouveau_channel_owner(dev, (cl), (id))) {           \
+		NV_ERROR(dev, "pid %d doesn't own channel %d\n", \
+			 DRM_CURRENTPID, (id));                  \
+		return -EPERM;                                   \
+	}                                                        \
+	(ch) = nv->fifos[(id)];                                  \
+} while (0)
+
+/* nouveau_drv.c */
+extern int nouveau_noagp;
+extern int nouveau_duallink;
+extern int nouveau_uscript_lvds;
+extern int nouveau_uscript_tmds;
+extern int nouveau_vram_pushbuf;
+extern int nouveau_vram_notify;
+extern int nouveau_fbpercrtc;
+extern char *nouveau_tv_norm;
+extern int nouveau_reg_debug;
+extern char *nouveau_vbios;
+
+/* nouveau_state.c */
+extern void nouveau_preclose(struct drm_device *dev, struct drm_file *);
+extern int  nouveau_load(struct drm_device *, unsigned long flags);
+extern int  nouveau_firstopen(struct drm_device *);
+extern void nouveau_lastclose(struct drm_device *);
+extern int  nouveau_unload(struct drm_device *);
+extern int  nouveau_ioctl_getparam(struct drm_device *, void *data,
+				   struct drm_file *);
+extern int  nouveau_ioctl_setparam(struct drm_device *, void *data,
+				   struct drm_file *);
+extern bool nouveau_wait_until(struct drm_device *, uint64_t timeout,
+			       uint32_t reg, uint32_t mask, uint32_t val);
+extern bool nouveau_wait_for_idle(struct drm_device *);
+extern int  nouveau_card_init(struct drm_device *);
+extern int  nouveau_ioctl_card_init(struct drm_device *, void *data,
+				    struct drm_file *);
+extern int  nouveau_ioctl_suspend(struct drm_device *, void *data,
+				  struct drm_file *);
+extern int  nouveau_ioctl_resume(struct drm_device *, void *data,
+				 struct drm_file *);
+
+/* nouveau_mem.c */
+extern int  nouveau_mem_init_heap(struct mem_block **, uint64_t start,
+				 uint64_t size);
+extern struct mem_block *nouveau_mem_alloc_block(struct mem_block *,
+						 uint64_t size, int align2,
+						 struct drm_file *, int tail);
+extern void nouveau_mem_takedown(struct mem_block **heap);
+extern void nouveau_mem_free_block(struct mem_block *);
+extern uint64_t nouveau_mem_fb_amount(struct drm_device *);
+extern void nouveau_mem_release(struct drm_file *, struct mem_block *heap);
+extern int  nouveau_mem_init(struct drm_device *);
+extern int  nouveau_mem_init_agp(struct drm_device *);
+extern void nouveau_mem_close(struct drm_device *);
+extern int  nv50_mem_vm_bind_linear(struct drm_device *, uint64_t virt,
+				    uint32_t size, uint32_t flags,
+				    uint64_t phys);
+extern void nv50_mem_vm_unbind(struct drm_device *, uint64_t virt,
+			       uint32_t size);
+
+/* nouveau_notifier.c */
+extern int  nouveau_notifier_init_channel(struct nouveau_channel *);
+extern void nouveau_notifier_takedown_channel(struct nouveau_channel *);
+extern int  nouveau_notifier_alloc(struct nouveau_channel *, uint32_t handle,
+				   int cout, uint32_t *offset);
+extern int  nouveau_notifier_offset(struct nouveau_gpuobj *, uint32_t *);
+extern int  nouveau_ioctl_notifier_alloc(struct drm_device *, void *data,
+					 struct drm_file *);
+extern int  nouveau_ioctl_notifier_free(struct drm_device *, void *data,
+					struct drm_file *);
+
+/* nouveau_channel.c */
+extern struct drm_ioctl_desc nouveau_ioctls[];
+extern int nouveau_max_ioctl;
+extern void nouveau_channel_cleanup(struct drm_device *, struct drm_file *);
+extern int  nouveau_channel_owner(struct drm_device *, struct drm_file *,
+				  int channel);
+extern int  nouveau_channel_alloc(struct drm_device *dev,
+				  struct nouveau_channel **chan,
+				  struct drm_file *file_priv,
+				  uint32_t fb_ctxdma, uint32_t tt_ctxdma);
+extern void nouveau_channel_free(struct nouveau_channel *);
+extern int  nouveau_channel_idle(struct nouveau_channel *chan);
+
+/* nouveau_object.c */
+extern int  nouveau_gpuobj_early_init(struct drm_device *);
+extern int  nouveau_gpuobj_init(struct drm_device *);
+extern void nouveau_gpuobj_takedown(struct drm_device *);
+extern void nouveau_gpuobj_late_takedown(struct drm_device *);
+extern int  nouveau_gpuobj_suspend(struct drm_device *dev);
+extern void nouveau_gpuobj_suspend_cleanup(struct drm_device *dev);
+extern void nouveau_gpuobj_resume(struct drm_device *dev);
+extern int nouveau_gpuobj_channel_init(struct nouveau_channel *,
+				       uint32_t vram_h, uint32_t tt_h);
+extern void nouveau_gpuobj_channel_takedown(struct nouveau_channel *);
+extern int nouveau_gpuobj_new(struct drm_device *, struct nouveau_channel *,
+			      uint32_t size, int align, uint32_t flags,
+			      struct nouveau_gpuobj **);
+extern int nouveau_gpuobj_del(struct drm_device *, struct nouveau_gpuobj **);
+extern int nouveau_gpuobj_ref_add(struct drm_device *, struct nouveau_channel *,
+				  uint32_t handle, struct nouveau_gpuobj *,
+				  struct nouveau_gpuobj_ref **);
+extern int nouveau_gpuobj_ref_del(struct drm_device *,
+				  struct nouveau_gpuobj_ref **);
+extern int nouveau_gpuobj_ref_find(struct nouveau_channel *, uint32_t handle,
+				   struct nouveau_gpuobj_ref **ref_ret);
+extern int nouveau_gpuobj_new_ref(struct drm_device *,
+				  struct nouveau_channel *alloc_chan,
+				  struct nouveau_channel *ref_chan,
+				  uint32_t handle, uint32_t size, int align,
+				  uint32_t flags, struct nouveau_gpuobj_ref **);
+extern int nouveau_gpuobj_new_fake(struct drm_device *,
+				   uint32_t p_offset, uint32_t b_offset,
+				   uint32_t size, uint32_t flags,
+				   struct nouveau_gpuobj **,
+				   struct nouveau_gpuobj_ref**);
+extern int nouveau_gpuobj_dma_new(struct nouveau_channel *, int class,
+				  uint64_t offset, uint64_t size, int access,
+				  int target, struct nouveau_gpuobj **);
+extern int nouveau_gpuobj_gart_dma_new(struct nouveau_channel *,
+				       uint64_t offset, uint64_t size,
+				       int access, struct nouveau_gpuobj **,
+				       uint32_t *o_ret);
+extern int nouveau_gpuobj_gr_new(struct nouveau_channel *, int class,
+				 struct nouveau_gpuobj **);
+extern int nouveau_ioctl_grobj_alloc(struct drm_device *, void *data,
+				     struct drm_file *);
+extern int nouveau_ioctl_gpuobj_free(struct drm_device *, void *data,
+				     struct drm_file *);
+
+/* nouveau_irq.c */
+extern irqreturn_t nouveau_irq_handler(DRM_IRQ_ARGS);
+extern void        nouveau_irq_preinstall(struct drm_device *);
+extern int         nouveau_irq_postinstall(struct drm_device *);
+extern void        nouveau_irq_uninstall(struct drm_device *);
+
+/* nouveau_sgdma.c */
+extern int nouveau_sgdma_init(struct drm_device *);
+extern void nouveau_sgdma_takedown(struct drm_device *);
+extern int nouveau_sgdma_get_page(struct drm_device *, uint32_t offset,
+				  uint32_t *page);
+extern struct ttm_backend *nouveau_sgdma_init_ttm(struct drm_device *);
+
+/* nouveau_debugfs.c */
+#if defined(CONFIG_DRM_NOUVEAU_DEBUG)
+extern int  nouveau_debugfs_init(struct drm_minor *);
+extern void nouveau_debugfs_takedown(struct drm_minor *);
+extern int  nouveau_debugfs_channel_init(struct nouveau_channel *);
+extern void nouveau_debugfs_channel_fini(struct nouveau_channel *);
+#else
+static inline int
+nouveau_debugfs_init(struct drm_minor *minor)
+{
+	return 0;
+}
+
+static inline void nouveau_debugfs_takedown(struct drm_minor *minor)
+{
+}
+
+static inline int
+nouveau_debugfs_channel_init(struct nouveau_channel *chan)
+{
+	return 0;
+}
+
+static inline void
+nouveau_debugfs_channel_fini(struct nouveau_channel *chan)
+{
+}
+#endif
+
+/* nouveau_dma.c */
+extern int  nouveau_dma_init(struct nouveau_channel *);
+extern int  nouveau_dma_wait(struct nouveau_channel *, int size);
+
+/* nouveau_acpi.c */
+#ifdef CONFIG_ACPI
+extern int nouveau_hybrid_setup(struct drm_device *dev);
+extern bool nouveau_dsm_probe(struct drm_device *dev);
+#else
+static inline int nouveau_hybrid_setup(struct drm_device *dev)
+{
+	return 0;
+}
+static inline bool nouveau_dsm_probe(struct drm_device *dev)
+{
+	return false;
+}
+#endif
+
+/* nouveau_backlight.c */
+#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT
+extern int nouveau_backlight_init(struct drm_device *);
+extern void nouveau_backlight_exit(struct drm_device *);
+#else
+static inline int nouveau_backlight_init(struct drm_device *dev)
+{
+	return 0;
+}
+
+static inline void nouveau_backlight_exit(struct drm_device *dev) { }
+#endif
+
+/* nouveau_bios.c */
+extern int nouveau_bios_init(struct drm_device *);
+extern void nouveau_bios_takedown(struct drm_device *dev);
+extern int nouveau_run_vbios_init(struct drm_device *);
+extern void nouveau_bios_run_init_table(struct drm_device *, uint16_t table,
+					struct dcb_entry *);
+extern struct dcb_gpio_entry *nouveau_bios_gpio_entry(struct drm_device *,
+						      enum dcb_gpio_tag);
+extern struct dcb_connector_table_entry *
+nouveau_bios_connector_entry(struct drm_device *, int index);
+extern int get_pll_limits(struct drm_device *, uint32_t limit_match,
+			  struct pll_lims *);
+extern int nouveau_bios_run_display_table(struct drm_device *,
+					  struct dcb_entry *,
+					  uint32_t script, int pxclk);
+extern void *nouveau_bios_dp_table(struct drm_device *, struct dcb_entry *,
+				   int *length);
+extern bool nouveau_bios_fp_mode(struct drm_device *, struct drm_display_mode *);
+extern uint8_t *nouveau_bios_embedded_edid(struct drm_device *);
+extern int nouveau_bios_parse_lvds_table(struct drm_device *, int pxclk,
+					 bool *dl, bool *if_is_24bit);
+extern int run_tmds_table(struct drm_device *, struct dcb_entry *,
+			  int head, int pxclk);
+extern int call_lvds_script(struct drm_device *, struct dcb_entry *, int head,
+			    enum LVDS_script, int pxclk);
+
+/* nouveau_ttm.c */
+int nouveau_ttm_global_init(struct drm_nouveau_private *);
+void nouveau_ttm_global_release(struct drm_nouveau_private *);
+int nouveau_ttm_mmap(struct file *, struct vm_area_struct *);
+
+/* nouveau_dp.c */
+int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr,
+		     uint8_t *data, int data_nr);
+bool nouveau_dp_detect(struct drm_encoder *);
+bool nouveau_dp_link_train(struct drm_encoder *);
+
+/* nv04_fb.c */
+extern int  nv04_fb_init(struct drm_device *);
+extern void nv04_fb_takedown(struct drm_device *);
+
+/* nv10_fb.c */
+extern int  nv10_fb_init(struct drm_device *);
+extern void nv10_fb_takedown(struct drm_device *);
+
+/* nv40_fb.c */
+extern int  nv40_fb_init(struct drm_device *);
+extern void nv40_fb_takedown(struct drm_device *);
+
+/* nv04_fifo.c */
+extern int  nv04_fifo_init(struct drm_device *);
+extern void nv04_fifo_disable(struct drm_device *);
+extern void nv04_fifo_enable(struct drm_device *);
+extern bool nv04_fifo_reassign(struct drm_device *, bool);
+extern int  nv04_fifo_channel_id(struct drm_device *);
+extern int  nv04_fifo_create_context(struct nouveau_channel *);
+extern void nv04_fifo_destroy_context(struct nouveau_channel *);
+extern int  nv04_fifo_load_context(struct nouveau_channel *);
+extern int  nv04_fifo_unload_context(struct drm_device *);
+
+/* nv10_fifo.c */
+extern int  nv10_fifo_init(struct drm_device *);
+extern int  nv10_fifo_channel_id(struct drm_device *);
+extern int  nv10_fifo_create_context(struct nouveau_channel *);
+extern void nv10_fifo_destroy_context(struct nouveau_channel *);
+extern int  nv10_fifo_load_context(struct nouveau_channel *);
+extern int  nv10_fifo_unload_context(struct drm_device *);
+
+/* nv40_fifo.c */
+extern int  nv40_fifo_init(struct drm_device *);
+extern int  nv40_fifo_create_context(struct nouveau_channel *);
+extern void nv40_fifo_destroy_context(struct nouveau_channel *);
+extern int  nv40_fifo_load_context(struct nouveau_channel *);
+extern int  nv40_fifo_unload_context(struct drm_device *);
+
+/* nv50_fifo.c */
+extern int  nv50_fifo_init(struct drm_device *);
+extern void nv50_fifo_takedown(struct drm_device *);
+extern int  nv50_fifo_channel_id(struct drm_device *);
+extern int  nv50_fifo_create_context(struct nouveau_channel *);
+extern void nv50_fifo_destroy_context(struct nouveau_channel *);
+extern int  nv50_fifo_load_context(struct nouveau_channel *);
+extern int  nv50_fifo_unload_context(struct drm_device *);
+
+/* nv04_graph.c */
+extern struct nouveau_pgraph_object_class nv04_graph_grclass[];
+extern int  nv04_graph_init(struct drm_device *);
+extern void nv04_graph_takedown(struct drm_device *);
+extern void nv04_graph_fifo_access(struct drm_device *, bool);
+extern struct nouveau_channel *nv04_graph_channel(struct drm_device *);
+extern int  nv04_graph_create_context(struct nouveau_channel *);
+extern void nv04_graph_destroy_context(struct nouveau_channel *);
+extern int  nv04_graph_load_context(struct nouveau_channel *);
+extern int  nv04_graph_unload_context(struct drm_device *);
+extern void nv04_graph_context_switch(struct drm_device *);
+
+/* nv10_graph.c */
+extern struct nouveau_pgraph_object_class nv10_graph_grclass[];
+extern int  nv10_graph_init(struct drm_device *);
+extern void nv10_graph_takedown(struct drm_device *);
+extern struct nouveau_channel *nv10_graph_channel(struct drm_device *);
+extern int  nv10_graph_create_context(struct nouveau_channel *);
+extern void nv10_graph_destroy_context(struct nouveau_channel *);
+extern int  nv10_graph_load_context(struct nouveau_channel *);
+extern int  nv10_graph_unload_context(struct drm_device *);
+extern void nv10_graph_context_switch(struct drm_device *);
+
+/* nv20_graph.c */
+extern struct nouveau_pgraph_object_class nv20_graph_grclass[];
+extern struct nouveau_pgraph_object_class nv30_graph_grclass[];
+extern int  nv20_graph_create_context(struct nouveau_channel *);
+extern void nv20_graph_destroy_context(struct nouveau_channel *);
+extern int  nv20_graph_load_context(struct nouveau_channel *);
+extern int  nv20_graph_unload_context(struct drm_device *);
+extern int  nv20_graph_init(struct drm_device *);
+extern void nv20_graph_takedown(struct drm_device *);
+extern int  nv30_graph_init(struct drm_device *);
+
+/* nv40_graph.c */
+extern struct nouveau_pgraph_object_class nv40_graph_grclass[];
+extern int  nv40_graph_init(struct drm_device *);
+extern void nv40_graph_takedown(struct drm_device *);
+extern struct nouveau_channel *nv40_graph_channel(struct drm_device *);
+extern int  nv40_graph_create_context(struct nouveau_channel *);
+extern void nv40_graph_destroy_context(struct nouveau_channel *);
+extern int  nv40_graph_load_context(struct nouveau_channel *);
+extern int  nv40_graph_unload_context(struct drm_device *);
+extern int  nv40_grctx_init(struct drm_device *);
+extern void nv40_grctx_fini(struct drm_device *);
+extern void nv40_grctx_vals_load(struct drm_device *, struct nouveau_gpuobj *);
+
+/* nv50_graph.c */
+extern struct nouveau_pgraph_object_class nv50_graph_grclass[];
+extern int  nv50_graph_init(struct drm_device *);
+extern void nv50_graph_takedown(struct drm_device *);
+extern void nv50_graph_fifo_access(struct drm_device *, bool);
+extern struct nouveau_channel *nv50_graph_channel(struct drm_device *);
+extern int  nv50_graph_create_context(struct nouveau_channel *);
+extern void nv50_graph_destroy_context(struct nouveau_channel *);
+extern int  nv50_graph_load_context(struct nouveau_channel *);
+extern int  nv50_graph_unload_context(struct drm_device *);
+extern void nv50_graph_context_switch(struct drm_device *);
+
+/* nv04_instmem.c */
+extern int  nv04_instmem_init(struct drm_device *);
+extern void nv04_instmem_takedown(struct drm_device *);
+extern int  nv04_instmem_suspend(struct drm_device *);
+extern void nv04_instmem_resume(struct drm_device *);
+extern int  nv04_instmem_populate(struct drm_device *, struct nouveau_gpuobj *,
+				  uint32_t *size);
+extern void nv04_instmem_clear(struct drm_device *, struct nouveau_gpuobj *);
+extern int  nv04_instmem_bind(struct drm_device *, struct nouveau_gpuobj *);
+extern int  nv04_instmem_unbind(struct drm_device *, struct nouveau_gpuobj *);
+extern void nv04_instmem_prepare_access(struct drm_device *, bool write);
+extern void nv04_instmem_finish_access(struct drm_device *);
+
+/* nv50_instmem.c */
+extern int  nv50_instmem_init(struct drm_device *);
+extern void nv50_instmem_takedown(struct drm_device *);
+extern int  nv50_instmem_suspend(struct drm_device *);
+extern void nv50_instmem_resume(struct drm_device *);
+extern int  nv50_instmem_populate(struct drm_device *, struct nouveau_gpuobj *,
+				  uint32_t *size);
+extern void nv50_instmem_clear(struct drm_device *, struct nouveau_gpuobj *);
+extern int  nv50_instmem_bind(struct drm_device *, struct nouveau_gpuobj *);
+extern int  nv50_instmem_unbind(struct drm_device *, struct nouveau_gpuobj *);
+extern void nv50_instmem_prepare_access(struct drm_device *, bool write);
+extern void nv50_instmem_finish_access(struct drm_device *);
+
+/* nv04_mc.c */
+extern int  nv04_mc_init(struct drm_device *);
+extern void nv04_mc_takedown(struct drm_device *);
+
+/* nv40_mc.c */
+extern int  nv40_mc_init(struct drm_device *);
+extern void nv40_mc_takedown(struct drm_device *);
+
+/* nv50_mc.c */
+extern int  nv50_mc_init(struct drm_device *);
+extern void nv50_mc_takedown(struct drm_device *);
+
+/* nv04_timer.c */
+extern int  nv04_timer_init(struct drm_device *);
+extern uint64_t nv04_timer_read(struct drm_device *);
+extern void nv04_timer_takedown(struct drm_device *);
+
+extern long nouveau_compat_ioctl(struct file *file, unsigned int cmd,
+				 unsigned long arg);
+
+/* nv04_dac.c */
+extern int nv04_dac_create(struct drm_device *dev, struct dcb_entry *entry);
+extern enum drm_connector_status nv17_dac_detect(struct drm_encoder *encoder,
+						 struct drm_connector *connector);
+extern int nv04_dac_output_offset(struct drm_encoder *encoder);
+extern void nv04_dac_update_dacclk(struct drm_encoder *encoder, bool enable);
+
+/* nv04_dfp.c */
+extern int nv04_dfp_create(struct drm_device *dev, struct dcb_entry *entry);
+extern int nv04_dfp_get_bound_head(struct drm_device *dev, struct dcb_entry *dcbent);
+extern void nv04_dfp_bind_head(struct drm_device *dev, struct dcb_entry *dcbent,
+			       int head, bool dl);
+extern void nv04_dfp_disable(struct drm_device *dev, int head);
+extern void nv04_dfp_update_fp_control(struct drm_encoder *encoder, int mode);
+
+/* nv04_tv.c */
+extern int nv04_tv_identify(struct drm_device *dev, int i2c_index);
+extern int nv04_tv_create(struct drm_device *dev, struct dcb_entry *entry);
+
+/* nv17_tv.c */
+extern int nv17_tv_create(struct drm_device *dev, struct dcb_entry *entry);
+extern enum drm_connector_status nv17_tv_detect(struct drm_encoder *encoder,
+						struct drm_connector *connector,
+						uint32_t pin_mask);
+
+/* nv04_display.c */
+extern int nv04_display_create(struct drm_device *);
+extern void nv04_display_destroy(struct drm_device *);
+extern void nv04_display_restore(struct drm_device *);
+
+/* nv04_crtc.c */
+extern int nv04_crtc_create(struct drm_device *, int index);
+
+/* nouveau_bo.c */
+extern struct ttm_bo_driver nouveau_bo_driver;
+extern int nouveau_bo_new(struct drm_device *, struct nouveau_channel *,
+			  int size, int align, uint32_t flags,
+			  uint32_t tile_mode, uint32_t tile_flags,
+			  bool no_vm, bool mappable, struct nouveau_bo **);
+extern int nouveau_bo_pin(struct nouveau_bo *, uint32_t flags);
+extern int nouveau_bo_unpin(struct nouveau_bo *);
+extern int nouveau_bo_map(struct nouveau_bo *);
+extern void nouveau_bo_unmap(struct nouveau_bo *);
+extern void nouveau_bo_placement_set(struct nouveau_bo *, uint32_t memtype);
+extern u16 nouveau_bo_rd16(struct nouveau_bo *nvbo, unsigned index);
+extern void nouveau_bo_wr16(struct nouveau_bo *nvbo, unsigned index, u16 val);
+extern u32 nouveau_bo_rd32(struct nouveau_bo *nvbo, unsigned index);
+extern void nouveau_bo_wr32(struct nouveau_bo *nvbo, unsigned index, u32 val);
+
+/* nouveau_fence.c */
+struct nouveau_fence;
+extern int nouveau_fence_init(struct nouveau_channel *);
+extern void nouveau_fence_fini(struct nouveau_channel *);
+extern void nouveau_fence_update(struct nouveau_channel *);
+extern int nouveau_fence_new(struct nouveau_channel *, struct nouveau_fence **,
+			     bool emit);
+extern int nouveau_fence_emit(struct nouveau_fence *);
+struct nouveau_channel *nouveau_fence_channel(struct nouveau_fence *);
+extern bool nouveau_fence_signalled(void *obj, void *arg);
+extern int nouveau_fence_wait(void *obj, void *arg, bool lazy, bool intr);
+extern int nouveau_fence_flush(void *obj, void *arg);
+extern void nouveau_fence_unref(void **obj);
+extern void *nouveau_fence_ref(void *obj);
+extern void nouveau_fence_handler(struct drm_device *dev, int channel);
+
+/* nouveau_gem.c */
+extern int nouveau_gem_new(struct drm_device *, struct nouveau_channel *,
+			   int size, int align, uint32_t flags,
+			   uint32_t tile_mode, uint32_t tile_flags,
+			   bool no_vm, bool mappable, struct nouveau_bo **);
+extern int nouveau_gem_object_new(struct drm_gem_object *);
+extern void nouveau_gem_object_del(struct drm_gem_object *);
+extern int nouveau_gem_ioctl_new(struct drm_device *, void *,
+				 struct drm_file *);
+extern int nouveau_gem_ioctl_pushbuf(struct drm_device *, void *,
+				     struct drm_file *);
+extern int nouveau_gem_ioctl_pushbuf_call(struct drm_device *, void *,
+					  struct drm_file *);
+extern int nouveau_gem_ioctl_pushbuf_call2(struct drm_device *, void *,
+					   struct drm_file *);
+extern int nouveau_gem_ioctl_pin(struct drm_device *, void *,
+				 struct drm_file *);
+extern int nouveau_gem_ioctl_unpin(struct drm_device *, void *,
+				   struct drm_file *);
+extern int nouveau_gem_ioctl_tile(struct drm_device *, void *,
+				  struct drm_file *);
+extern int nouveau_gem_ioctl_cpu_prep(struct drm_device *, void *,
+				      struct drm_file *);
+extern int nouveau_gem_ioctl_cpu_fini(struct drm_device *, void *,
+				      struct drm_file *);
+extern int nouveau_gem_ioctl_info(struct drm_device *, void *,
+				  struct drm_file *);
+
+/* nv17_gpio.c */
+int nv17_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag);
+int nv17_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state);
+
+#ifndef ioread32_native
+#ifdef __BIG_ENDIAN
+#define ioread16_native ioread16be
+#define iowrite16_native iowrite16be
+#define ioread32_native  ioread32be
+#define iowrite32_native iowrite32be
+#else /* def __BIG_ENDIAN */
+#define ioread16_native ioread16
+#define iowrite16_native iowrite16
+#define ioread32_native  ioread32
+#define iowrite32_native iowrite32
+#endif /* def __BIG_ENDIAN else */
+#endif /* !ioread32_native */
+
+/* channel control reg access */
+static inline u32 nvchan_rd32(struct nouveau_channel *chan, unsigned reg)
+{
+	return ioread32_native(chan->user + reg);
+}
+
+static inline void nvchan_wr32(struct nouveau_channel *chan,
+							unsigned reg, u32 val)
+{
+	iowrite32_native(val, chan->user + reg);
+}
+
+/* register access */
+static inline u32 nv_rd32(struct drm_device *dev, unsigned reg)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	return ioread32_native(dev_priv->mmio + reg);
+}
+
+static inline void nv_wr32(struct drm_device *dev, unsigned reg, u32 val)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	iowrite32_native(val, dev_priv->mmio + reg);
+}
+
+static inline u8 nv_rd08(struct drm_device *dev, unsigned reg)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	return ioread8(dev_priv->mmio + reg);
+}
+
+static inline void nv_wr08(struct drm_device *dev, unsigned reg, u8 val)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	iowrite8(val, dev_priv->mmio + reg);
+}
+
+#define nv_wait(reg, mask, val) \
+	nouveau_wait_until(dev, 2000000000ULL, (reg), (mask), (val))
+
+/* PRAMIN access */
+static inline u32 nv_ri32(struct drm_device *dev, unsigned offset)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	return ioread32_native(dev_priv->ramin + offset);
+}
+
+static inline void nv_wi32(struct drm_device *dev, unsigned offset, u32 val)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	iowrite32_native(val, dev_priv->ramin + offset);
+}
+
+/* object access */
+static inline u32 nv_ro32(struct drm_device *dev, struct nouveau_gpuobj *obj,
+				unsigned index)
+{
+	return nv_ri32(dev, obj->im_pramin->start + index * 4);
+}
+
+static inline void nv_wo32(struct drm_device *dev, struct nouveau_gpuobj *obj,
+				unsigned index, u32 val)
+{
+	nv_wi32(dev, obj->im_pramin->start + index * 4, val);
+}
+
+/*
+ * Logging
+ * Argument d is (struct drm_device *).
+ */
+#define NV_PRINTK(level, d, fmt, arg...) \
+	printk(level "[" DRM_NAME "] " DRIVER_NAME " %s: " fmt, \
+					pci_name(d->pdev), ##arg)
+#ifndef NV_DEBUG_NOTRACE
+#define NV_DEBUG(d, fmt, arg...) do {                                          \
+	if (drm_debug) {                                                       \
+		NV_PRINTK(KERN_DEBUG, d, "%s:%d - " fmt, __func__,             \
+			  __LINE__, ##arg);                                    \
+	}                                                                      \
+} while (0)
+#else
+#define NV_DEBUG(d, fmt, arg...) do {                                          \
+	if (drm_debug)                                                         \
+		NV_PRINTK(KERN_DEBUG, d, fmt, ##arg);                          \
+} while (0)
+#endif
+#define NV_ERROR(d, fmt, arg...) NV_PRINTK(KERN_ERR, d, fmt, ##arg)
+#define NV_INFO(d, fmt, arg...) NV_PRINTK(KERN_INFO, d, fmt, ##arg)
+#define NV_TRACEWARN(d, fmt, arg...) NV_PRINTK(KERN_NOTICE, d, fmt, ##arg)
+#define NV_TRACE(d, fmt, arg...) NV_PRINTK(KERN_INFO, d, fmt, ##arg)
+#define NV_WARN(d, fmt, arg...) NV_PRINTK(KERN_WARNING, d, fmt, ##arg)
+
+/* nouveau_reg_debug bitmask */
+enum {
+	NOUVEAU_REG_DEBUG_MC             = 0x1,
+	NOUVEAU_REG_DEBUG_VIDEO          = 0x2,
+	NOUVEAU_REG_DEBUG_FB             = 0x4,
+	NOUVEAU_REG_DEBUG_EXTDEV         = 0x8,
+	NOUVEAU_REG_DEBUG_CRTC           = 0x10,
+	NOUVEAU_REG_DEBUG_RAMDAC         = 0x20,
+	NOUVEAU_REG_DEBUG_VGACRTC        = 0x40,
+	NOUVEAU_REG_DEBUG_RMVIO          = 0x80,
+	NOUVEAU_REG_DEBUG_VGAATTR        = 0x100,
+	NOUVEAU_REG_DEBUG_EVO            = 0x200,
+};
+
+#define NV_REG_DEBUG(type, dev, fmt, arg...) do { \
+	if (nouveau_reg_debug & NOUVEAU_REG_DEBUG_##type) \
+		NV_PRINTK(KERN_DEBUG, dev, "%s: " fmt, __func__, ##arg); \
+} while (0)
+
+static inline bool
+nv_two_heads(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	const int impl = dev->pci_device & 0x0ff0;
+
+	if (dev_priv->card_type >= NV_10 && impl != 0x0100 &&
+	    impl != 0x0150 && impl != 0x01a0 && impl != 0x0200)
+		return true;
+
+	return false;
+}
+
+static inline bool
+nv_gf4_disp_arch(struct drm_device *dev)
+{
+	return nv_two_heads(dev) && (dev->pci_device & 0x0ff0) != 0x0110;
+}
+
+static inline bool
+nv_two_reg_pll(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	const int impl = dev->pci_device & 0x0ff0;
+
+	if (impl == 0x0310 || impl == 0x0340 || dev_priv->card_type >= NV_40)
+		return true;
+	return false;
+}
+
+#define NV50_NVSW                                                    0x0000506e
+#define NV50_NVSW_DMA_SEMAPHORE                                      0x00000060
+#define NV50_NVSW_SEMAPHORE_OFFSET                                   0x00000064
+#define NV50_NVSW_SEMAPHORE_ACQUIRE                                  0x00000068
+#define NV50_NVSW_SEMAPHORE_RELEASE                                  0x0000006c
+#define NV50_NVSW_DMA_VBLSEM                                         0x0000018c
+#define NV50_NVSW_VBLSEM_OFFSET                                      0x00000400
+#define NV50_NVSW_VBLSEM_RELEASE_VALUE                               0x00000404
+#define NV50_NVSW_VBLSEM_RELEASE                                     0x00000408
+
+#endif /* __NOUVEAU_DRV_H__ */
diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h
new file mode 100644
index 0000000..bc4a240
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef __NOUVEAU_ENCODER_H__
+#define __NOUVEAU_ENCODER_H__
+
+#include "drm_encoder_slave.h"
+#include "nouveau_drv.h"
+
+#define NV_DPMS_CLEARED 0x80
+
+struct nouveau_encoder {
+	struct drm_encoder_slave base;
+
+	struct dcb_entry *dcb;
+	int or;
+
+	struct drm_display_mode mode;
+	int last_dpms;
+
+	struct nv04_output_reg restore;
+
+	void (*disconnect)(struct nouveau_encoder *encoder);
+
+	union {
+		struct {
+			int dpcd_version;
+			int link_nr;
+			int link_bw;
+		} dp;
+	};
+};
+
+static inline struct nouveau_encoder *nouveau_encoder(struct drm_encoder *enc)
+{
+	struct drm_encoder_slave *slave = to_encoder_slave(enc);
+
+	return container_of(slave, struct nouveau_encoder, base);
+}
+
+static inline struct drm_encoder *to_drm_encoder(struct nouveau_encoder *enc)
+{
+	return &enc->base.base;
+}
+
+struct nouveau_connector *
+nouveau_encoder_connector_get(struct nouveau_encoder *encoder);
+int nv50_sor_create(struct drm_device *dev, struct dcb_entry *entry);
+int nv50_dac_create(struct drm_device *dev, struct dcb_entry *entry);
+
+struct bit_displayport_encoder_table {
+	uint32_t match;
+	uint8_t  record_nr;
+	uint8_t  unknown;
+	uint16_t script0;
+	uint16_t script1;
+	uint16_t unknown_table;
+} __attribute__ ((packed));
+
+struct bit_displayport_encoder_table_entry {
+	uint8_t vs_level;
+	uint8_t pre_level;
+	uint8_t reg0;
+	uint8_t reg1;
+	uint8_t reg2;
+} __attribute__ ((packed));
+
+#endif /* __NOUVEAU_ENCODER_H__ */
diff --git a/drivers/gpu/drm/nouveau/nouveau_fb.h b/drivers/gpu/drm/nouveau/nouveau_fb.h
new file mode 100644
index 0000000..4a3f31a
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_fb.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef __NOUVEAU_FB_H__
+#define __NOUVEAU_FB_H__
+
+struct nouveau_framebuffer {
+	struct drm_framebuffer base;
+	struct nouveau_bo *nvbo;
+};
+
+static inline struct nouveau_framebuffer *
+nouveau_framebuffer(struct drm_framebuffer *fb)
+{
+	return container_of(fb, struct nouveau_framebuffer, base);
+}
+
+extern const struct drm_mode_config_funcs nouveau_mode_config_funcs;
+
+struct drm_framebuffer *
+nouveau_framebuffer_create(struct drm_device *, struct nouveau_bo *,
+			   struct drm_mode_fb_cmd *);
+
+#endif /* __NOUVEAU_FB_H__ */
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
new file mode 100644
index 0000000..36e8c5e
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
@@ -0,0 +1,380 @@
+/*
+ * Copyright © 2007 David Airlie
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *     David Airlie
+ */
+
+#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/sysrq.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/screen_info.h>
+
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+#include "drm_fb_helper.h"
+#include "nouveau_drv.h"
+#include "nouveau_drm.h"
+#include "nouveau_crtc.h"
+#include "nouveau_fb.h"
+#include "nouveau_fbcon.h"
+#include "nouveau_dma.h"
+
+static int
+nouveau_fbcon_sync(struct fb_info *info)
+{
+	struct nouveau_fbcon_par *par = info->par;
+	struct drm_device *dev = par->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_channel *chan = dev_priv->channel;
+	int ret, i;
+
+	if (!chan->accel_done ||
+	    info->state != FBINFO_STATE_RUNNING ||
+	    info->flags & FBINFO_HWACCEL_DISABLED)
+		return 0;
+
+	if (RING_SPACE(chan, 4)) {
+		NV_ERROR(dev, "GPU lockup - switching to software fbcon\n");
+		info->flags |= FBINFO_HWACCEL_DISABLED;
+		return 0;
+	}
+
+	BEGIN_RING(chan, 0, 0x0104, 1);
+	OUT_RING(chan, 0);
+	BEGIN_RING(chan, 0, 0x0100, 1);
+	OUT_RING(chan, 0);
+	nouveau_bo_wr32(chan->notifier_bo, chan->m2mf_ntfy + 3, 0xffffffff);
+	FIRE_RING(chan);
+
+	ret = -EBUSY;
+	for (i = 0; i < 100000; i++) {
+		if (!nouveau_bo_rd32(chan->notifier_bo, chan->m2mf_ntfy + 3)) {
+			ret = 0;
+			break;
+		}
+		DRM_UDELAY(1);
+	}
+
+	if (ret) {
+		NV_ERROR(dev, "GPU lockup - switching to software fbcon\n");
+		info->flags |= FBINFO_HWACCEL_DISABLED;
+		return 0;
+	}
+
+	chan->accel_done = false;
+	return 0;
+}
+
+static struct fb_ops nouveau_fbcon_ops = {
+	.owner = THIS_MODULE,
+	.fb_check_var = drm_fb_helper_check_var,
+	.fb_set_par = drm_fb_helper_set_par,
+	.fb_setcolreg = drm_fb_helper_setcolreg,
+	.fb_fillrect = cfb_fillrect,
+	.fb_copyarea = cfb_copyarea,
+	.fb_imageblit = cfb_imageblit,
+	.fb_sync = nouveau_fbcon_sync,
+	.fb_pan_display = drm_fb_helper_pan_display,
+	.fb_blank = drm_fb_helper_blank,
+	.fb_setcmap = drm_fb_helper_setcmap,
+};
+
+static void nouveau_fbcon_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
+				    u16 blue, int regno)
+{
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+
+	nv_crtc->lut.r[regno] = red;
+	nv_crtc->lut.g[regno] = green;
+	nv_crtc->lut.b[regno] = blue;
+}
+
+static void nouveau_fbcon_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
+				    u16 *blue, int regno)
+{
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+
+	*red = nv_crtc->lut.r[regno];
+	*green = nv_crtc->lut.g[regno];
+	*blue = nv_crtc->lut.b[regno];
+}
+
+static struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = {
+	.gamma_set = nouveau_fbcon_gamma_set,
+	.gamma_get = nouveau_fbcon_gamma_get
+};
+
+#if defined(__i386__) || defined(__x86_64__)
+static bool
+nouveau_fbcon_has_vesafb_or_efifb(struct drm_device *dev)
+{
+	struct pci_dev *pdev = dev->pdev;
+	int ramin;
+
+	if (screen_info.orig_video_isVGA != VIDEO_TYPE_VLFB &&
+	    screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
+		return false;
+
+	if (screen_info.lfb_base < pci_resource_start(pdev, 1))
+		goto not_fb;
+
+	if (screen_info.lfb_base + screen_info.lfb_size >=
+	    pci_resource_start(pdev, 1) + pci_resource_len(pdev, 1))
+		goto not_fb;
+
+	return true;
+not_fb:
+	ramin = 2;
+	if (pci_resource_len(pdev, ramin) == 0) {
+		ramin = 3;
+		if (pci_resource_len(pdev, ramin) == 0)
+			return false;
+	}
+
+	if (screen_info.lfb_base < pci_resource_start(pdev, ramin))
+		return false;
+
+	if (screen_info.lfb_base + screen_info.lfb_size >=
+	    pci_resource_start(pdev, ramin) + pci_resource_len(pdev, ramin))
+		return false;
+
+	return true;
+}
+#endif
+
+void
+nouveau_fbcon_zfill(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct fb_info *info = dev_priv->fbdev_info;
+	struct fb_fillrect rect;
+
+	/* Clear the entire fbcon.  The drm will program every connector
+	 * with it's preferred mode.  If the sizes differ, one display will
+	 * quite likely have garbage around the console.
+	 */
+	rect.dx = rect.dy = 0;
+	rect.width = info->var.xres_virtual;
+	rect.height = info->var.yres_virtual;
+	rect.color = 0;
+	rect.rop = ROP_COPY;
+	info->fbops->fb_fillrect(info, &rect);
+}
+
+static int
+nouveau_fbcon_create(struct drm_device *dev, uint32_t fb_width,
+		     uint32_t fb_height, uint32_t surface_width,
+		     uint32_t surface_height, uint32_t surface_depth,
+		     uint32_t surface_bpp, struct drm_framebuffer **pfb)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct fb_info *info;
+	struct nouveau_fbcon_par *par;
+	struct drm_framebuffer *fb;
+	struct nouveau_framebuffer *nouveau_fb;
+	struct nouveau_bo *nvbo;
+	struct drm_mode_fb_cmd mode_cmd;
+	struct device *device = &dev->pdev->dev;
+	int size, ret;
+
+	mode_cmd.width = surface_width;
+	mode_cmd.height = surface_height;
+
+	mode_cmd.bpp = surface_bpp;
+	mode_cmd.pitch = mode_cmd.width * (mode_cmd.bpp >> 3);
+	mode_cmd.pitch = ALIGN(mode_cmd.pitch, 256);
+	mode_cmd.depth = surface_depth;
+
+	size = mode_cmd.pitch * mode_cmd.height;
+	size = ALIGN(size, PAGE_SIZE);
+
+	ret = nouveau_gem_new(dev, dev_priv->channel, size, 0, TTM_PL_FLAG_VRAM,
+			      0, 0x0000, false, true, &nvbo);
+	if (ret) {
+		NV_ERROR(dev, "failed to allocate framebuffer\n");
+		goto out;
+	}
+
+	ret = nouveau_bo_pin(nvbo, TTM_PL_FLAG_VRAM);
+	if (ret) {
+		NV_ERROR(dev, "failed to pin fb: %d\n", ret);
+		nouveau_bo_ref(NULL, &nvbo);
+		goto out;
+	}
+
+	ret = nouveau_bo_map(nvbo);
+	if (ret) {
+		NV_ERROR(dev, "failed to map fb: %d\n", ret);
+		nouveau_bo_unpin(nvbo);
+		nouveau_bo_ref(NULL, &nvbo);
+		goto out;
+	}
+
+	mutex_lock(&dev->struct_mutex);
+
+	fb = nouveau_framebuffer_create(dev, nvbo, &mode_cmd);
+	if (!fb) {
+		ret = -ENOMEM;
+		NV_ERROR(dev, "failed to allocate fb.\n");
+		goto out_unref;
+	}
+
+	list_add(&fb->filp_head, &dev->mode_config.fb_kernel_list);
+
+	nouveau_fb = nouveau_framebuffer(fb);
+	*pfb = fb;
+
+	info = framebuffer_alloc(sizeof(struct nouveau_fbcon_par), device);
+	if (!info) {
+		ret = -ENOMEM;
+		goto out_unref;
+	}
+
+	par = info->par;
+	par->helper.funcs = &nouveau_fbcon_helper_funcs;
+	par->helper.dev = dev;
+	ret = drm_fb_helper_init_crtc_count(&par->helper, 2, 4);
+	if (ret)
+		goto out_unref;
+	dev_priv->fbdev_info = info;
+
+	strcpy(info->fix.id, "nouveaufb");
+	info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA |
+		      FBINFO_HWACCEL_FILLRECT | FBINFO_HWACCEL_IMAGEBLIT;
+	info->fbops = &nouveau_fbcon_ops;
+	info->fix.smem_start = dev->mode_config.fb_base + nvbo->bo.offset -
+			       dev_priv->vm_vram_base;
+	info->fix.smem_len = size;
+
+	info->screen_base = nvbo_kmap_obj_iovirtual(nouveau_fb->nvbo);
+	info->screen_size = size;
+
+	drm_fb_helper_fill_fix(info, fb->pitch, fb->depth);
+	drm_fb_helper_fill_var(info, fb, fb_width, fb_height);
+
+	/* FIXME: we really shouldn't expose mmio space at all */
+	info->fix.mmio_start = pci_resource_start(dev->pdev, 1);
+	info->fix.mmio_len = pci_resource_len(dev->pdev, 1);
+
+	/* Set aperture base/size for vesafb takeover */
+#if defined(__i386__) || defined(__x86_64__)
+	if (nouveau_fbcon_has_vesafb_or_efifb(dev)) {
+		/* Some NVIDIA VBIOS' are stupid and decide to put the
+		 * framebuffer in the middle of the PRAMIN BAR for
+		 * whatever reason.  We need to know the exact lfb_base
+		 * to get vesafb kicked off, and the only reliable way
+		 * we have left is to find out lfb_base the same way
+		 * vesafb did.
+		 */
+		info->aperture_base = screen_info.lfb_base;
+		info->aperture_size = screen_info.lfb_size;
+		if (screen_info.orig_video_isVGA == VIDEO_TYPE_VLFB)
+			info->aperture_size *= 65536;
+	} else
+#endif
+	{
+		info->aperture_base = info->fix.mmio_start;
+		info->aperture_size = info->fix.mmio_len;
+	}
+
+	info->pixmap.size = 64*1024;
+	info->pixmap.buf_align = 8;
+	info->pixmap.access_align = 32;
+	info->pixmap.flags = FB_PIXMAP_SYSTEM;
+	info->pixmap.scan_align = 1;
+
+	fb->fbdev = info;
+
+	par->nouveau_fb = nouveau_fb;
+	par->dev = dev;
+
+	switch (dev_priv->card_type) {
+	case NV_50:
+		nv50_fbcon_accel_init(info);
+		break;
+	default:
+		nv04_fbcon_accel_init(info);
+		break;
+	};
+
+	nouveau_fbcon_zfill(dev);
+
+	/* To allow resizeing without swapping buffers */
+	NV_INFO(dev, "allocated %dx%d fb: 0x%lx, bo %p\n",
+						nouveau_fb->base.width,
+						nouveau_fb->base.height,
+						nvbo->bo.offset, nvbo);
+
+	mutex_unlock(&dev->struct_mutex);
+	return 0;
+
+out_unref:
+	mutex_unlock(&dev->struct_mutex);
+out:
+	return ret;
+}
+
+int
+nouveau_fbcon_probe(struct drm_device *dev)
+{
+	NV_DEBUG(dev, "\n");
+
+	return drm_fb_helper_single_fb_probe(dev, 32, nouveau_fbcon_create);
+}
+
+int
+nouveau_fbcon_remove(struct drm_device *dev, struct drm_framebuffer *fb)
+{
+	struct nouveau_framebuffer *nouveau_fb = nouveau_framebuffer(fb);
+	struct fb_info *info;
+
+	if (!fb)
+		return -EINVAL;
+
+	info = fb->fbdev;
+	if (info) {
+		struct nouveau_fbcon_par *par = info->par;
+
+		unregister_framebuffer(info);
+		nouveau_bo_unmap(nouveau_fb->nvbo);
+		mutex_lock(&dev->struct_mutex);
+		drm_gem_object_unreference(nouveau_fb->nvbo->gem);
+		nouveau_fb->nvbo = NULL;
+		mutex_unlock(&dev->struct_mutex);
+		if (par)
+			drm_fb_helper_free(&par->helper);
+		framebuffer_release(info);
+	}
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.h b/drivers/gpu/drm/nouveau/nouveau_fbcon.h
new file mode 100644
index 0000000..8531140
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef __NOUVEAU_FBCON_H__
+#define __NOUVEAU_FBCON_H__
+
+#include "drm_fb_helper.h"
+
+struct nouveau_fbcon_par {
+	struct drm_fb_helper helper;
+	struct drm_device *dev;
+	struct nouveau_framebuffer *nouveau_fb;
+};
+
+int nouveau_fbcon_probe(struct drm_device *dev);
+int nouveau_fbcon_remove(struct drm_device *dev, struct drm_framebuffer *fb);
+void nouveau_fbcon_restore(void);
+void nouveau_fbcon_zfill(struct drm_device *dev);
+
+int nv04_fbcon_accel_init(struct fb_info *info);
+int nv50_fbcon_accel_init(struct fb_info *info);
+
+#endif /* __NV50_FBCON_H__ */
+
diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c
new file mode 100644
index 0000000..0cff7eb
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_fence.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2007 Ben Skeggs.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "drmP.h"
+#include "drm.h"
+
+#include "nouveau_drv.h"
+#include "nouveau_dma.h"
+
+#define USE_REFCNT (dev_priv->card_type >= NV_10)
+
+struct nouveau_fence {
+	struct nouveau_channel *channel;
+	struct kref refcount;
+	struct list_head entry;
+
+	uint32_t sequence;
+	bool signalled;
+};
+
+static inline struct nouveau_fence *
+nouveau_fence(void *sync_obj)
+{
+	return (struct nouveau_fence *)sync_obj;
+}
+
+static void
+nouveau_fence_del(struct kref *ref)
+{
+	struct nouveau_fence *fence =
+		container_of(ref, struct nouveau_fence, refcount);
+
+	kfree(fence);
+}
+
+void
+nouveau_fence_update(struct nouveau_channel *chan)
+{
+	struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
+	struct list_head *entry, *tmp;
+	struct nouveau_fence *fence;
+	uint32_t sequence;
+
+	if (USE_REFCNT)
+		sequence = nvchan_rd32(chan, 0x48);
+	else
+		sequence = chan->fence.last_sequence_irq;
+
+	if (chan->fence.sequence_ack == sequence)
+		return;
+	chan->fence.sequence_ack = sequence;
+
+	list_for_each_safe(entry, tmp, &chan->fence.pending) {
+		fence = list_entry(entry, struct nouveau_fence, entry);
+
+		sequence = fence->sequence;
+		fence->signalled = true;
+		list_del(&fence->entry);
+		kref_put(&fence->refcount, nouveau_fence_del);
+
+		if (sequence == chan->fence.sequence_ack)
+			break;
+	}
+}
+
+int
+nouveau_fence_new(struct nouveau_channel *chan, struct nouveau_fence **pfence,
+		  bool emit)
+{
+	struct nouveau_fence *fence;
+	int ret = 0;
+
+	fence = kzalloc(sizeof(*fence), GFP_KERNEL);
+	if (!fence)
+		return -ENOMEM;
+	kref_init(&fence->refcount);
+	fence->channel = chan;
+
+	if (emit)
+		ret = nouveau_fence_emit(fence);
+
+	if (ret)
+		nouveau_fence_unref((void *)&fence);
+	*pfence = fence;
+	return ret;
+}
+
+struct nouveau_channel *
+nouveau_fence_channel(struct nouveau_fence *fence)
+{
+	return fence ? fence->channel : NULL;
+}
+
+int
+nouveau_fence_emit(struct nouveau_fence *fence)
+{
+	struct drm_nouveau_private *dev_priv = fence->channel->dev->dev_private;
+	struct nouveau_channel *chan = fence->channel;
+	unsigned long flags;
+	int ret;
+
+	ret = RING_SPACE(chan, 2);
+	if (ret)
+		return ret;
+
+	if (unlikely(chan->fence.sequence == chan->fence.sequence_ack - 1)) {
+		spin_lock_irqsave(&chan->fence.lock, flags);
+		nouveau_fence_update(chan);
+		spin_unlock_irqrestore(&chan->fence.lock, flags);
+
+		BUG_ON(chan->fence.sequence ==
+		       chan->fence.sequence_ack - 1);
+	}
+
+	fence->sequence = ++chan->fence.sequence;
+
+	kref_get(&fence->refcount);
+	spin_lock_irqsave(&chan->fence.lock, flags);
+	list_add_tail(&fence->entry, &chan->fence.pending);
+	spin_unlock_irqrestore(&chan->fence.lock, flags);
+
+	BEGIN_RING(chan, NvSubM2MF, USE_REFCNT ? 0x0050 : 0x0150, 1);
+	OUT_RING(chan, fence->sequence);
+	FIRE_RING(chan);
+
+	return 0;
+}
+
+void
+nouveau_fence_unref(void **sync_obj)
+{
+	struct nouveau_fence *fence = nouveau_fence(*sync_obj);
+
+	if (fence)
+		kref_put(&fence->refcount, nouveau_fence_del);
+	*sync_obj = NULL;
+}
+
+void *
+nouveau_fence_ref(void *sync_obj)
+{
+	struct nouveau_fence *fence = nouveau_fence(sync_obj);
+
+	kref_get(&fence->refcount);
+	return sync_obj;
+}
+
+bool
+nouveau_fence_signalled(void *sync_obj, void *sync_arg)
+{
+	struct nouveau_fence *fence = nouveau_fence(sync_obj);
+	struct nouveau_channel *chan = fence->channel;
+	unsigned long flags;
+
+	if (fence->signalled)
+		return true;
+
+	spin_lock_irqsave(&chan->fence.lock, flags);
+	nouveau_fence_update(chan);
+	spin_unlock_irqrestore(&chan->fence.lock, flags);
+	return fence->signalled;
+}
+
+int
+nouveau_fence_wait(void *sync_obj, void *sync_arg, bool lazy, bool intr)
+{
+	unsigned long timeout = jiffies + (3 * DRM_HZ);
+	int ret = 0;
+
+	__set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
+
+	while (1) {
+		if (nouveau_fence_signalled(sync_obj, sync_arg))
+			break;
+
+		if (time_after_eq(jiffies, timeout)) {
+			ret = -EBUSY;
+			break;
+		}
+
+		if (lazy)
+			schedule_timeout(1);
+
+		if (intr && signal_pending(current)) {
+			ret = -ERESTART;
+			break;
+		}
+	}
+
+	__set_current_state(TASK_RUNNING);
+
+	return ret;
+}
+
+int
+nouveau_fence_flush(void *sync_obj, void *sync_arg)
+{
+	return 0;
+}
+
+void
+nouveau_fence_handler(struct drm_device *dev, int channel)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_channel *chan = NULL;
+
+	if (channel >= 0 && channel < dev_priv->engine.fifo.channels)
+		chan = dev_priv->fifos[channel];
+
+	if (chan) {
+		spin_lock_irq(&chan->fence.lock);
+		nouveau_fence_update(chan);
+		spin_unlock_irq(&chan->fence.lock);
+	}
+}
+
+int
+nouveau_fence_init(struct nouveau_channel *chan)
+{
+	INIT_LIST_HEAD(&chan->fence.pending);
+	spin_lock_init(&chan->fence.lock);
+	return 0;
+}
+
+void
+nouveau_fence_fini(struct nouveau_channel *chan)
+{
+	struct list_head *entry, *tmp;
+	struct nouveau_fence *fence;
+
+	list_for_each_safe(entry, tmp, &chan->fence.pending) {
+		fence = list_entry(entry, struct nouveau_fence, entry);
+
+		fence->signalled = true;
+		list_del(&fence->entry);
+		kref_put(&fence->refcount, nouveau_fence_del);
+	}
+}
+
diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c
new file mode 100644
index 0000000..11f831f
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
@@ -0,0 +1,992 @@
+/*
+ * Copyright (C) 2008 Ben Skeggs.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+#include "drmP.h"
+#include "drm.h"
+
+#include "nouveau_drv.h"
+#include "nouveau_drm.h"
+#include "nouveau_dma.h"
+
+#define nouveau_gem_pushbuf_sync(chan) 0
+
+int
+nouveau_gem_object_new(struct drm_gem_object *gem)
+{
+	return 0;
+}
+
+void
+nouveau_gem_object_del(struct drm_gem_object *gem)
+{
+	struct nouveau_bo *nvbo = gem->driver_private;
+	struct ttm_buffer_object *bo = &nvbo->bo;
+
+	if (!nvbo)
+		return;
+	nvbo->gem = NULL;
+
+	if (unlikely(nvbo->cpu_filp))
+		ttm_bo_synccpu_write_release(bo);
+
+	if (unlikely(nvbo->pin_refcnt)) {
+		nvbo->pin_refcnt = 1;
+		nouveau_bo_unpin(nvbo);
+	}
+
+	ttm_bo_unref(&bo);
+}
+
+int
+nouveau_gem_new(struct drm_device *dev, struct nouveau_channel *chan,
+		int size, int align, uint32_t flags, uint32_t tile_mode,
+		uint32_t tile_flags, bool no_vm, bool mappable,
+		struct nouveau_bo **pnvbo)
+{
+	struct nouveau_bo *nvbo;
+	int ret;
+
+	ret = nouveau_bo_new(dev, chan, size, align, flags, tile_mode,
+			     tile_flags, no_vm, mappable, pnvbo);
+	if (ret)
+		return ret;
+	nvbo = *pnvbo;
+
+	nvbo->gem = drm_gem_object_alloc(dev, nvbo->bo.mem.size);
+	if (!nvbo->gem) {
+		nouveau_bo_ref(NULL, pnvbo);
+		return -ENOMEM;
+	}
+
+	nvbo->bo.persistant_swap_storage = nvbo->gem->filp;
+	nvbo->gem->driver_private = nvbo;
+	return 0;
+}
+
+static int
+nouveau_gem_info(struct drm_gem_object *gem, struct drm_nouveau_gem_info *rep)
+{
+	struct nouveau_bo *nvbo = nouveau_gem_object(gem);
+
+	if (nvbo->bo.mem.mem_type == TTM_PL_TT)
+		rep->domain = NOUVEAU_GEM_DOMAIN_GART;
+	else
+		rep->domain = NOUVEAU_GEM_DOMAIN_VRAM;
+
+	rep->size = nvbo->bo.mem.num_pages << PAGE_SHIFT;
+	rep->offset = nvbo->bo.offset;
+	rep->map_handle = nvbo->mappable ? nvbo->bo.addr_space_offset : 0;
+	rep->tile_mode = nvbo->tile_mode;
+	rep->tile_flags = nvbo->tile_flags;
+	return 0;
+}
+
+static bool
+nouveau_gem_tile_flags_valid(struct drm_device *dev, uint32_t tile_flags) {
+	switch (tile_flags) {
+	case 0x0000:
+	case 0x1800:
+	case 0x2800:
+	case 0x4800:
+	case 0x7000:
+	case 0x7400:
+	case 0x7a00:
+	case 0xe000:
+		break;
+	default:
+		NV_ERROR(dev, "bad page flags: 0x%08x\n", tile_flags);
+		return false;
+	}
+
+	return true;
+}
+
+int
+nouveau_gem_ioctl_new(struct drm_device *dev, void *data,
+		      struct drm_file *file_priv)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct drm_nouveau_gem_new *req = data;
+	struct nouveau_bo *nvbo = NULL;
+	struct nouveau_channel *chan = NULL;
+	uint32_t flags = 0;
+	int ret = 0;
+
+	NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
+
+	if (unlikely(dev_priv->ttm.bdev.dev_mapping == NULL))
+		dev_priv->ttm.bdev.dev_mapping = dev_priv->dev->dev_mapping;
+
+	if (req->channel_hint) {
+		NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(req->channel_hint,
+						     file_priv, chan);
+	}
+
+	if (req->info.domain & NOUVEAU_GEM_DOMAIN_VRAM)
+		flags |= TTM_PL_FLAG_VRAM;
+	if (req->info.domain & NOUVEAU_GEM_DOMAIN_GART)
+		flags |= TTM_PL_FLAG_TT;
+	if (!flags || req->info.domain & NOUVEAU_GEM_DOMAIN_CPU)
+		flags |= TTM_PL_FLAG_SYSTEM;
+
+	if (!nouveau_gem_tile_flags_valid(dev, req->info.tile_flags))
+		return -EINVAL;
+
+	ret = nouveau_gem_new(dev, chan, req->info.size, req->align, flags,
+			      req->info.tile_mode, req->info.tile_flags, false,
+			      (req->info.domain & NOUVEAU_GEM_DOMAIN_MAPPABLE),
+			      &nvbo);
+	if (ret)
+		return ret;
+
+	ret = nouveau_gem_info(nvbo->gem, &req->info);
+	if (ret)
+		goto out;
+
+	ret = drm_gem_handle_create(file_priv, nvbo->gem, &req->info.handle);
+out:
+	mutex_lock(&dev->struct_mutex);
+	drm_gem_object_handle_unreference(nvbo->gem);
+	mutex_unlock(&dev->struct_mutex);
+
+	if (ret)
+		drm_gem_object_unreference(nvbo->gem);
+	return ret;
+}
+
+static int
+nouveau_gem_set_domain(struct drm_gem_object *gem, uint32_t read_domains,
+		       uint32_t write_domains, uint32_t valid_domains)
+{
+	struct nouveau_bo *nvbo = gem->driver_private;
+	struct ttm_buffer_object *bo = &nvbo->bo;
+	uint64_t flags;
+
+	if (!valid_domains || (!read_domains && !write_domains))
+		return -EINVAL;
+
+	if (write_domains) {
+		if ((valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) &&
+		    (write_domains & NOUVEAU_GEM_DOMAIN_VRAM))
+			flags = TTM_PL_FLAG_VRAM;
+		else
+		if ((valid_domains & NOUVEAU_GEM_DOMAIN_GART) &&
+		    (write_domains & NOUVEAU_GEM_DOMAIN_GART))
+			flags = TTM_PL_FLAG_TT;
+		else
+			return -EINVAL;
+	} else {
+		if ((valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) &&
+		    (read_domains & NOUVEAU_GEM_DOMAIN_VRAM) &&
+		    bo->mem.mem_type == TTM_PL_VRAM)
+			flags = TTM_PL_FLAG_VRAM;
+		else
+		if ((valid_domains & NOUVEAU_GEM_DOMAIN_GART) &&
+		    (read_domains & NOUVEAU_GEM_DOMAIN_GART) &&
+		    bo->mem.mem_type == TTM_PL_TT)
+			flags = TTM_PL_FLAG_TT;
+		else
+		if ((valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) &&
+		    (read_domains & NOUVEAU_GEM_DOMAIN_VRAM))
+			flags = TTM_PL_FLAG_VRAM;
+		else
+			flags = TTM_PL_FLAG_TT;
+	}
+
+	nouveau_bo_placement_set(nvbo, flags);
+	return 0;
+}
+
+struct validate_op {
+	struct nouveau_fence *fence;
+	struct list_head vram_list;
+	struct list_head gart_list;
+	struct list_head both_list;
+};
+
+static void
+validate_fini_list(struct list_head *list, struct nouveau_fence *fence)
+{
+	struct list_head *entry, *tmp;
+	struct nouveau_bo *nvbo;
+
+	list_for_each_safe(entry, tmp, list) {
+		nvbo = list_entry(entry, struct nouveau_bo, entry);
+		if (likely(fence)) {
+			struct nouveau_fence *prev_fence;
+
+			spin_lock(&nvbo->bo.lock);
+			prev_fence = nvbo->bo.sync_obj;
+			nvbo->bo.sync_obj = nouveau_fence_ref(fence);
+			spin_unlock(&nvbo->bo.lock);
+			nouveau_fence_unref((void *)&prev_fence);
+		}
+
+		list_del(&nvbo->entry);
+		nvbo->reserved_by = NULL;
+		ttm_bo_unreserve(&nvbo->bo);
+		drm_gem_object_unreference(nvbo->gem);
+	}
+}
+
+static void
+validate_fini(struct validate_op *op, bool success)
+{
+	struct nouveau_fence *fence = op->fence;
+
+	if (unlikely(!success))
+		op->fence = NULL;
+
+	validate_fini_list(&op->vram_list, op->fence);
+	validate_fini_list(&op->gart_list, op->fence);
+	validate_fini_list(&op->both_list, op->fence);
+	nouveau_fence_unref((void *)&fence);
+}
+
+static int
+validate_init(struct nouveau_channel *chan, struct drm_file *file_priv,
+	      struct drm_nouveau_gem_pushbuf_bo *pbbo,
+	      int nr_buffers, struct validate_op *op)
+{
+	struct drm_device *dev = chan->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	uint32_t sequence;
+	int trycnt = 0;
+	int ret, i;
+
+	sequence = atomic_add_return(1, &dev_priv->ttm.validate_sequence);
+retry:
+	if (++trycnt > 100000) {
+		NV_ERROR(dev, "%s failed and gave up.\n", __func__);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < nr_buffers; i++) {
+		struct drm_nouveau_gem_pushbuf_bo *b = &pbbo[i];
+		struct drm_gem_object *gem;
+		struct nouveau_bo *nvbo;
+
+		gem = drm_gem_object_lookup(dev, file_priv, b->handle);
+		if (!gem) {
+			NV_ERROR(dev, "Unknown handle 0x%08x\n", b->handle);
+			validate_fini(op, NULL);
+			return -EINVAL;
+		}
+		nvbo = gem->driver_private;
+
+		if (nvbo->reserved_by && nvbo->reserved_by == file_priv) {
+			NV_ERROR(dev, "multiple instances of buffer %d on "
+				      "validation list\n", b->handle);
+			validate_fini(op, NULL);
+			return -EINVAL;
+		}
+
+		ret = ttm_bo_reserve(&nvbo->bo, false, false, true, sequence);
+		if (ret) {
+			validate_fini(op, NULL);
+			if (ret == -EAGAIN)
+				ret = ttm_bo_wait_unreserved(&nvbo->bo, false);
+			drm_gem_object_unreference(gem);
+			if (ret)
+				return ret;
+			goto retry;
+		}
+
+		nvbo->reserved_by = file_priv;
+		nvbo->pbbo_index = i;
+		if ((b->valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) &&
+		    (b->valid_domains & NOUVEAU_GEM_DOMAIN_GART))
+			list_add_tail(&nvbo->entry, &op->both_list);
+		else
+		if (b->valid_domains & NOUVEAU_GEM_DOMAIN_VRAM)
+			list_add_tail(&nvbo->entry, &op->vram_list);
+		else
+		if (b->valid_domains & NOUVEAU_GEM_DOMAIN_GART)
+			list_add_tail(&nvbo->entry, &op->gart_list);
+		else {
+			NV_ERROR(dev, "invalid valid domains: 0x%08x\n",
+				 b->valid_domains);
+			validate_fini(op, NULL);
+			return -EINVAL;
+		}
+
+		if (unlikely(atomic_read(&nvbo->bo.cpu_writers) > 0)) {
+			validate_fini(op, NULL);
+
+			if (nvbo->cpu_filp == file_priv) {
+				NV_ERROR(dev, "bo %p mapped by process trying "
+					      "to validate it!\n", nvbo);
+				return -EINVAL;
+			}
+
+			ret = ttm_bo_wait_cpu(&nvbo->bo, false);
+			if (ret == -ERESTART)
+				ret = -EAGAIN;
+			if (ret)
+				return ret;
+			goto retry;
+		}
+	}
+
+	return 0;
+}
+
+static int
+validate_list(struct nouveau_channel *chan, struct list_head *list,
+	      struct drm_nouveau_gem_pushbuf_bo *pbbo, uint64_t user_pbbo_ptr)
+{
+	struct drm_nouveau_gem_pushbuf_bo __user *upbbo =
+				(void __force __user *)(uintptr_t)user_pbbo_ptr;
+	struct nouveau_bo *nvbo;
+	int ret, relocs = 0;
+
+	list_for_each_entry(nvbo, list, entry) {
+		struct drm_nouveau_gem_pushbuf_bo *b = &pbbo[nvbo->pbbo_index];
+		struct nouveau_fence *prev_fence = nvbo->bo.sync_obj;
+
+		if (prev_fence && nouveau_fence_channel(prev_fence) != chan) {
+			spin_lock(&nvbo->bo.lock);
+			ret = ttm_bo_wait(&nvbo->bo, false, false, false);
+			spin_unlock(&nvbo->bo.lock);
+			if (unlikely(ret))
+				return ret;
+		}
+
+		ret = nouveau_gem_set_domain(nvbo->gem, b->read_domains,
+					     b->write_domains,
+					     b->valid_domains);
+		if (unlikely(ret))
+			return ret;
+
+		nvbo->channel = chan;
+		ret = ttm_bo_validate(&nvbo->bo, &nvbo->placement,
+				      false, false);
+		nvbo->channel = NULL;
+		if (unlikely(ret))
+			return ret;
+
+		if (nvbo->bo.offset == b->presumed_offset &&
+		    ((nvbo->bo.mem.mem_type == TTM_PL_VRAM &&
+		      b->presumed_domain & NOUVEAU_GEM_DOMAIN_VRAM) ||
+		     (nvbo->bo.mem.mem_type == TTM_PL_TT &&
+		      b->presumed_domain & NOUVEAU_GEM_DOMAIN_GART)))
+			continue;
+
+		if (nvbo->bo.mem.mem_type == TTM_PL_TT)
+			b->presumed_domain = NOUVEAU_GEM_DOMAIN_GART;
+		else
+			b->presumed_domain = NOUVEAU_GEM_DOMAIN_VRAM;
+		b->presumed_offset = nvbo->bo.offset;
+		b->presumed_ok = 0;
+		relocs++;
+
+		if (DRM_COPY_TO_USER(&upbbo[nvbo->pbbo_index], b, sizeof(*b)))
+			return -EFAULT;
+	}
+
+	return relocs;
+}
+
+static int
+nouveau_gem_pushbuf_validate(struct nouveau_channel *chan,
+			     struct drm_file *file_priv,
+			     struct drm_nouveau_gem_pushbuf_bo *pbbo,
+			     uint64_t user_buffers, int nr_buffers,
+			     struct validate_op *op, int *apply_relocs)
+{
+	int ret, relocs = 0;
+
+	INIT_LIST_HEAD(&op->vram_list);
+	INIT_LIST_HEAD(&op->gart_list);
+	INIT_LIST_HEAD(&op->both_list);
+
+	ret = nouveau_fence_new(chan, &op->fence, false);
+	if (ret)
+		return ret;
+
+	if (nr_buffers == 0)
+		return 0;
+
+	ret = validate_init(chan, file_priv, pbbo, nr_buffers, op);
+	if (unlikely(ret))
+		return ret;
+
+	ret = validate_list(chan, &op->vram_list, pbbo, user_buffers);
+	if (unlikely(ret < 0)) {
+		validate_fini(op, NULL);
+		return ret;
+	}
+	relocs += ret;
+
+	ret = validate_list(chan, &op->gart_list, pbbo, user_buffers);
+	if (unlikely(ret < 0)) {
+		validate_fini(op, NULL);
+		return ret;
+	}
+	relocs += ret;
+
+	ret = validate_list(chan, &op->both_list, pbbo, user_buffers);
+	if (unlikely(ret < 0)) {
+		validate_fini(op, NULL);
+		return ret;
+	}
+	relocs += ret;
+
+	*apply_relocs = relocs;
+	return 0;
+}
+
+static inline void *
+u_memcpya(uint64_t user, unsigned nmemb, unsigned size)
+{
+	void *mem;
+	void __user *userptr = (void __force __user *)(uintptr_t)user;
+
+	mem = kmalloc(nmemb * size, GFP_KERNEL);
+	if (!mem)
+		return ERR_PTR(-ENOMEM);
+
+	if (DRM_COPY_FROM_USER(mem, userptr, nmemb * size)) {
+		kfree(mem);
+		return ERR_PTR(-EFAULT);
+	}
+
+	return mem;
+}
+
+static int
+nouveau_gem_pushbuf_reloc_apply(struct nouveau_channel *chan, int nr_bo,
+				struct drm_nouveau_gem_pushbuf_bo *bo,
+				int nr_relocs, uint64_t ptr_relocs,
+				int nr_dwords, int first_dword,
+				uint32_t *pushbuf, bool is_iomem)
+{
+	struct drm_nouveau_gem_pushbuf_reloc *reloc = NULL;
+	struct drm_device *dev = chan->dev;
+	int ret = 0, i;
+
+	reloc = u_memcpya(ptr_relocs, nr_relocs, sizeof(*reloc));
+	if (IS_ERR(reloc))
+		return PTR_ERR(reloc);
+
+	for (i = 0; i < nr_relocs; i++) {
+		struct drm_nouveau_gem_pushbuf_reloc *r = &reloc[i];
+		struct drm_nouveau_gem_pushbuf_bo *b;
+		uint32_t data;
+
+		if (r->bo_index >= nr_bo || r->reloc_index < first_dword ||
+		    r->reloc_index >= first_dword + nr_dwords) {
+			NV_ERROR(dev, "Bad relocation %d\n", i);
+			NV_ERROR(dev, "  bo: %d max %d\n", r->bo_index, nr_bo);
+			NV_ERROR(dev, "  id: %d max %d\n", r->reloc_index, nr_dwords);
+			ret = -EINVAL;
+			break;
+		}
+
+		b = &bo[r->bo_index];
+		if (b->presumed_ok)
+			continue;
+
+		if (r->flags & NOUVEAU_GEM_RELOC_LOW)
+			data = b->presumed_offset + r->data;
+		else
+		if (r->flags & NOUVEAU_GEM_RELOC_HIGH)
+			data = (b->presumed_offset + r->data) >> 32;
+		else
+			data = r->data;
+
+		if (r->flags & NOUVEAU_GEM_RELOC_OR) {
+			if (b->presumed_domain == NOUVEAU_GEM_DOMAIN_GART)
+				data |= r->tor;
+			else
+				data |= r->vor;
+		}
+
+		if (is_iomem)
+			iowrite32_native(data, (void __force __iomem *)
+						&pushbuf[r->reloc_index]);
+		else
+			pushbuf[r->reloc_index] = data;
+	}
+
+	kfree(reloc);
+	return ret;
+}
+
+int
+nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
+			  struct drm_file *file_priv)
+{
+	struct drm_nouveau_gem_pushbuf *req = data;
+	struct drm_nouveau_gem_pushbuf_bo *bo = NULL;
+	struct nouveau_channel *chan;
+	struct validate_op op;
+	uint32_t *pushbuf = NULL;
+	int ret = 0, do_reloc = 0, i;
+
+	NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
+	NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(req->channel, file_priv, chan);
+
+	if (req->nr_dwords >= chan->dma.max ||
+	    req->nr_buffers > NOUVEAU_GEM_MAX_BUFFERS ||
+	    req->nr_relocs > NOUVEAU_GEM_MAX_RELOCS) {
+		NV_ERROR(dev, "Pushbuf config exceeds limits:\n");
+		NV_ERROR(dev, "  dwords : %d max %d\n", req->nr_dwords,
+			 chan->dma.max - 1);
+		NV_ERROR(dev, "  buffers: %d max %d\n", req->nr_buffers,
+			 NOUVEAU_GEM_MAX_BUFFERS);
+		NV_ERROR(dev, "  relocs : %d max %d\n", req->nr_relocs,
+			 NOUVEAU_GEM_MAX_RELOCS);
+		return -EINVAL;
+	}
+
+	pushbuf = u_memcpya(req->dwords, req->nr_dwords, sizeof(uint32_t));
+	if (IS_ERR(pushbuf))
+		return PTR_ERR(pushbuf);
+
+	bo = u_memcpya(req->buffers, req->nr_buffers, sizeof(*bo));
+	if (IS_ERR(bo)) {
+		kfree(pushbuf);
+		return PTR_ERR(bo);
+	}
+
+	mutex_lock(&dev->struct_mutex);
+
+	/* Validate buffer list */
+	ret = nouveau_gem_pushbuf_validate(chan, file_priv, bo, req->buffers,
+					   req->nr_buffers, &op, &do_reloc);
+	if (ret)
+		goto out;
+
+	/* Apply any relocations that are required */
+	if (do_reloc) {
+		ret = nouveau_gem_pushbuf_reloc_apply(chan, req->nr_buffers,
+						      bo, req->nr_relocs,
+						      req->relocs,
+						      req->nr_dwords, 0,
+						      pushbuf, false);
+		if (ret)
+			goto out;
+	}
+
+	/* Emit push buffer to the hw
+	 */
+	ret = RING_SPACE(chan, req->nr_dwords);
+	if (ret)
+		goto out;
+
+	OUT_RINGp(chan, pushbuf, req->nr_dwords);
+
+	ret = nouveau_fence_emit(op.fence);
+	if (ret) {
+		NV_ERROR(dev, "error fencing pushbuf: %d\n", ret);
+		WIND_RING(chan);
+		goto out;
+	}
+
+	if (nouveau_gem_pushbuf_sync(chan)) {
+		ret = nouveau_fence_wait(op.fence, NULL, false, false);
+		if (ret) {
+			for (i = 0; i < req->nr_dwords; i++)
+				NV_ERROR(dev, "0x%08x\n", pushbuf[i]);
+			NV_ERROR(dev, "^^ above push buffer is fail :(\n");
+		}
+	}
+
+out:
+	validate_fini(&op, ret == 0);
+	mutex_unlock(&dev->struct_mutex);
+	kfree(pushbuf);
+	kfree(bo);
+	return ret;
+}
+
+#define PUSHBUF_CAL (dev_priv->card_type >= NV_20)
+
+int
+nouveau_gem_ioctl_pushbuf_call(struct drm_device *dev, void *data,
+			       struct drm_file *file_priv)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct drm_nouveau_gem_pushbuf_call *req = data;
+	struct drm_nouveau_gem_pushbuf_bo *bo = NULL;
+	struct nouveau_channel *chan;
+	struct drm_gem_object *gem;
+	struct nouveau_bo *pbbo;
+	struct validate_op op;
+	int i, ret = 0, do_reloc = 0;
+
+	NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
+	NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(req->channel, file_priv, chan);
+
+	if (unlikely(req->handle == 0))
+		goto out_next;
+
+	if (req->nr_buffers > NOUVEAU_GEM_MAX_BUFFERS ||
+	    req->nr_relocs > NOUVEAU_GEM_MAX_RELOCS) {
+		NV_ERROR(dev, "Pushbuf config exceeds limits:\n");
+		NV_ERROR(dev, "  buffers: %d max %d\n", req->nr_buffers,
+			 NOUVEAU_GEM_MAX_BUFFERS);
+		NV_ERROR(dev, "  relocs : %d max %d\n", req->nr_relocs,
+			 NOUVEAU_GEM_MAX_RELOCS);
+		return -EINVAL;
+	}
+
+	bo = u_memcpya(req->buffers, req->nr_buffers, sizeof(*bo));
+	if (IS_ERR(bo))
+		return PTR_ERR(bo);
+
+	mutex_lock(&dev->struct_mutex);
+
+	/* Validate buffer list */
+	ret = nouveau_gem_pushbuf_validate(chan, file_priv, bo, req->buffers,
+					   req->nr_buffers, &op, &do_reloc);
+	if (ret) {
+		NV_ERROR(dev, "validate: %d\n", ret);
+		goto out;
+	}
+
+	/* Validate DMA push buffer */
+	gem = drm_gem_object_lookup(dev, file_priv, req->handle);
+	if (!gem) {
+		NV_ERROR(dev, "Unknown pb handle 0x%08x\n", req->handle);
+		ret = -EINVAL;
+		goto out;
+	}
+	pbbo = nouveau_gem_object(gem);
+
+	ret = ttm_bo_reserve(&pbbo->bo, false, false, true,
+			     chan->fence.sequence);
+	if (ret) {
+		NV_ERROR(dev, "resv pb: %d\n", ret);
+		drm_gem_object_unreference(gem);
+		goto out;
+	}
+
+	nouveau_bo_placement_set(pbbo, 1 << chan->pushbuf_bo->bo.mem.mem_type);
+	ret = ttm_bo_validate(&pbbo->bo, &pbbo->placement, false, false);
+	if (ret) {
+		NV_ERROR(dev, "validate pb: %d\n", ret);
+		ttm_bo_unreserve(&pbbo->bo);
+		drm_gem_object_unreference(gem);
+		goto out;
+	}
+
+	list_add_tail(&pbbo->entry, &op.both_list);
+
+	/* If presumed return address doesn't match, we need to map the
+	 * push buffer and fix it..
+	 */
+	if (!PUSHBUF_CAL) {
+		uint32_t retaddy;
+
+		if (chan->dma.free < 4 + NOUVEAU_DMA_SKIPS) {
+			ret = nouveau_dma_wait(chan, 4 + NOUVEAU_DMA_SKIPS);
+			if (ret) {
+				NV_ERROR(dev, "jmp_space: %d\n", ret);
+				goto out;
+			}
+		}
+
+		retaddy  = chan->pushbuf_base + ((chan->dma.cur + 2) << 2);
+		retaddy |= 0x20000000;
+		if (retaddy != req->suffix0) {
+			req->suffix0 = retaddy;
+			do_reloc = 1;
+		}
+	}
+
+	/* Apply any relocations that are required */
+	if (do_reloc) {
+		void *pbvirt;
+		bool is_iomem;
+		ret = ttm_bo_kmap(&pbbo->bo, 0, pbbo->bo.mem.num_pages,
+				  &pbbo->kmap);
+		if (ret) {
+			NV_ERROR(dev, "kmap pb: %d\n", ret);
+			goto out;
+		}
+
+		pbvirt = ttm_kmap_obj_virtual(&pbbo->kmap, &is_iomem);
+		ret = nouveau_gem_pushbuf_reloc_apply(chan, req->nr_buffers, bo,
+						      req->nr_relocs,
+						      req->relocs,
+						      req->nr_dwords,
+						      req->offset / 4,
+						      pbvirt, is_iomem);
+
+		if (!PUSHBUF_CAL) {
+			nouveau_bo_wr32(pbbo,
+					req->offset / 4 + req->nr_dwords - 2,
+					req->suffix0);
+		}
+
+		ttm_bo_kunmap(&pbbo->kmap);
+		if (ret) {
+			NV_ERROR(dev, "reloc apply: %d\n", ret);
+			goto out;
+		}
+	}
+
+	if (PUSHBUF_CAL) {
+		ret = RING_SPACE(chan, 2);
+		if (ret) {
+			NV_ERROR(dev, "cal_space: %d\n", ret);
+			goto out;
+		}
+		OUT_RING(chan, ((pbbo->bo.mem.mm_node->start << PAGE_SHIFT) +
+				  req->offset) | 2);
+		OUT_RING(chan, 0);
+	} else {
+		ret = RING_SPACE(chan, 2 + NOUVEAU_DMA_SKIPS);
+		if (ret) {
+			NV_ERROR(dev, "jmp_space: %d\n", ret);
+			goto out;
+		}
+		OUT_RING(chan, ((pbbo->bo.mem.mm_node->start << PAGE_SHIFT) +
+				  req->offset) | 0x20000000);
+		OUT_RING(chan, 0);
+
+		/* Space the jumps apart with NOPs. */
+		for (i = 0; i < NOUVEAU_DMA_SKIPS; i++)
+			OUT_RING(chan, 0);
+	}
+
+	ret = nouveau_fence_emit(op.fence);
+	if (ret) {
+		NV_ERROR(dev, "error fencing pushbuf: %d\n", ret);
+		WIND_RING(chan);
+		goto out;
+	}
+
+out:
+	validate_fini(&op, ret == 0);
+	mutex_unlock(&dev->struct_mutex);
+	kfree(bo);
+
+out_next:
+	if (PUSHBUF_CAL) {
+		req->suffix0 = 0x00020000;
+		req->suffix1 = 0x00000000;
+	} else {
+		req->suffix0 = 0x20000000 |
+			      (chan->pushbuf_base + ((chan->dma.cur + 2) << 2));
+		req->suffix1 = 0x00000000;
+	}
+
+	return ret;
+}
+
+int
+nouveau_gem_ioctl_pushbuf_call2(struct drm_device *dev, void *data,
+				struct drm_file *file_priv)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct drm_nouveau_gem_pushbuf_call *req = data;
+
+	req->vram_available = dev_priv->fb_aper_free;
+	req->gart_available = dev_priv->gart_info.aper_free;
+
+	return nouveau_gem_ioctl_pushbuf_call(dev, data, file_priv);
+}
+
+static inline uint32_t
+domain_to_ttm(struct nouveau_bo *nvbo, uint32_t domain)
+{
+	uint32_t flags = 0;
+
+	if (domain & NOUVEAU_GEM_DOMAIN_VRAM)
+		flags |= TTM_PL_FLAG_VRAM;
+	if (domain & NOUVEAU_GEM_DOMAIN_GART)
+		flags |= TTM_PL_FLAG_TT;
+
+	return flags;
+}
+
+int
+nouveau_gem_ioctl_pin(struct drm_device *dev, void *data,
+		      struct drm_file *file_priv)
+{
+	struct drm_nouveau_gem_pin *req = data;
+	struct drm_gem_object *gem;
+	struct nouveau_bo *nvbo;
+	int ret = 0;
+
+	NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
+
+	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+		NV_ERROR(dev, "pin only allowed without kernel modesetting\n");
+		return -EINVAL;
+	}
+
+	if (!DRM_SUSER(DRM_CURPROC))
+		return -EPERM;
+
+	gem = drm_gem_object_lookup(dev, file_priv, req->handle);
+	if (!gem)
+		return -EINVAL;
+	nvbo = nouveau_gem_object(gem);
+
+	ret = nouveau_bo_pin(nvbo, domain_to_ttm(nvbo, req->domain));
+	if (ret)
+		goto out;
+
+	req->offset = nvbo->bo.offset;
+	if (nvbo->bo.mem.mem_type == TTM_PL_TT)
+		req->domain = NOUVEAU_GEM_DOMAIN_GART;
+	else
+		req->domain = NOUVEAU_GEM_DOMAIN_VRAM;
+
+out:
+	mutex_lock(&dev->struct_mutex);
+	drm_gem_object_unreference(gem);
+	mutex_unlock(&dev->struct_mutex);
+
+	return ret;
+}
+
+int
+nouveau_gem_ioctl_unpin(struct drm_device *dev, void *data,
+			struct drm_file *file_priv)
+{
+	struct drm_nouveau_gem_pin *req = data;
+	struct drm_gem_object *gem;
+	int ret;
+
+	NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
+
+	if (drm_core_check_feature(dev, DRIVER_MODESET))
+		return -EINVAL;
+
+	gem = drm_gem_object_lookup(dev, file_priv, req->handle);
+	if (!gem)
+		return -EINVAL;
+
+	ret = nouveau_bo_unpin(nouveau_gem_object(gem));
+
+	mutex_lock(&dev->struct_mutex);
+	drm_gem_object_unreference(gem);
+	mutex_unlock(&dev->struct_mutex);
+
+	return ret;
+}
+
+int
+nouveau_gem_ioctl_cpu_prep(struct drm_device *dev, void *data,
+			   struct drm_file *file_priv)
+{
+	struct drm_nouveau_gem_cpu_prep *req = data;
+	struct drm_gem_object *gem;
+	struct nouveau_bo *nvbo;
+	bool no_wait = !!(req->flags & NOUVEAU_GEM_CPU_PREP_NOWAIT);
+	int ret = -EINVAL;
+
+	NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
+
+	gem = drm_gem_object_lookup(dev, file_priv, req->handle);
+	if (!gem)
+		return ret;
+	nvbo = nouveau_gem_object(gem);
+
+	if (nvbo->cpu_filp) {
+		if (nvbo->cpu_filp == file_priv)
+			goto out;
+
+		ret = ttm_bo_wait_cpu(&nvbo->bo, no_wait);
+		if (ret == -ERESTART)
+			ret = -EAGAIN;
+		if (ret)
+			goto out;
+	}
+
+	if (req->flags & NOUVEAU_GEM_CPU_PREP_NOBLOCK) {
+		ret = ttm_bo_wait(&nvbo->bo, false, false, no_wait);
+	} else {
+		ret = ttm_bo_synccpu_write_grab(&nvbo->bo, no_wait);
+		if (ret == -ERESTART)
+			ret = -EAGAIN;
+		else
+		if (ret == 0)
+			nvbo->cpu_filp = file_priv;
+	}
+
+out:
+	mutex_lock(&dev->struct_mutex);
+	drm_gem_object_unreference(gem);
+	mutex_unlock(&dev->struct_mutex);
+	return ret;
+}
+
+int
+nouveau_gem_ioctl_cpu_fini(struct drm_device *dev, void *data,
+			   struct drm_file *file_priv)
+{
+	struct drm_nouveau_gem_cpu_prep *req = data;
+	struct drm_gem_object *gem;
+	struct nouveau_bo *nvbo;
+	int ret = -EINVAL;
+
+	NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
+
+	gem = drm_gem_object_lookup(dev, file_priv, req->handle);
+	if (!gem)
+		return ret;
+	nvbo = nouveau_gem_object(gem);
+
+	if (nvbo->cpu_filp != file_priv)
+		goto out;
+	nvbo->cpu_filp = NULL;
+
+	ttm_bo_synccpu_write_release(&nvbo->bo);
+	ret = 0;
+
+out:
+	mutex_lock(&dev->struct_mutex);
+	drm_gem_object_unreference(gem);
+	mutex_unlock(&dev->struct_mutex);
+	return ret;
+}
+
+int
+nouveau_gem_ioctl_info(struct drm_device *dev, void *data,
+		       struct drm_file *file_priv)
+{
+	struct drm_nouveau_gem_info *req = data;
+	struct drm_gem_object *gem;
+	int ret;
+
+	NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
+
+	gem = drm_gem_object_lookup(dev, file_priv, req->handle);
+	if (!gem)
+		return -EINVAL;
+
+	ret = nouveau_gem_info(gem, req);
+	mutex_lock(&dev->struct_mutex);
+	drm_gem_object_unreference(gem);
+	mutex_unlock(&dev->struct_mutex);
+	return ret;
+}
+
diff --git a/drivers/gpu/drm/nouveau/nouveau_hw.c b/drivers/gpu/drm/nouveau/nouveau_hw.c
new file mode 100644
index 0000000..dc46792
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_hw.c
@@ -0,0 +1,1080 @@
+/*
+ * Copyright 2006 Dave Airlie
+ * Copyright 2007 Maarten Maathuis
+ * Copyright 2007-2009 Stuart Bennett
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "drmP.h"
+#include "nouveau_drv.h"
+#include "nouveau_hw.h"
+
+#define CHIPSET_NFORCE 0x01a0
+#define CHIPSET_NFORCE2 0x01f0
+
+/*
+ * misc hw access wrappers/control functions
+ */
+
+void
+NVWriteVgaSeq(struct drm_device *dev, int head, uint8_t index, uint8_t value)
+{
+	NVWritePRMVIO(dev, head, NV_PRMVIO_SRX, index);
+	NVWritePRMVIO(dev, head, NV_PRMVIO_SR, value);
+}
+
+uint8_t
+NVReadVgaSeq(struct drm_device *dev, int head, uint8_t index)
+{
+	NVWritePRMVIO(dev, head, NV_PRMVIO_SRX, index);
+	return NVReadPRMVIO(dev, head, NV_PRMVIO_SR);
+}
+
+void
+NVWriteVgaGr(struct drm_device *dev, int head, uint8_t index, uint8_t value)
+{
+	NVWritePRMVIO(dev, head, NV_PRMVIO_GRX, index);
+	NVWritePRMVIO(dev, head, NV_PRMVIO_GX, value);
+}
+
+uint8_t
+NVReadVgaGr(struct drm_device *dev, int head, uint8_t index)
+{
+	NVWritePRMVIO(dev, head, NV_PRMVIO_GRX, index);
+	return NVReadPRMVIO(dev, head, NV_PRMVIO_GX);
+}
+
+/* CR44 takes values 0 (head A), 3 (head B) and 4 (heads tied)
+ * it affects only the 8 bit vga io regs, which we access using mmio at
+ * 0xc{0,2}3c*, 0x60{1,3}3*, and 0x68{1,3}3d*
+ * in general, the set value of cr44 does not matter: reg access works as
+ * expected and values can be set for the appropriate head by using a 0x2000
+ * offset as required
+ * however:
+ * a) pre nv40, the head B range of PRMVIO regs at 0xc23c* was not exposed and
+ *    cr44 must be set to 0 or 3 for accessing values on the correct head
+ *    through the common 0xc03c* addresses
+ * b) in tied mode (4) head B is programmed to the values set on head A, and
+ *    access using the head B addresses can have strange results, ergo we leave
+ *    tied mode in init once we know to what cr44 should be restored on exit
+ *
+ * the owner parameter is slightly abused:
+ * 0 and 1 are treated as head values and so the set value is (owner * 3)
+ * other values are treated as literal values to set
+ */
+void
+NVSetOwner(struct drm_device *dev, int owner)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	if (owner == 1)
+		owner *= 3;
+
+	if (dev_priv->chipset == 0x11) {
+		/* This might seem stupid, but the blob does it and
+		 * omitting it often locks the system up.
+		 */
+		NVReadVgaCrtc(dev, 0, NV_CIO_SR_LOCK_INDEX);
+		NVReadVgaCrtc(dev, 1, NV_CIO_SR_LOCK_INDEX);
+	}
+
+	/* CR44 is always changed on CRTC0 */
+	NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_44, owner);
+
+	if (dev_priv->chipset == 0x11) {	/* set me harder */
+		NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_2E, owner);
+		NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_2E, owner);
+	}
+}
+
+void
+NVBlankScreen(struct drm_device *dev, int head, bool blank)
+{
+	unsigned char seq1;
+
+	if (nv_two_heads(dev))
+		NVSetOwner(dev, head);
+
+	seq1 = NVReadVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX);
+
+	NVVgaSeqReset(dev, head, true);
+	if (blank)
+		NVWriteVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX, seq1 | 0x20);
+	else
+		NVWriteVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX, seq1 & ~0x20);
+	NVVgaSeqReset(dev, head, false);
+}
+
+/*
+ * PLL setting
+ */
+
+static int
+powerctrl_1_shift(int chip_version, int reg)
+{
+	int shift = -4;
+
+	if (chip_version < 0x17 || chip_version == 0x1a || chip_version == 0x20)
+		return shift;
+
+	switch (reg) {
+	case NV_RAMDAC_VPLL2:
+		shift += 4;
+	case NV_PRAMDAC_VPLL_COEFF:
+		shift += 4;
+	case NV_PRAMDAC_MPLL_COEFF:
+		shift += 4;
+	case NV_PRAMDAC_NVPLL_COEFF:
+		shift += 4;
+	}
+
+	/*
+	 * the shift for vpll regs is only used for nv3x chips with a single
+	 * stage pll
+	 */
+	if (shift > 4 && (chip_version < 0x32 || chip_version == 0x35 ||
+			  chip_version == 0x36 || chip_version >= 0x40))
+		shift = -4;
+
+	return shift;
+}
+
+static void
+setPLL_single(struct drm_device *dev, uint32_t reg, struct nouveau_pll_vals *pv)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	int chip_version = dev_priv->vbios->chip_version;
+	uint32_t oldpll = NVReadRAMDAC(dev, 0, reg);
+	int oldN = (oldpll >> 8) & 0xff, oldM = oldpll & 0xff;
+	uint32_t pll = (oldpll & 0xfff80000) | pv->log2P << 16 | pv->NM1;
+	uint32_t saved_powerctrl_1 = 0;
+	int shift_powerctrl_1 = powerctrl_1_shift(chip_version, reg);
+
+	if (oldpll == pll)
+		return;	/* already set */
+
+	if (shift_powerctrl_1 >= 0) {
+		saved_powerctrl_1 = nvReadMC(dev, NV_PBUS_POWERCTRL_1);
+		nvWriteMC(dev, NV_PBUS_POWERCTRL_1,
+			(saved_powerctrl_1 & ~(0xf << shift_powerctrl_1)) |
+			1 << shift_powerctrl_1);
+	}
+
+	if (oldM && pv->M1 && (oldN / oldM < pv->N1 / pv->M1))
+		/* upclock -- write new post divider first */
+		NVWriteRAMDAC(dev, 0, reg, pv->log2P << 16 | (oldpll & 0xffff));
+	else
+		/* downclock -- write new NM first */
+		NVWriteRAMDAC(dev, 0, reg, (oldpll & 0xffff0000) | pv->NM1);
+
+	if (chip_version < 0x17 && chip_version != 0x11)
+		/* wait a bit on older chips */
+		msleep(64);
+	NVReadRAMDAC(dev, 0, reg);
+
+	/* then write the other half as well */
+	NVWriteRAMDAC(dev, 0, reg, pll);
+
+	if (shift_powerctrl_1 >= 0)
+		nvWriteMC(dev, NV_PBUS_POWERCTRL_1, saved_powerctrl_1);
+}
+
+static uint32_t
+new_ramdac580(uint32_t reg1, bool ss, uint32_t ramdac580)
+{
+	bool head_a = (reg1 == NV_PRAMDAC_VPLL_COEFF);
+
+	if (ss)	/* single stage pll mode */
+		ramdac580 |= head_a ? NV_RAMDAC_580_VPLL1_ACTIVE :
+				      NV_RAMDAC_580_VPLL2_ACTIVE;
+	else
+		ramdac580 &= head_a ? ~NV_RAMDAC_580_VPLL1_ACTIVE :
+				      ~NV_RAMDAC_580_VPLL2_ACTIVE;
+
+	return ramdac580;
+}
+
+static void
+setPLL_double_highregs(struct drm_device *dev, uint32_t reg1,
+		       struct nouveau_pll_vals *pv)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	int chip_version = dev_priv->vbios->chip_version;
+	bool nv3035 = chip_version == 0x30 || chip_version == 0x35;
+	uint32_t reg2 = reg1 + ((reg1 == NV_RAMDAC_VPLL2) ? 0x5c : 0x70);
+	uint32_t oldpll1 = NVReadRAMDAC(dev, 0, reg1);
+	uint32_t oldpll2 = !nv3035 ? NVReadRAMDAC(dev, 0, reg2) : 0;
+	uint32_t pll1 = (oldpll1 & 0xfff80000) | pv->log2P << 16 | pv->NM1;
+	uint32_t pll2 = (oldpll2 & 0x7fff0000) | 1 << 31 | pv->NM2;
+	uint32_t oldramdac580 = 0, ramdac580 = 0;
+	bool single_stage = !pv->NM2 || pv->N2 == pv->M2;	/* nv41+ only */
+	uint32_t saved_powerctrl_1 = 0, savedc040 = 0;
+	int shift_powerctrl_1 = powerctrl_1_shift(chip_version, reg1);
+
+	/* model specific additions to generic pll1 and pll2 set up above */
+	if (nv3035) {
+		pll1 = (pll1 & 0xfcc7ffff) | (pv->N2 & 0x18) << 21 |
+		       (pv->N2 & 0x7) << 19 | 8 << 4 | (pv->M2 & 7) << 4;
+		pll2 = 0;
+	}
+	if (chip_version > 0x40 && reg1 >= NV_PRAMDAC_VPLL_COEFF) { /* !nv40 */
+		oldramdac580 = NVReadRAMDAC(dev, 0, NV_PRAMDAC_580);
+		ramdac580 = new_ramdac580(reg1, single_stage, oldramdac580);
+		if (oldramdac580 != ramdac580)
+			oldpll1 = ~0;	/* force mismatch */
+		if (single_stage)
+			/* magic value used by nvidia in single stage mode */
+			pll2 |= 0x011f;
+	}
+	if (chip_version > 0x70)
+		/* magic bits set by the blob (but not the bios) on g71-73 */
+		pll1 = (pll1 & 0x7fffffff) | (single_stage ? 0x4 : 0xc) << 28;
+
+	if (oldpll1 == pll1 && oldpll2 == pll2)
+		return;	/* already set */
+
+	if (shift_powerctrl_1 >= 0) {
+		saved_powerctrl_1 = nvReadMC(dev, NV_PBUS_POWERCTRL_1);
+		nvWriteMC(dev, NV_PBUS_POWERCTRL_1,
+			(saved_powerctrl_1 & ~(0xf << shift_powerctrl_1)) |
+			1 << shift_powerctrl_1);
+	}
+
+	if (chip_version >= 0x40) {
+		int shift_c040 = 14;
+
+		switch (reg1) {
+		case NV_PRAMDAC_MPLL_COEFF:
+			shift_c040 += 2;
+		case NV_PRAMDAC_NVPLL_COEFF:
+			shift_c040 += 2;
+		case NV_RAMDAC_VPLL2:
+			shift_c040 += 2;
+		case NV_PRAMDAC_VPLL_COEFF:
+			shift_c040 += 2;
+		}
+
+		savedc040 = nvReadMC(dev, 0xc040);
+		if (shift_c040 != 14)
+			nvWriteMC(dev, 0xc040, savedc040 & ~(3 << shift_c040));
+	}
+
+	if (oldramdac580 != ramdac580)
+		NVWriteRAMDAC(dev, 0, NV_PRAMDAC_580, ramdac580);
+
+	if (!nv3035)
+		NVWriteRAMDAC(dev, 0, reg2, pll2);
+	NVWriteRAMDAC(dev, 0, reg1, pll1);
+
+	if (shift_powerctrl_1 >= 0)
+		nvWriteMC(dev, NV_PBUS_POWERCTRL_1, saved_powerctrl_1);
+	if (chip_version >= 0x40)
+		nvWriteMC(dev, 0xc040, savedc040);
+}
+
+static void
+setPLL_double_lowregs(struct drm_device *dev, uint32_t NMNMreg,
+		      struct nouveau_pll_vals *pv)
+{
+	/* When setting PLLs, there is a merry game of disabling and enabling
+	 * various bits of hardware during the process. This function is a
+	 * synthesis of six nv4x traces, nearly each card doing a subtly
+	 * different thing. With luck all the necessary bits for each card are
+	 * combined herein. Without luck it deviates from each card's formula
+	 * so as to not work on any :)
+	 */
+
+	uint32_t Preg = NMNMreg - 4;
+	bool mpll = Preg == 0x4020;
+	uint32_t oldPval = nvReadMC(dev, Preg);
+	uint32_t NMNM = pv->NM2 << 16 | pv->NM1;
+	uint32_t Pval = (oldPval & (mpll ? ~(0x11 << 16) : ~(1 << 16))) |
+			0xc << 28 | pv->log2P << 16;
+	uint32_t saved4600 = 0;
+	/* some cards have different maskc040s */
+	uint32_t maskc040 = ~(3 << 14), savedc040;
+	bool single_stage = !pv->NM2 || pv->N2 == pv->M2;
+
+	if (nvReadMC(dev, NMNMreg) == NMNM && (oldPval & 0xc0070000) == Pval)
+		return;
+
+	if (Preg == 0x4000)
+		maskc040 = ~0x333;
+	if (Preg == 0x4058)
+		maskc040 = ~(0xc << 24);
+
+	if (mpll) {
+		struct pll_lims pll_lim;
+		uint8_t Pval2;
+
+		if (get_pll_limits(dev, Preg, &pll_lim))
+			return;
+
+		Pval2 = pv->log2P + pll_lim.log2p_bias;
+		if (Pval2 > pll_lim.max_log2p)
+			Pval2 = pll_lim.max_log2p;
+		Pval |= 1 << 28 | Pval2 << 20;
+
+		saved4600 = nvReadMC(dev, 0x4600);
+		nvWriteMC(dev, 0x4600, saved4600 | 8 << 28);
+	}
+	if (single_stage)
+		Pval |= mpll ? 1 << 12 : 1 << 8;
+
+	nvWriteMC(dev, Preg, oldPval | 1 << 28);
+	nvWriteMC(dev, Preg, Pval & ~(4 << 28));
+	if (mpll) {
+		Pval |= 8 << 20;
+		nvWriteMC(dev, 0x4020, Pval & ~(0xc << 28));
+		nvWriteMC(dev, 0x4038, Pval & ~(0xc << 28));
+	}
+
+	savedc040 = nvReadMC(dev, 0xc040);
+	nvWriteMC(dev, 0xc040, savedc040 & maskc040);
+
+	nvWriteMC(dev, NMNMreg, NMNM);
+	if (NMNMreg == 0x4024)
+		nvWriteMC(dev, 0x403c, NMNM);
+
+	nvWriteMC(dev, Preg, Pval);
+	if (mpll) {
+		Pval &= ~(8 << 20);
+		nvWriteMC(dev, 0x4020, Pval);
+		nvWriteMC(dev, 0x4038, Pval);
+		nvWriteMC(dev, 0x4600, saved4600);
+	}
+
+	nvWriteMC(dev, 0xc040, savedc040);
+
+	if (mpll) {
+		nvWriteMC(dev, 0x4020, Pval & ~(1 << 28));
+		nvWriteMC(dev, 0x4038, Pval & ~(1 << 28));
+	}
+}
+
+void
+nouveau_hw_setpll(struct drm_device *dev, uint32_t reg1,
+		  struct nouveau_pll_vals *pv)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	int cv = dev_priv->vbios->chip_version;
+
+	if (cv == 0x30 || cv == 0x31 || cv == 0x35 || cv == 0x36 ||
+	    cv >= 0x40) {
+		if (reg1 > 0x405c)
+			setPLL_double_highregs(dev, reg1, pv);
+		else
+			setPLL_double_lowregs(dev, reg1, pv);
+	} else
+		setPLL_single(dev, reg1, pv);
+}
+
+/*
+ * PLL getting
+ */
+
+static void
+nouveau_hw_decode_pll(struct drm_device *dev, uint32_t reg1, uint32_t pll1,
+		      uint32_t pll2, struct nouveau_pll_vals *pllvals)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	/* to force parsing as single stage (i.e. nv40 vplls) pass pll2 as 0 */
+
+	/* log2P is & 0x7 as never more than 7, and nv30/35 only uses 3 bits */
+	pllvals->log2P = (pll1 >> 16) & 0x7;
+	pllvals->N2 = pllvals->M2 = 1;
+
+	if (reg1 <= 0x405c) {
+		pllvals->NM1 = pll2 & 0xffff;
+		/* single stage NVPLL and VPLLs use 1 << 8, MPLL uses 1 << 12 */
+		if (!(pll1 & 0x1100))
+			pllvals->NM2 = pll2 >> 16;
+	} else {
+		pllvals->NM1 = pll1 & 0xffff;
+		if (nv_two_reg_pll(dev) && pll2 & NV31_RAMDAC_ENABLE_VCO2)
+			pllvals->NM2 = pll2 & 0xffff;
+		else if (dev_priv->chipset == 0x30 || dev_priv->chipset == 0x35) {
+			pllvals->M1 &= 0xf; /* only 4 bits */
+			if (pll1 & NV30_RAMDAC_ENABLE_VCO2) {
+				pllvals->M2 = (pll1 >> 4) & 0x7;
+				pllvals->N2 = ((pll1 >> 21) & 0x18) |
+					      ((pll1 >> 19) & 0x7);
+			}
+		}
+	}
+}
+
+int
+nouveau_hw_get_pllvals(struct drm_device *dev, enum pll_types plltype,
+		       struct nouveau_pll_vals *pllvals)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	const uint32_t nv04_regs[MAX_PLL_TYPES] = { NV_PRAMDAC_NVPLL_COEFF,
+						    NV_PRAMDAC_MPLL_COEFF,
+						    NV_PRAMDAC_VPLL_COEFF,
+						    NV_RAMDAC_VPLL2 };
+	const uint32_t nv40_regs[MAX_PLL_TYPES] = { 0x4000,
+						    0x4020,
+						    NV_PRAMDAC_VPLL_COEFF,
+						    NV_RAMDAC_VPLL2 };
+	uint32_t reg1, pll1, pll2 = 0;
+	struct pll_lims pll_lim;
+	int ret;
+
+	if (dev_priv->card_type < NV_40)
+		reg1 = nv04_regs[plltype];
+	else
+		reg1 = nv40_regs[plltype];
+
+	pll1 = nvReadMC(dev, reg1);
+
+	if (reg1 <= 0x405c)
+		pll2 = nvReadMC(dev, reg1 + 4);
+	else if (nv_two_reg_pll(dev)) {
+		uint32_t reg2 = reg1 + (reg1 == NV_RAMDAC_VPLL2 ? 0x5c : 0x70);
+
+		pll2 = nvReadMC(dev, reg2);
+	}
+
+	if (dev_priv->card_type == 0x40 && reg1 >= NV_PRAMDAC_VPLL_COEFF) {
+		uint32_t ramdac580 = NVReadRAMDAC(dev, 0, NV_PRAMDAC_580);
+
+		/* check whether vpll has been forced into single stage mode */
+		if (reg1 == NV_PRAMDAC_VPLL_COEFF) {
+			if (ramdac580 & NV_RAMDAC_580_VPLL1_ACTIVE)
+				pll2 = 0;
+		} else
+			if (ramdac580 & NV_RAMDAC_580_VPLL2_ACTIVE)
+				pll2 = 0;
+	}
+
+	nouveau_hw_decode_pll(dev, reg1, pll1, pll2, pllvals);
+
+	ret = get_pll_limits(dev, plltype, &pll_lim);
+	if (ret)
+		return ret;
+
+	pllvals->refclk = pll_lim.refclk;
+
+	return 0;
+}
+
+int
+nouveau_hw_pllvals_to_clk(struct nouveau_pll_vals *pv)
+{
+	/* Avoid divide by zero if called at an inappropriate time */
+	if (!pv->M1 || !pv->M2)
+		return 0;
+
+	return pv->N1 * pv->N2 * pv->refclk / (pv->M1 * pv->M2) >> pv->log2P;
+}
+
+int
+nouveau_hw_get_clock(struct drm_device *dev, enum pll_types plltype)
+{
+	struct nouveau_pll_vals pllvals;
+
+	if (plltype == MPLL && (dev->pci_device & 0x0ff0) == CHIPSET_NFORCE) {
+		uint32_t mpllP;
+
+		pci_read_config_dword(pci_get_bus_and_slot(0, 3), 0x6c, &mpllP);
+		if (!mpllP)
+			mpllP = 4;
+
+		return 400000 / mpllP;
+	} else
+	if (plltype == MPLL && (dev->pci_device & 0xff0) == CHIPSET_NFORCE2) {
+		uint32_t clock;
+
+		pci_read_config_dword(pci_get_bus_and_slot(0, 5), 0x4c, &clock);
+		return clock;
+	}
+
+	nouveau_hw_get_pllvals(dev, plltype, &pllvals);
+
+	return nouveau_hw_pllvals_to_clk(&pllvals);
+}
+
+static void
+nouveau_hw_fix_bad_vpll(struct drm_device *dev, int head)
+{
+	/* the vpll on an unused head can come up with a random value, way
+	 * beyond the pll limits.  for some reason this causes the chip to
+	 * lock up when reading the dac palette regs, so set a valid pll here
+	 * when such a condition detected.  only seen on nv11 to date
+	 */
+
+	struct pll_lims pll_lim;
+	struct nouveau_pll_vals pv;
+	uint32_t pllreg = head ? NV_RAMDAC_VPLL2 : NV_PRAMDAC_VPLL_COEFF;
+
+	if (get_pll_limits(dev, head ? VPLL2 : VPLL1, &pll_lim))
+		return;
+	nouveau_hw_get_pllvals(dev, head ? VPLL2 : VPLL1, &pv);
+
+	if (pv.M1 >= pll_lim.vco1.min_m && pv.M1 <= pll_lim.vco1.max_m &&
+	    pv.N1 >= pll_lim.vco1.min_n && pv.N1 <= pll_lim.vco1.max_n &&
+	    pv.log2P <= pll_lim.max_log2p)
+		return;
+
+	NV_WARN(dev, "VPLL %d outwith limits, attempting to fix\n", head + 1);
+
+	/* set lowest clock within static limits */
+	pv.M1 = pll_lim.vco1.max_m;
+	pv.N1 = pll_lim.vco1.min_n;
+	pv.log2P = pll_lim.max_usable_log2p;
+	nouveau_hw_setpll(dev, pllreg, &pv);
+}
+
+/*
+ * vga font save/restore
+ */
+
+static void nouveau_vga_font_io(struct drm_device *dev,
+				void __iomem *iovram,
+				bool save, unsigned plane)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	unsigned i;
+
+	NVWriteVgaSeq(dev, 0, NV_VIO_SR_PLANE_MASK_INDEX, 1 << plane);
+	NVWriteVgaGr(dev, 0, NV_VIO_GX_READ_MAP_INDEX, plane);
+	for (i = 0; i < 16384; i++) {
+		if (save) {
+			dev_priv->saved_vga_font[plane][i] =
+					ioread32_native(iovram + i * 4);
+		} else {
+			iowrite32_native(dev_priv->saved_vga_font[plane][i],
+							iovram + i * 4);
+		}
+	}
+}
+
+void
+nouveau_hw_save_vga_fonts(struct drm_device *dev, bool save)
+{
+	uint8_t misc, gr4, gr5, gr6, seq2, seq4;
+	bool graphicsmode;
+	unsigned plane;
+	void __iomem *iovram;
+
+	if (nv_two_heads(dev))
+		NVSetOwner(dev, 0);
+
+	NVSetEnablePalette(dev, 0, true);
+	graphicsmode = NVReadVgaAttr(dev, 0, NV_CIO_AR_MODE_INDEX) & 1;
+	NVSetEnablePalette(dev, 0, false);
+
+	if (graphicsmode) /* graphics mode => framebuffer => no need to save */
+		return;
+
+	NV_INFO(dev, "%sing VGA fonts\n", save ? "Sav" : "Restor");
+
+	/* map first 64KiB of VRAM, holds VGA fonts etc */
+	iovram = ioremap(pci_resource_start(dev->pdev, 1), 65536);
+	if (!iovram) {
+		NV_ERROR(dev, "Failed to map VRAM, "
+					"cannot save/restore VGA fonts.\n");
+		return;
+	}
+
+	if (nv_two_heads(dev))
+		NVBlankScreen(dev, 1, true);
+	NVBlankScreen(dev, 0, true);
+
+	/* save control regs */
+	misc = NVReadPRMVIO(dev, 0, NV_PRMVIO_MISC__READ);
+	seq2 = NVReadVgaSeq(dev, 0, NV_VIO_SR_PLANE_MASK_INDEX);
+	seq4 = NVReadVgaSeq(dev, 0, NV_VIO_SR_MEM_MODE_INDEX);
+	gr4 = NVReadVgaGr(dev, 0, NV_VIO_GX_READ_MAP_INDEX);
+	gr5 = NVReadVgaGr(dev, 0, NV_VIO_GX_MODE_INDEX);
+	gr6 = NVReadVgaGr(dev, 0, NV_VIO_GX_MISC_INDEX);
+
+	NVWritePRMVIO(dev, 0, NV_PRMVIO_MISC__WRITE, 0x67);
+	NVWriteVgaSeq(dev, 0, NV_VIO_SR_MEM_MODE_INDEX, 0x6);
+	NVWriteVgaGr(dev, 0, NV_VIO_GX_MODE_INDEX, 0x0);
+	NVWriteVgaGr(dev, 0, NV_VIO_GX_MISC_INDEX, 0x5);
+
+	/* store font in planes 0..3 */
+	for (plane = 0; plane < 4; plane++)
+		nouveau_vga_font_io(dev, iovram, save, plane);
+
+	/* restore control regs */
+	NVWritePRMVIO(dev, 0, NV_PRMVIO_MISC__WRITE, misc);
+	NVWriteVgaGr(dev, 0, NV_VIO_GX_READ_MAP_INDEX, gr4);
+	NVWriteVgaGr(dev, 0, NV_VIO_GX_MODE_INDEX, gr5);
+	NVWriteVgaGr(dev, 0, NV_VIO_GX_MISC_INDEX, gr6);
+	NVWriteVgaSeq(dev, 0, NV_VIO_SR_PLANE_MASK_INDEX, seq2);
+	NVWriteVgaSeq(dev, 0, NV_VIO_SR_MEM_MODE_INDEX, seq4);
+
+	if (nv_two_heads(dev))
+		NVBlankScreen(dev, 1, false);
+	NVBlankScreen(dev, 0, false);
+
+	iounmap(iovram);
+}
+
+/*
+ * mode state save/load
+ */
+
+static void
+rd_cio_state(struct drm_device *dev, int head,
+	     struct nv04_crtc_reg *crtcstate, int index)
+{
+	crtcstate->CRTC[index] = NVReadVgaCrtc(dev, head, index);
+}
+
+static void
+wr_cio_state(struct drm_device *dev, int head,
+	     struct nv04_crtc_reg *crtcstate, int index)
+{
+	NVWriteVgaCrtc(dev, head, index, crtcstate->CRTC[index]);
+}
+
+static void
+nv_save_state_ramdac(struct drm_device *dev, int head,
+		     struct nv04_mode_state *state)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nv04_crtc_reg *regp = &state->crtc_reg[head];
+	int i;
+
+	if (dev_priv->card_type >= NV_10)
+		regp->nv10_cursync = NVReadRAMDAC(dev, head, NV_RAMDAC_NV10_CURSYNC);
+
+	nouveau_hw_get_pllvals(dev, head ? VPLL2 : VPLL1, &regp->pllvals);
+	state->pllsel = NVReadRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT);
+	if (nv_two_heads(dev))
+		state->sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK);
+	if (dev_priv->chipset == 0x11)
+		regp->dither = NVReadRAMDAC(dev, head, NV_RAMDAC_DITHER_NV11);
+
+	regp->ramdac_gen_ctrl = NVReadRAMDAC(dev, head, NV_PRAMDAC_GENERAL_CONTROL);
+
+	if (nv_gf4_disp_arch(dev))
+		regp->ramdac_630 = NVReadRAMDAC(dev, head, NV_PRAMDAC_630);
+	if (dev_priv->chipset >= 0x30)
+		regp->ramdac_634 = NVReadRAMDAC(dev, head, NV_PRAMDAC_634);
+
+	regp->tv_setup = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP);
+	regp->tv_vtotal = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_VTOTAL);
+	regp->tv_vskew = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_VSKEW);
+	regp->tv_vsync_delay = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_VSYNC_DELAY);
+	regp->tv_htotal = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HTOTAL);
+	regp->tv_hskew = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HSKEW);
+	regp->tv_hsync_delay = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY);
+	regp->tv_hsync_delay2 = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY2);
+
+	for (i = 0; i < 7; i++) {
+		uint32_t ramdac_reg = NV_PRAMDAC_FP_VDISPLAY_END + (i * 4);
+		regp->fp_vert_regs[i] = NVReadRAMDAC(dev, head, ramdac_reg);
+		regp->fp_horiz_regs[i] = NVReadRAMDAC(dev, head, ramdac_reg + 0x20);
+	}
+
+	if (nv_gf4_disp_arch(dev)) {
+		regp->dither = NVReadRAMDAC(dev, head, NV_RAMDAC_FP_DITHER);
+		for (i = 0; i < 3; i++) {
+			regp->dither_regs[i] = NVReadRAMDAC(dev, head, NV_PRAMDAC_850 + i * 4);
+			regp->dither_regs[i + 3] = NVReadRAMDAC(dev, head, NV_PRAMDAC_85C + i * 4);
+		}
+	}
+
+	regp->fp_control = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL);
+	regp->fp_debug_0 = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_0);
+	if (!nv_gf4_disp_arch(dev) && head == 0) {
+		/* early chips don't allow access to PRAMDAC_TMDS_* without
+		 * the head A FPCLK on (nv11 even locks up) */
+		NVWriteRAMDAC(dev, 0, NV_PRAMDAC_FP_DEBUG_0, regp->fp_debug_0 &
+			      ~NV_PRAMDAC_FP_DEBUG_0_PWRDOWN_FPCLK);
+	}
+	regp->fp_debug_1 = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_1);
+	regp->fp_debug_2 = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_2);
+
+	regp->fp_margin_color = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_MARGIN_COLOR);
+
+	if (nv_gf4_disp_arch(dev))
+		regp->ramdac_8c0 = NVReadRAMDAC(dev, head, NV_PRAMDAC_8C0);
+
+	if (dev_priv->card_type == NV_40) {
+		regp->ramdac_a20 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A20);
+		regp->ramdac_a24 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A24);
+		regp->ramdac_a34 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A34);
+
+		for (i = 0; i < 38; i++)
+			regp->ctv_regs[i] = NVReadRAMDAC(dev, head,
+							 NV_PRAMDAC_CTV + 4*i);
+	}
+}
+
+static void
+nv_load_state_ramdac(struct drm_device *dev, int head,
+		     struct nv04_mode_state *state)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nv04_crtc_reg *regp = &state->crtc_reg[head];
+	uint32_t pllreg = head ? NV_RAMDAC_VPLL2 : NV_PRAMDAC_VPLL_COEFF;
+	int i;
+
+	if (dev_priv->card_type >= NV_10)
+		NVWriteRAMDAC(dev, head, NV_RAMDAC_NV10_CURSYNC, regp->nv10_cursync);
+
+	nouveau_hw_setpll(dev, pllreg, &regp->pllvals);
+	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT, state->pllsel);
+	if (nv_two_heads(dev))
+		NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, state->sel_clk);
+	if (dev_priv->chipset == 0x11)
+		NVWriteRAMDAC(dev, head, NV_RAMDAC_DITHER_NV11, regp->dither);
+
+	NVWriteRAMDAC(dev, head, NV_PRAMDAC_GENERAL_CONTROL, regp->ramdac_gen_ctrl);
+
+	if (nv_gf4_disp_arch(dev))
+		NVWriteRAMDAC(dev, head, NV_PRAMDAC_630, regp->ramdac_630);
+	if (dev_priv->chipset >= 0x30)
+		NVWriteRAMDAC(dev, head, NV_PRAMDAC_634, regp->ramdac_634);
+
+	NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP, regp->tv_setup);
+	NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_VTOTAL, regp->tv_vtotal);
+	NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_VSKEW, regp->tv_vskew);
+	NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_VSYNC_DELAY, regp->tv_vsync_delay);
+	NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HTOTAL, regp->tv_htotal);
+	NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HSKEW, regp->tv_hskew);
+	NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY, regp->tv_hsync_delay);
+	NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY2, regp->tv_hsync_delay2);
+
+	for (i = 0; i < 7; i++) {
+		uint32_t ramdac_reg = NV_PRAMDAC_FP_VDISPLAY_END + (i * 4);
+
+		NVWriteRAMDAC(dev, head, ramdac_reg, regp->fp_vert_regs[i]);
+		NVWriteRAMDAC(dev, head, ramdac_reg + 0x20, regp->fp_horiz_regs[i]);
+	}
+
+	if (nv_gf4_disp_arch(dev)) {
+		NVWriteRAMDAC(dev, head, NV_RAMDAC_FP_DITHER, regp->dither);
+		for (i = 0; i < 3; i++) {
+			NVWriteRAMDAC(dev, head, NV_PRAMDAC_850 + i * 4, regp->dither_regs[i]);
+			NVWriteRAMDAC(dev, head, NV_PRAMDAC_85C + i * 4, regp->dither_regs[i + 3]);
+		}
+	}
+
+	NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL, regp->fp_control);
+	NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_0, regp->fp_debug_0);
+	NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_1, regp->fp_debug_1);
+	NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_2, regp->fp_debug_2);
+
+	NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_MARGIN_COLOR, regp->fp_margin_color);
+
+	if (nv_gf4_disp_arch(dev))
+		NVWriteRAMDAC(dev, head, NV_PRAMDAC_8C0, regp->ramdac_8c0);
+
+	if (dev_priv->card_type == NV_40) {
+		NVWriteRAMDAC(dev, head, NV_PRAMDAC_A20, regp->ramdac_a20);
+		NVWriteRAMDAC(dev, head, NV_PRAMDAC_A24, regp->ramdac_a24);
+		NVWriteRAMDAC(dev, head, NV_PRAMDAC_A34, regp->ramdac_a34);
+
+		for (i = 0; i < 38; i++)
+			NVWriteRAMDAC(dev, head,
+				      NV_PRAMDAC_CTV + 4*i, regp->ctv_regs[i]);
+	}
+}
+
+static void
+nv_save_state_vga(struct drm_device *dev, int head,
+		  struct nv04_mode_state *state)
+{
+	struct nv04_crtc_reg *regp = &state->crtc_reg[head];
+	int i;
+
+	regp->MiscOutReg = NVReadPRMVIO(dev, head, NV_PRMVIO_MISC__READ);
+
+	for (i = 0; i < 25; i++)
+		rd_cio_state(dev, head, regp, i);
+
+	NVSetEnablePalette(dev, head, true);
+	for (i = 0; i < 21; i++)
+		regp->Attribute[i] = NVReadVgaAttr(dev, head, i);
+	NVSetEnablePalette(dev, head, false);
+
+	for (i = 0; i < 9; i++)
+		regp->Graphics[i] = NVReadVgaGr(dev, head, i);
+
+	for (i = 0; i < 5; i++)
+		regp->Sequencer[i] = NVReadVgaSeq(dev, head, i);
+}
+
+static void
+nv_load_state_vga(struct drm_device *dev, int head,
+		  struct nv04_mode_state *state)
+{
+	struct nv04_crtc_reg *regp = &state->crtc_reg[head];
+	int i;
+
+	NVWritePRMVIO(dev, head, NV_PRMVIO_MISC__WRITE, regp->MiscOutReg);
+
+	for (i = 0; i < 5; i++)
+		NVWriteVgaSeq(dev, head, i, regp->Sequencer[i]);
+
+	nv_lock_vga_crtc_base(dev, head, false);
+	for (i = 0; i < 25; i++)
+		wr_cio_state(dev, head, regp, i);
+	nv_lock_vga_crtc_base(dev, head, true);
+
+	for (i = 0; i < 9; i++)
+		NVWriteVgaGr(dev, head, i, regp->Graphics[i]);
+
+	NVSetEnablePalette(dev, head, true);
+	for (i = 0; i < 21; i++)
+		NVWriteVgaAttr(dev, head, i, regp->Attribute[i]);
+	NVSetEnablePalette(dev, head, false);
+}
+
+static void
+nv_save_state_ext(struct drm_device *dev, int head,
+		  struct nv04_mode_state *state)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nv04_crtc_reg *regp = &state->crtc_reg[head];
+	int i;
+
+	rd_cio_state(dev, head, regp, NV_CIO_CRE_LCD__INDEX);
+	rd_cio_state(dev, head, regp, NV_CIO_CRE_RPC0_INDEX);
+	rd_cio_state(dev, head, regp, NV_CIO_CRE_RPC1_INDEX);
+	rd_cio_state(dev, head, regp, NV_CIO_CRE_LSR_INDEX);
+	rd_cio_state(dev, head, regp, NV_CIO_CRE_PIXEL_INDEX);
+	rd_cio_state(dev, head, regp, NV_CIO_CRE_HEB__INDEX);
+	rd_cio_state(dev, head, regp, NV_CIO_CRE_ENH_INDEX);
+
+	rd_cio_state(dev, head, regp, NV_CIO_CRE_FF_INDEX);
+	rd_cio_state(dev, head, regp, NV_CIO_CRE_FFLWM__INDEX);
+	rd_cio_state(dev, head, regp, NV_CIO_CRE_21);
+	if (dev_priv->card_type >= NV_30)
+		rd_cio_state(dev, head, regp, NV_CIO_CRE_47);
+	rd_cio_state(dev, head, regp, NV_CIO_CRE_49);
+	rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX);
+	rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX);
+	rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR2_INDEX);
+	rd_cio_state(dev, head, regp, NV_CIO_CRE_ILACE__INDEX);
+
+	if (dev_priv->card_type >= NV_10) {
+		regp->crtc_830 = NVReadCRTC(dev, head, NV_PCRTC_830);
+		regp->crtc_834 = NVReadCRTC(dev, head, NV_PCRTC_834);
+
+		if (dev_priv->card_type >= NV_30)
+			regp->gpio_ext = NVReadCRTC(dev, head, NV_PCRTC_GPIO_EXT);
+
+		if (dev_priv->card_type == NV_40)
+			regp->crtc_850 = NVReadCRTC(dev, head, NV_PCRTC_850);
+
+		if (nv_two_heads(dev))
+			regp->crtc_eng_ctrl = NVReadCRTC(dev, head, NV_PCRTC_ENGINE_CTRL);
+		regp->cursor_cfg = NVReadCRTC(dev, head, NV_PCRTC_CURSOR_CONFIG);
+	}
+
+	regp->crtc_cfg = NVReadCRTC(dev, head, NV_PCRTC_CONFIG);
+
+	rd_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH3__INDEX);
+	rd_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH4__INDEX);
+	if (dev_priv->card_type >= NV_10) {
+		rd_cio_state(dev, head, regp, NV_CIO_CRE_EBR_INDEX);
+		rd_cio_state(dev, head, regp, NV_CIO_CRE_CSB);
+		rd_cio_state(dev, head, regp, NV_CIO_CRE_4B);
+		rd_cio_state(dev, head, regp, NV_CIO_CRE_TVOUT_LATENCY);
+	}
+	/* NV11 and NV20 don't have this, they stop at 0x52. */
+	if (nv_gf4_disp_arch(dev)) {
+		rd_cio_state(dev, head, regp, NV_CIO_CRE_53);
+		rd_cio_state(dev, head, regp, NV_CIO_CRE_54);
+
+		for (i = 0; i < 0x10; i++)
+			regp->CR58[i] = NVReadVgaCrtc5758(dev, head, i);
+		rd_cio_state(dev, head, regp, NV_CIO_CRE_59);
+		rd_cio_state(dev, head, regp, NV_CIO_CRE_5B);
+
+		rd_cio_state(dev, head, regp, NV_CIO_CRE_85);
+		rd_cio_state(dev, head, regp, NV_CIO_CRE_86);
+	}
+
+	regp->fb_start = NVReadCRTC(dev, head, NV_PCRTC_START);
+}
+
+static void
+nv_load_state_ext(struct drm_device *dev, int head,
+		  struct nv04_mode_state *state)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nv04_crtc_reg *regp = &state->crtc_reg[head];
+	uint32_t reg900;
+	int i;
+
+	if (dev_priv->card_type >= NV_10) {
+		if (nv_two_heads(dev))
+			/* setting ENGINE_CTRL (EC) *must* come before
+			 * CIO_CRE_LCD, as writing CRE_LCD sets bits 16 & 17 in
+			 * EC that should not be overwritten by writing stale EC
+			 */
+			NVWriteCRTC(dev, head, NV_PCRTC_ENGINE_CTRL, regp->crtc_eng_ctrl);
+
+		nvWriteVIDEO(dev, NV_PVIDEO_STOP, 1);
+		nvWriteVIDEO(dev, NV_PVIDEO_INTR_EN, 0);
+		nvWriteVIDEO(dev, NV_PVIDEO_OFFSET_BUFF(0), 0);
+		nvWriteVIDEO(dev, NV_PVIDEO_OFFSET_BUFF(1), 0);
+		nvWriteVIDEO(dev, NV_PVIDEO_LIMIT(0), dev_priv->fb_available_size - 1);
+		nvWriteVIDEO(dev, NV_PVIDEO_LIMIT(1), dev_priv->fb_available_size - 1);
+		nvWriteVIDEO(dev, NV_PVIDEO_UVPLANE_LIMIT(0), dev_priv->fb_available_size - 1);
+		nvWriteVIDEO(dev, NV_PVIDEO_UVPLANE_LIMIT(1), dev_priv->fb_available_size - 1);
+		nvWriteMC(dev, NV_PBUS_POWERCTRL_2, 0);
+
+		NVWriteCRTC(dev, head, NV_PCRTC_CURSOR_CONFIG, regp->cursor_cfg);
+		NVWriteCRTC(dev, head, NV_PCRTC_830, regp->crtc_830);
+		NVWriteCRTC(dev, head, NV_PCRTC_834, regp->crtc_834);
+
+		if (dev_priv->card_type >= NV_30)
+			NVWriteCRTC(dev, head, NV_PCRTC_GPIO_EXT, regp->gpio_ext);
+
+		if (dev_priv->card_type == NV_40) {
+			NVWriteCRTC(dev, head, NV_PCRTC_850, regp->crtc_850);
+
+			reg900 = NVReadRAMDAC(dev, head, NV_PRAMDAC_900);
+			if (regp->crtc_cfg == NV_PCRTC_CONFIG_START_ADDRESS_HSYNC)
+				NVWriteRAMDAC(dev, head, NV_PRAMDAC_900, reg900 | 0x10000);
+			else
+				NVWriteRAMDAC(dev, head, NV_PRAMDAC_900, reg900 & ~0x10000);
+		}
+	}
+
+	NVWriteCRTC(dev, head, NV_PCRTC_CONFIG, regp->crtc_cfg);
+
+	wr_cio_state(dev, head, regp, NV_CIO_CRE_RPC0_INDEX);
+	wr_cio_state(dev, head, regp, NV_CIO_CRE_RPC1_INDEX);
+	wr_cio_state(dev, head, regp, NV_CIO_CRE_LSR_INDEX);
+	wr_cio_state(dev, head, regp, NV_CIO_CRE_PIXEL_INDEX);
+	wr_cio_state(dev, head, regp, NV_CIO_CRE_LCD__INDEX);
+	wr_cio_state(dev, head, regp, NV_CIO_CRE_HEB__INDEX);
+	wr_cio_state(dev, head, regp, NV_CIO_CRE_ENH_INDEX);
+	wr_cio_state(dev, head, regp, NV_CIO_CRE_FF_INDEX);
+	wr_cio_state(dev, head, regp, NV_CIO_CRE_FFLWM__INDEX);
+	if (dev_priv->card_type >= NV_30)
+		wr_cio_state(dev, head, regp, NV_CIO_CRE_47);
+
+	wr_cio_state(dev, head, regp, NV_CIO_CRE_49);
+	wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX);
+	wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX);
+	wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR2_INDEX);
+	if (dev_priv->card_type == NV_40)
+		nv_fix_nv40_hw_cursor(dev, head);
+	wr_cio_state(dev, head, regp, NV_CIO_CRE_ILACE__INDEX);
+
+	wr_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH3__INDEX);
+	wr_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH4__INDEX);
+	if (dev_priv->card_type >= NV_10) {
+		wr_cio_state(dev, head, regp, NV_CIO_CRE_EBR_INDEX);
+		wr_cio_state(dev, head, regp, NV_CIO_CRE_CSB);
+		wr_cio_state(dev, head, regp, NV_CIO_CRE_4B);
+		wr_cio_state(dev, head, regp, NV_CIO_CRE_TVOUT_LATENCY);
+	}
+	/* NV11 and NV20 stop at 0x52. */
+	if (nv_gf4_disp_arch(dev)) {
+		if (dev_priv->card_type == NV_10) {
+			/* Not waiting for vertical retrace before modifying
+			   CRE_53/CRE_54 causes lockups. */
+			nouveau_wait_until(dev, 650000000, NV_PRMCIO_INP0__COLOR, 0x8, 0x8);
+			nouveau_wait_until(dev, 650000000, NV_PRMCIO_INP0__COLOR, 0x8, 0x0);
+		}
+
+		wr_cio_state(dev, head, regp, NV_CIO_CRE_53);
+		wr_cio_state(dev, head, regp, NV_CIO_CRE_54);
+
+		for (i = 0; i < 0x10; i++)
+			NVWriteVgaCrtc5758(dev, head, i, regp->CR58[i]);
+		wr_cio_state(dev, head, regp, NV_CIO_CRE_59);
+		wr_cio_state(dev, head, regp, NV_CIO_CRE_5B);
+
+		wr_cio_state(dev, head, regp, NV_CIO_CRE_85);
+		wr_cio_state(dev, head, regp, NV_CIO_CRE_86);
+	}
+
+	NVWriteCRTC(dev, head, NV_PCRTC_START, regp->fb_start);
+
+	/* Setting 1 on this value gives you interrupts for every vblank period. */
+	NVWriteCRTC(dev, head, NV_PCRTC_INTR_EN_0, 0);
+	NVWriteCRTC(dev, head, NV_PCRTC_INTR_0, NV_PCRTC_INTR_0_VBLANK);
+}
+
+static void
+nv_save_state_palette(struct drm_device *dev, int head,
+		      struct nv04_mode_state *state)
+{
+	int head_offset = head * NV_PRMDIO_SIZE, i;
+
+	nv_wr08(dev, NV_PRMDIO_PIXEL_MASK + head_offset,
+				NV_PRMDIO_PIXEL_MASK_MASK);
+	nv_wr08(dev, NV_PRMDIO_READ_MODE_ADDRESS + head_offset, 0x0);
+
+	for (i = 0; i < 768; i++) {
+		state->crtc_reg[head].DAC[i] = nv_rd08(dev,
+				NV_PRMDIO_PALETTE_DATA + head_offset);
+	}
+
+	NVSetEnablePalette(dev, head, false);
+}
+
+void
+nouveau_hw_load_state_palette(struct drm_device *dev, int head,
+			      struct nv04_mode_state *state)
+{
+	int head_offset = head * NV_PRMDIO_SIZE, i;
+
+	nv_wr08(dev, NV_PRMDIO_PIXEL_MASK + head_offset,
+				NV_PRMDIO_PIXEL_MASK_MASK);
+	nv_wr08(dev, NV_PRMDIO_WRITE_MODE_ADDRESS + head_offset, 0x0);
+
+	for (i = 0; i < 768; i++) {
+		nv_wr08(dev, NV_PRMDIO_PALETTE_DATA + head_offset,
+				state->crtc_reg[head].DAC[i]);
+	}
+
+	NVSetEnablePalette(dev, head, false);
+}
+
+void nouveau_hw_save_state(struct drm_device *dev, int head,
+			   struct nv04_mode_state *state)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	if (dev_priv->chipset == 0x11)
+		/* NB: no attempt is made to restore the bad pll later on */
+		nouveau_hw_fix_bad_vpll(dev, head);
+	nv_save_state_ramdac(dev, head, state);
+	nv_save_state_vga(dev, head, state);
+	nv_save_state_palette(dev, head, state);
+	nv_save_state_ext(dev, head, state);
+}
+
+void nouveau_hw_load_state(struct drm_device *dev, int head,
+			   struct nv04_mode_state *state)
+{
+	NVVgaProtect(dev, head, true);
+	nv_load_state_ramdac(dev, head, state);
+	nv_load_state_ext(dev, head, state);
+	nouveau_hw_load_state_palette(dev, head, state);
+	nv_load_state_vga(dev, head, state);
+	NVVgaProtect(dev, head, false);
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_hw.h b/drivers/gpu/drm/nouveau/nouveau_hw.h
new file mode 100644
index 0000000..869130f
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_hw.h
@@ -0,0 +1,455 @@
+/*
+ * Copyright 2008 Stuart Bennett
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef __NOUVEAU_HW_H__
+#define __NOUVEAU_HW_H__
+
+#include "drmP.h"
+#include "nouveau_drv.h"
+
+#define MASK(field) ( \
+	(0xffffffff >> (31 - ((1 ? field) - (0 ? field)))) << (0 ? field))
+
+#define XLATE(src, srclowbit, outfield) ( \
+	(((src) >> (srclowbit)) << (0 ? outfield)) & MASK(outfield))
+
+void NVWriteVgaSeq(struct drm_device *, int head, uint8_t index, uint8_t value);
+uint8_t NVReadVgaSeq(struct drm_device *, int head, uint8_t index);
+void NVWriteVgaGr(struct drm_device *, int head, uint8_t index, uint8_t value);
+uint8_t NVReadVgaGr(struct drm_device *, int head, uint8_t index);
+void NVSetOwner(struct drm_device *, int owner);
+void NVBlankScreen(struct drm_device *, int head, bool blank);
+void nouveau_hw_setpll(struct drm_device *, uint32_t reg1,
+		       struct nouveau_pll_vals *pv);
+int nouveau_hw_get_pllvals(struct drm_device *, enum pll_types plltype,
+			   struct nouveau_pll_vals *pllvals);
+int nouveau_hw_pllvals_to_clk(struct nouveau_pll_vals *pllvals);
+int nouveau_hw_get_clock(struct drm_device *, enum pll_types plltype);
+void nouveau_hw_save_vga_fonts(struct drm_device *, bool save);
+void nouveau_hw_save_state(struct drm_device *, int head,
+			   struct nv04_mode_state *state);
+void nouveau_hw_load_state(struct drm_device *, int head,
+			   struct nv04_mode_state *state);
+void nouveau_hw_load_state_palette(struct drm_device *, int head,
+				   struct nv04_mode_state *state);
+
+/* nouveau_calc.c */
+extern void nouveau_calc_arb(struct drm_device *, int vclk, int bpp,
+			     int *burst, int *lwm);
+extern int nouveau_calc_pll_mnp(struct drm_device *, struct pll_lims *pll_lim,
+				int clk, struct nouveau_pll_vals *pv);
+
+static inline uint32_t
+nvReadMC(struct drm_device *dev, uint32_t reg)
+{
+	uint32_t val = nv_rd32(dev, reg);
+	NV_REG_DEBUG(MC, dev, "reg %08x val %08x\n", reg, val);
+	return val;
+}
+
+static inline void
+nvWriteMC(struct drm_device *dev, uint32_t reg, uint32_t val)
+{
+	NV_REG_DEBUG(MC, dev, "reg %08x val %08x\n", reg, val);
+	nv_wr32(dev, reg, val);
+}
+
+static inline uint32_t
+nvReadVIDEO(struct drm_device *dev, uint32_t reg)
+{
+	uint32_t val = nv_rd32(dev, reg);
+	NV_REG_DEBUG(VIDEO, dev, "reg %08x val %08x\n", reg, val);
+	return val;
+}
+
+static inline void
+nvWriteVIDEO(struct drm_device *dev, uint32_t reg, uint32_t val)
+{
+	NV_REG_DEBUG(VIDEO, dev, "reg %08x val %08x\n", reg, val);
+	nv_wr32(dev, reg, val);
+}
+
+static inline uint32_t
+nvReadFB(struct drm_device *dev, uint32_t reg)
+{
+	uint32_t val = nv_rd32(dev, reg);
+	NV_REG_DEBUG(FB, dev, "reg %08x val %08x\n", reg, val);
+	return val;
+}
+
+static inline void
+nvWriteFB(struct drm_device *dev, uint32_t reg, uint32_t val)
+{
+	NV_REG_DEBUG(FB, dev, "reg %08x val %08x\n", reg, val);
+	nv_wr32(dev, reg, val);
+}
+
+static inline uint32_t
+nvReadEXTDEV(struct drm_device *dev, uint32_t reg)
+{
+	uint32_t val = nv_rd32(dev, reg);
+	NV_REG_DEBUG(EXTDEV, dev, "reg %08x val %08x\n", reg, val);
+	return val;
+}
+
+static inline void
+nvWriteEXTDEV(struct drm_device *dev, uint32_t reg, uint32_t val)
+{
+	NV_REG_DEBUG(EXTDEV, dev, "reg %08x val %08x\n", reg, val);
+	nv_wr32(dev, reg, val);
+}
+
+static inline uint32_t NVReadCRTC(struct drm_device *dev,
+					int head, uint32_t reg)
+{
+	uint32_t val;
+	if (head)
+		reg += NV_PCRTC0_SIZE;
+	val = nv_rd32(dev, reg);
+	NV_REG_DEBUG(CRTC, dev, "head %d reg %08x val %08x\n", head, reg, val);
+	return val;
+}
+
+static inline void NVWriteCRTC(struct drm_device *dev,
+					int head, uint32_t reg, uint32_t val)
+{
+	if (head)
+		reg += NV_PCRTC0_SIZE;
+	NV_REG_DEBUG(CRTC, dev, "head %d reg %08x val %08x\n", head, reg, val);
+	nv_wr32(dev, reg, val);
+}
+
+static inline uint32_t NVReadRAMDAC(struct drm_device *dev,
+					int head, uint32_t reg)
+{
+	uint32_t val;
+	if (head)
+		reg += NV_PRAMDAC0_SIZE;
+	val = nv_rd32(dev, reg);
+	NV_REG_DEBUG(RAMDAC, dev, "head %d reg %08x val %08x\n",
+							head, reg, val);
+	return val;
+}
+
+static inline void NVWriteRAMDAC(struct drm_device *dev,
+					int head, uint32_t reg, uint32_t val)
+{
+	if (head)
+		reg += NV_PRAMDAC0_SIZE;
+	NV_REG_DEBUG(RAMDAC, dev, "head %d reg %08x val %08x\n",
+							head, reg, val);
+	nv_wr32(dev, reg, val);
+}
+
+static inline uint8_t nv_read_tmds(struct drm_device *dev,
+					int or, int dl, uint8_t address)
+{
+	int ramdac = (or & OUTPUT_C) >> 2;
+
+	NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_CONTROL + dl * 8,
+	NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE | address);
+	return NVReadRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_DATA + dl * 8);
+}
+
+static inline void nv_write_tmds(struct drm_device *dev,
+					int or, int dl, uint8_t address,
+					uint8_t data)
+{
+	int ramdac = (or & OUTPUT_C) >> 2;
+
+	NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_DATA + dl * 8, data);
+	NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_CONTROL + dl * 8, address);
+}
+
+static inline void NVWriteVgaCrtc(struct drm_device *dev,
+					int head, uint8_t index, uint8_t value)
+{
+	NV_REG_DEBUG(VGACRTC, dev, "head %d index 0x%02x data 0x%02x\n",
+							head, index, value);
+	nv_wr08(dev, NV_PRMCIO_CRX__COLOR + head * NV_PRMCIO_SIZE, index);
+	nv_wr08(dev, NV_PRMCIO_CR__COLOR + head * NV_PRMCIO_SIZE, value);
+}
+
+static inline uint8_t NVReadVgaCrtc(struct drm_device *dev,
+					int head, uint8_t index)
+{
+	uint8_t val;
+	nv_wr08(dev, NV_PRMCIO_CRX__COLOR + head * NV_PRMCIO_SIZE, index);
+	val = nv_rd08(dev, NV_PRMCIO_CR__COLOR + head * NV_PRMCIO_SIZE);
+	NV_REG_DEBUG(VGACRTC, dev, "head %d index 0x%02x data 0x%02x\n",
+							head, index, val);
+	return val;
+}
+
+/* CR57 and CR58 are a fun pair of regs. CR57 provides an index (0-0xf) for CR58
+ * I suspect they in fact do nothing, but are merely a way to carry useful
+ * per-head variables around
+ *
+ * Known uses:
+ * CR57		CR58
+ * 0x00		index to the appropriate dcb entry (or 7f for inactive)
+ * 0x02		dcb entry's "or" value (or 00 for inactive)
+ * 0x03		bit0 set for dual link (LVDS, possibly elsewhere too)
+ * 0x08 or 0x09	pxclk in MHz
+ * 0x0f		laptop panel info -	low nibble for PEXTDEV_BOOT_0 strap
+ * 					high nibble for xlat strap value
+ */
+
+static inline void
+NVWriteVgaCrtc5758(struct drm_device *dev, int head, uint8_t index, uint8_t value)
+{
+	NVWriteVgaCrtc(dev, head, NV_CIO_CRE_57, index);
+	NVWriteVgaCrtc(dev, head, NV_CIO_CRE_58, value);
+}
+
+static inline uint8_t NVReadVgaCrtc5758(struct drm_device *dev, int head, uint8_t index)
+{
+	NVWriteVgaCrtc(dev, head, NV_CIO_CRE_57, index);
+	return NVReadVgaCrtc(dev, head, NV_CIO_CRE_58);
+}
+
+static inline uint8_t NVReadPRMVIO(struct drm_device *dev,
+					int head, uint32_t reg)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	uint8_t val;
+
+	/* Only NV4x have two pvio ranges; other twoHeads cards MUST call
+	 * NVSetOwner for the relevant head to be programmed */
+	if (head && dev_priv->card_type == NV_40)
+		reg += NV_PRMVIO_SIZE;
+
+	val = nv_rd08(dev, reg);
+	NV_REG_DEBUG(RMVIO, dev, "head %d reg %08x val %02x\n", head, reg, val);
+	return val;
+}
+
+static inline void NVWritePRMVIO(struct drm_device *dev,
+					int head, uint32_t reg, uint8_t value)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	/* Only NV4x have two pvio ranges; other twoHeads cards MUST call
+	 * NVSetOwner for the relevant head to be programmed */
+	if (head && dev_priv->card_type == NV_40)
+		reg += NV_PRMVIO_SIZE;
+
+	NV_REG_DEBUG(RMVIO, dev, "head %d reg %08x val %02x\n",
+						head, reg, value);
+	nv_wr08(dev, reg, value);
+}
+
+static inline void NVSetEnablePalette(struct drm_device *dev, int head, bool enable)
+{
+	nv_rd08(dev, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE);
+	nv_wr08(dev, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, enable ? 0 : 0x20);
+}
+
+static inline bool NVGetEnablePalette(struct drm_device *dev, int head)
+{
+	nv_rd08(dev, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE);
+	return !(nv_rd08(dev, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE) & 0x20);
+}
+
+static inline void NVWriteVgaAttr(struct drm_device *dev,
+					int head, uint8_t index, uint8_t value)
+{
+	if (NVGetEnablePalette(dev, head))
+		index &= ~0x20;
+	else
+		index |= 0x20;
+
+	nv_rd08(dev, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE);
+	NV_REG_DEBUG(VGAATTR, dev, "head %d index 0x%02x data 0x%02x\n",
+							head, index, value);
+	nv_wr08(dev, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, index);
+	nv_wr08(dev, NV_PRMCIO_AR__WRITE + head * NV_PRMCIO_SIZE, value);
+}
+
+static inline uint8_t NVReadVgaAttr(struct drm_device *dev,
+					int head, uint8_t index)
+{
+	uint8_t val;
+	if (NVGetEnablePalette(dev, head))
+		index &= ~0x20;
+	else
+		index |= 0x20;
+
+	nv_rd08(dev, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE);
+	nv_wr08(dev, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, index);
+	val = nv_rd08(dev, NV_PRMCIO_AR__READ + head * NV_PRMCIO_SIZE);
+	NV_REG_DEBUG(VGAATTR, dev, "head %d index 0x%02x data 0x%02x\n",
+							head, index, val);
+	return val;
+}
+
+static inline void NVVgaSeqReset(struct drm_device *dev, int head, bool start)
+{
+	NVWriteVgaSeq(dev, head, NV_VIO_SR_RESET_INDEX, start ? 0x1 : 0x3);
+}
+
+static inline void NVVgaProtect(struct drm_device *dev, int head, bool protect)
+{
+	uint8_t seq1 = NVReadVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX);
+
+	if (protect) {
+		NVVgaSeqReset(dev, head, true);
+		NVWriteVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX, seq1 | 0x20);
+	} else {
+		/* Reenable sequencer, then turn on screen */
+		NVWriteVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX, seq1 & ~0x20);   /* reenable display */
+		NVVgaSeqReset(dev, head, false);
+	}
+	NVSetEnablePalette(dev, head, protect);
+}
+
+static inline bool
+nv_heads_tied(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	if (dev_priv->chipset == 0x11)
+		return !!(nvReadMC(dev, NV_PBUS_DEBUG_1) & (1 << 28));
+
+	return NVReadVgaCrtc(dev, 0, NV_CIO_CRE_44) & 0x4;
+}
+
+/* makes cr0-7 on the specified head read-only */
+static inline bool
+nv_lock_vga_crtc_base(struct drm_device *dev, int head, bool lock)
+{
+	uint8_t cr11 = NVReadVgaCrtc(dev, head, NV_CIO_CR_VRE_INDEX);
+	bool waslocked = cr11 & 0x80;
+
+	if (lock)
+		cr11 |= 0x80;
+	else
+		cr11 &= ~0x80;
+	NVWriteVgaCrtc(dev, head, NV_CIO_CR_VRE_INDEX, cr11);
+
+	return waslocked;
+}
+
+static inline void
+nv_lock_vga_crtc_shadow(struct drm_device *dev, int head, int lock)
+{
+	/* shadow lock: connects 0x60?3d? regs to "real" 0x3d? regs
+	 * bit7: unlocks HDT, HBS, HBE, HRS, HRE, HEB
+	 * bit6: seems to have some effect on CR09 (double scan, VBS_9)
+	 * bit5: unlocks HDE
+	 * bit4: unlocks VDE
+	 * bit3: unlocks VDT, OVL, VRS, ?VRE?, VBS, VBE, LSR, EBR
+	 * bit2: same as bit 1 of 0x60?804
+	 * bit0: same as bit 0 of 0x60?804
+	 */
+
+	uint8_t cr21 = lock;
+
+	if (lock < 0)
+		/* 0xfa is generic "unlock all" mask */
+		cr21 = NVReadVgaCrtc(dev, head, NV_CIO_CRE_21) | 0xfa;
+
+	NVWriteVgaCrtc(dev, head, NV_CIO_CRE_21, cr21);
+}
+
+/* renders the extended crtc regs (cr19+) on all crtcs impervious:
+ * immutable and unreadable
+ */
+static inline bool
+NVLockVgaCrtcs(struct drm_device *dev, bool lock)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	bool waslocked = !NVReadVgaCrtc(dev, 0, NV_CIO_SR_LOCK_INDEX);
+
+	NVWriteVgaCrtc(dev, 0, NV_CIO_SR_LOCK_INDEX,
+		       lock ? NV_CIO_SR_LOCK_VALUE : NV_CIO_SR_UNLOCK_RW_VALUE);
+	/* NV11 has independently lockable extended crtcs, except when tied */
+	if (dev_priv->chipset == 0x11 && !nv_heads_tied(dev))
+		NVWriteVgaCrtc(dev, 1, NV_CIO_SR_LOCK_INDEX,
+			       lock ? NV_CIO_SR_LOCK_VALUE :
+				      NV_CIO_SR_UNLOCK_RW_VALUE);
+
+	return waslocked;
+}
+
+/* nv04 cursor max dimensions of 32x32 (A1R5G5B5) */
+#define NV04_CURSOR_SIZE 32
+/* limit nv10 cursors to 64x64 (ARGB8) (we could go to 64x255) */
+#define NV10_CURSOR_SIZE 64
+
+static inline int nv_cursor_width(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	return dev_priv->card_type >= NV_10 ? NV10_CURSOR_SIZE : NV04_CURSOR_SIZE;
+}
+
+static inline void
+nv_fix_nv40_hw_cursor(struct drm_device *dev, int head)
+{
+	/* on some nv40 (such as the "true" (in the NV_PFB_BOOT_0 sense) nv40,
+	 * the gf6800gt) a hardware bug requires a write to PRAMDAC_CURSOR_POS
+	 * for changes to the CRTC CURCTL regs to take effect, whether changing
+	 * the pixmap location, or just showing/hiding the cursor
+	 */
+	uint32_t curpos = NVReadRAMDAC(dev, head, NV_PRAMDAC_CU_START_POS);
+	NVWriteRAMDAC(dev, head, NV_PRAMDAC_CU_START_POS, curpos);
+}
+
+static inline void
+nv_show_cursor(struct drm_device *dev, int head, bool show)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	uint8_t *curctl1 =
+		&dev_priv->mode_reg.crtc_reg[head].CRTC[NV_CIO_CRE_HCUR_ADDR1_INDEX];
+
+	if (show)
+		*curctl1 |= MASK(NV_CIO_CRE_HCUR_ADDR1_ENABLE);
+	else
+		*curctl1 &= ~MASK(NV_CIO_CRE_HCUR_ADDR1_ENABLE);
+	NVWriteVgaCrtc(dev, head, NV_CIO_CRE_HCUR_ADDR1_INDEX, *curctl1);
+
+	if (dev_priv->card_type == NV_40)
+		nv_fix_nv40_hw_cursor(dev, head);
+}
+
+static inline uint32_t
+nv_pitch_align(struct drm_device *dev, uint32_t width, int bpp)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	int mask;
+
+	if (bpp == 15)
+		bpp = 16;
+	if (bpp == 24)
+		bpp = 8;
+
+	/* Alignment requirements taken from the Haiku driver */
+	if (dev_priv->card_type == NV_04)
+		mask = 128 / bpp - 1;
+	else
+		mask = 512 / bpp - 1;
+
+	return (width + mask) & ~mask;
+}
+
+#endif	/* __NOUVEAU_HW_H__ */
diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c
new file mode 100644
index 0000000..70e994d
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c
@@ -0,0 +1,269 @@
+/*
+ * Copyright 2009 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "drmP.h"
+#include "nouveau_drv.h"
+#include "nouveau_i2c.h"
+#include "nouveau_hw.h"
+
+static void
+nv04_i2c_setscl(void *data, int state)
+{
+	struct nouveau_i2c_chan *i2c = data;
+	struct drm_device *dev = i2c->dev;
+	uint8_t val;
+
+	val = (NVReadVgaCrtc(dev, 0, i2c->wr) & 0xd0) | (state ? 0x20 : 0);
+	NVWriteVgaCrtc(dev, 0, i2c->wr, val | 0x01);
+}
+
+static void
+nv04_i2c_setsda(void *data, int state)
+{
+	struct nouveau_i2c_chan *i2c = data;
+	struct drm_device *dev = i2c->dev;
+	uint8_t val;
+
+	val = (NVReadVgaCrtc(dev, 0, i2c->wr) & 0xe0) | (state ? 0x10 : 0);
+	NVWriteVgaCrtc(dev, 0, i2c->wr, val | 0x01);
+}
+
+static int
+nv04_i2c_getscl(void *data)
+{
+	struct nouveau_i2c_chan *i2c = data;
+	struct drm_device *dev = i2c->dev;
+
+	return !!(NVReadVgaCrtc(dev, 0, i2c->rd) & 4);
+}
+
+static int
+nv04_i2c_getsda(void *data)
+{
+	struct nouveau_i2c_chan *i2c = data;
+	struct drm_device *dev = i2c->dev;
+
+	return !!(NVReadVgaCrtc(dev, 0, i2c->rd) & 8);
+}
+
+static void
+nv4e_i2c_setscl(void *data, int state)
+{
+	struct nouveau_i2c_chan *i2c = data;
+	struct drm_device *dev = i2c->dev;
+	uint8_t val;
+
+	val = (nv_rd32(dev, i2c->wr) & 0xd0) | (state ? 0x20 : 0);
+	nv_wr32(dev, i2c->wr, val | 0x01);
+}
+
+static void
+nv4e_i2c_setsda(void *data, int state)
+{
+	struct nouveau_i2c_chan *i2c = data;
+	struct drm_device *dev = i2c->dev;
+	uint8_t val;
+
+	val = (nv_rd32(dev, i2c->wr) & 0xe0) | (state ? 0x10 : 0);
+	nv_wr32(dev, i2c->wr, val | 0x01);
+}
+
+static int
+nv4e_i2c_getscl(void *data)
+{
+	struct nouveau_i2c_chan *i2c = data;
+	struct drm_device *dev = i2c->dev;
+
+	return !!((nv_rd32(dev, i2c->rd) >> 16) & 4);
+}
+
+static int
+nv4e_i2c_getsda(void *data)
+{
+	struct nouveau_i2c_chan *i2c = data;
+	struct drm_device *dev = i2c->dev;
+
+	return !!((nv_rd32(dev, i2c->rd) >> 16) & 8);
+}
+
+static int
+nv50_i2c_getscl(void *data)
+{
+	struct nouveau_i2c_chan *i2c = data;
+	struct drm_device *dev = i2c->dev;
+
+	return !!(nv_rd32(dev, i2c->rd) & 1);
+}
+
+
+static int
+nv50_i2c_getsda(void *data)
+{
+	struct nouveau_i2c_chan *i2c = data;
+	struct drm_device *dev = i2c->dev;
+
+	return !!(nv_rd32(dev, i2c->rd) & 2);
+}
+
+static void
+nv50_i2c_setscl(void *data, int state)
+{
+	struct nouveau_i2c_chan *i2c = data;
+	struct drm_device *dev = i2c->dev;
+
+	nv_wr32(dev, i2c->wr, 4 | (i2c->data ? 2 : 0) | (state ? 1 : 0));
+}
+
+static void
+nv50_i2c_setsda(void *data, int state)
+{
+	struct nouveau_i2c_chan *i2c = data;
+	struct drm_device *dev = i2c->dev;
+
+	nv_wr32(dev, i2c->wr,
+			(nv_rd32(dev, i2c->rd) & 1) | 4 | (state ? 2 : 0));
+	i2c->data = state;
+}
+
+static const uint32_t nv50_i2c_port[] = {
+	0x00e138, 0x00e150, 0x00e168, 0x00e180,
+	0x00e254, 0x00e274, 0x00e764, 0x00e780,
+	0x00e79c, 0x00e7b8
+};
+#define NV50_I2C_PORTS ARRAY_SIZE(nv50_i2c_port)
+
+int
+nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_i2c_chan *i2c;
+	int ret;
+
+	if (entry->chan)
+		return -EEXIST;
+
+	if (dev_priv->card_type == NV_50 && entry->read >= NV50_I2C_PORTS) {
+		NV_ERROR(dev, "unknown i2c port %d\n", entry->read);
+		return -EINVAL;
+	}
+
+	i2c = kzalloc(sizeof(*i2c), GFP_KERNEL);
+	if (i2c == NULL)
+		return -ENOMEM;
+
+	switch (entry->port_type) {
+	case 0:
+		i2c->algo.bit.setsda = nv04_i2c_setsda;
+		i2c->algo.bit.setscl = nv04_i2c_setscl;
+		i2c->algo.bit.getsda = nv04_i2c_getsda;
+		i2c->algo.bit.getscl = nv04_i2c_getscl;
+		i2c->rd = entry->read;
+		i2c->wr = entry->write;
+		break;
+	case 4:
+		i2c->algo.bit.setsda = nv4e_i2c_setsda;
+		i2c->algo.bit.setscl = nv4e_i2c_setscl;
+		i2c->algo.bit.getsda = nv4e_i2c_getsda;
+		i2c->algo.bit.getscl = nv4e_i2c_getscl;
+		i2c->rd = 0x600800 + entry->read;
+		i2c->wr = 0x600800 + entry->write;
+		break;
+	case 5:
+		i2c->algo.bit.setsda = nv50_i2c_setsda;
+		i2c->algo.bit.setscl = nv50_i2c_setscl;
+		i2c->algo.bit.getsda = nv50_i2c_getsda;
+		i2c->algo.bit.getscl = nv50_i2c_getscl;
+		i2c->rd = nv50_i2c_port[entry->read];
+		i2c->wr = i2c->rd;
+		break;
+	case 6:
+		i2c->rd = entry->read;
+		i2c->wr = entry->write;
+		break;
+	default:
+		NV_ERROR(dev, "DCB I2C port type %d unknown\n",
+			 entry->port_type);
+		kfree(i2c);
+		return -EINVAL;
+	}
+
+	snprintf(i2c->adapter.name, sizeof(i2c->adapter.name),
+		 "nouveau-%s-%d", pci_name(dev->pdev), index);
+	i2c->adapter.owner = THIS_MODULE;
+	i2c->adapter.dev.parent = &dev->pdev->dev;
+	i2c->dev = dev;
+	i2c_set_adapdata(&i2c->adapter, i2c);
+
+	if (entry->port_type < 6) {
+		i2c->adapter.algo_data = &i2c->algo.bit;
+		i2c->algo.bit.udelay = 40;
+		i2c->algo.bit.timeout = usecs_to_jiffies(5000);
+		i2c->algo.bit.data = i2c;
+		ret = i2c_bit_add_bus(&i2c->adapter);
+	} else {
+		i2c->adapter.algo_data = &i2c->algo.dp;
+		i2c->algo.dp.running = false;
+		i2c->algo.dp.address = 0;
+		i2c->algo.dp.aux_ch = nouveau_dp_i2c_aux_ch;
+		ret = i2c_dp_aux_add_bus(&i2c->adapter);
+	}
+
+	if (ret) {
+		NV_ERROR(dev, "Failed to register i2c %d\n", index);
+		kfree(i2c);
+		return ret;
+	}
+
+	entry->chan = i2c;
+	return 0;
+}
+
+void
+nouveau_i2c_fini(struct drm_device *dev, struct dcb_i2c_entry *entry)
+{
+	if (!entry->chan)
+		return;
+
+	i2c_del_adapter(&entry->chan->adapter);
+	kfree(entry->chan);
+	entry->chan = NULL;
+}
+
+struct nouveau_i2c_chan *
+nouveau_i2c_find(struct drm_device *dev, int index)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nvbios *bios = &dev_priv->VBIOS;
+
+	if (index > DCB_MAX_NUM_I2C_ENTRIES)
+		return NULL;
+
+	if (!bios->bdcb.dcb.i2c[index].chan) {
+		if (nouveau_i2c_init(dev, &bios->bdcb.dcb.i2c[index], index))
+			return NULL;
+	}
+
+	return bios->bdcb.dcb.i2c[index].chan;
+}
+
diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.h b/drivers/gpu/drm/nouveau/nouveau_i2c.h
new file mode 100644
index 0000000..c8eaf7a
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_i2c.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2009 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __NOUVEAU_I2C_H__
+#define __NOUVEAU_I2C_H__
+
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+#include <linux/i2c-algo-bit.h>
+#include "drm_dp_helper.h"
+
+struct dcb_i2c_entry;
+
+struct nouveau_i2c_chan {
+	struct i2c_adapter adapter;
+	struct drm_device *dev;
+	union {
+		struct i2c_algo_bit_data bit;
+		struct i2c_algo_dp_aux_data dp;
+	} algo;
+	unsigned rd;
+	unsigned wr;
+	unsigned data;
+};
+
+int nouveau_i2c_init(struct drm_device *, struct dcb_i2c_entry *, int index);
+void nouveau_i2c_fini(struct drm_device *, struct dcb_i2c_entry *);
+struct nouveau_i2c_chan *nouveau_i2c_find(struct drm_device *, int index);
+
+int nouveau_dp_i2c_aux_ch(struct i2c_adapter *, int mode, uint8_t write_byte,
+			  uint8_t *read_byte);
+
+#endif /* __NOUVEAU_I2C_H__ */
diff --git a/drivers/gpu/drm/nouveau/nouveau_ioc32.c b/drivers/gpu/drm/nouveau/nouveau_ioc32.c
new file mode 100644
index 0000000..a2c30f4
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_ioc32.c
@@ -0,0 +1,72 @@
+/**
+ * \file mga_ioc32.c
+ *
+ * 32-bit ioctl compatibility routines for the MGA DRM.
+ *
+ * \author Dave Airlie <airlied@linux.ie> with code from patches by Egbert Eich
+ *
+ *
+ * Copyright (C) Paul Mackerras 2005
+ * Copyright (C) Egbert Eich 2003,2004
+ * Copyright (C) Dave Airlie 2005
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <linux/compat.h>
+
+#include "drmP.h"
+#include "drm.h"
+
+#include "nouveau_drv.h"
+
+/**
+ * Called whenever a 32-bit process running under a 64-bit kernel
+ * performs an ioctl on /dev/dri/card<n>.
+ *
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg user argument.
+ * \return zero on success or negative number on failure.
+ */
+long nouveau_compat_ioctl(struct file *filp, unsigned int cmd,
+			 unsigned long arg)
+{
+	unsigned int nr = DRM_IOCTL_NR(cmd);
+	drm_ioctl_compat_t *fn = NULL;
+	int ret;
+
+	if (nr < DRM_COMMAND_BASE)
+		return drm_compat_ioctl(filp, cmd, arg);
+
+#if 0
+	if (nr < DRM_COMMAND_BASE + DRM_ARRAY_SIZE(mga_compat_ioctls))
+		fn = nouveau_compat_ioctls[nr - DRM_COMMAND_BASE];
+#endif
+	lock_kernel();	  /* XXX for now */
+	if (fn != NULL)
+		ret = (*fn)(filp, cmd, arg);
+	else
+		ret = drm_ioctl(filp->f_dentry->d_inode, filp, cmd, arg);
+	unlock_kernel();
+
+	return ret;
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_irq.c b/drivers/gpu/drm/nouveau/nouveau_irq.c
new file mode 100644
index 0000000..370c72c
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_irq.c
@@ -0,0 +1,702 @@
+/*
+ * Copyright (C) 2006 Ben Skeggs.
+ *
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/*
+ * Authors:
+ *   Ben Skeggs <darktama@iinet.net.au>
+ */
+
+#include "drmP.h"
+#include "drm.h"
+#include "nouveau_drm.h"
+#include "nouveau_drv.h"
+#include "nouveau_reg.h"
+#include <linux/ratelimit.h>
+
+/* needed for hotplug irq */
+#include "nouveau_connector.h"
+#include "nv50_display.h"
+
+void
+nouveau_irq_preinstall(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	/* Master disable */
+	nv_wr32(dev, NV03_PMC_INTR_EN_0, 0);
+
+	if (dev_priv->card_type == NV_50) {
+		INIT_WORK(&dev_priv->irq_work, nv50_display_irq_handler_bh);
+		INIT_LIST_HEAD(&dev_priv->vbl_waiting);
+	}
+}
+
+int
+nouveau_irq_postinstall(struct drm_device *dev)
+{
+	/* Master enable */
+	nv_wr32(dev, NV03_PMC_INTR_EN_0, NV_PMC_INTR_EN_0_MASTER_ENABLE);
+	return 0;
+}
+
+void
+nouveau_irq_uninstall(struct drm_device *dev)
+{
+	/* Master disable */
+	nv_wr32(dev, NV03_PMC_INTR_EN_0, 0);
+}
+
+static int
+nouveau_call_method(struct nouveau_channel *chan, int class, int mthd, int data)
+{
+	struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
+	struct nouveau_pgraph_object_method *grm;
+	struct nouveau_pgraph_object_class *grc;
+
+	grc = dev_priv->engine.graph.grclass;
+	while (grc->id) {
+		if (grc->id == class)
+			break;
+		grc++;
+	}
+
+	if (grc->id != class || !grc->methods)
+		return -ENOENT;
+
+	grm = grc->methods;
+	while (grm->id) {
+		if (grm->id == mthd)
+			return grm->exec(chan, class, mthd, data);
+		grm++;
+	}
+
+	return -ENOENT;
+}
+
+static bool
+nouveau_fifo_swmthd(struct nouveau_channel *chan, uint32_t addr, uint32_t data)
+{
+	struct drm_device *dev = chan->dev;
+	const int subc = (addr >> 13) & 0x7;
+	const int mthd = addr & 0x1ffc;
+
+	if (mthd == 0x0000) {
+		struct nouveau_gpuobj_ref *ref = NULL;
+
+		if (nouveau_gpuobj_ref_find(chan, data, &ref))
+			return false;
+
+		if (ref->gpuobj->engine != NVOBJ_ENGINE_SW)
+			return false;
+
+		chan->sw_subchannel[subc] = ref->gpuobj->class;
+		nv_wr32(dev, NV04_PFIFO_CACHE1_ENGINE, nv_rd32(dev,
+			NV04_PFIFO_CACHE1_ENGINE) & ~(0xf << subc * 4));
+		return true;
+	}
+
+	/* hw object */
+	if (nv_rd32(dev, NV04_PFIFO_CACHE1_ENGINE) & (1 << (subc*4)))
+		return false;
+
+	if (nouveau_call_method(chan, chan->sw_subchannel[subc], mthd, data))
+		return false;
+
+	return true;
+}
+
+static void
+nouveau_fifo_irq_handler(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_engine *engine = &dev_priv->engine;
+	uint32_t status, reassign;
+	int cnt = 0;
+
+	reassign = nv_rd32(dev, NV03_PFIFO_CACHES) & 1;
+	while ((status = nv_rd32(dev, NV03_PFIFO_INTR_0)) && (cnt++ < 100)) {
+		struct nouveau_channel *chan = NULL;
+		uint32_t chid, get;
+
+		nv_wr32(dev, NV03_PFIFO_CACHES, 0);
+
+		chid = engine->fifo.channel_id(dev);
+		if (chid >= 0 && chid < engine->fifo.channels)
+			chan = dev_priv->fifos[chid];
+		get  = nv_rd32(dev, NV03_PFIFO_CACHE1_GET);
+
+		if (status & NV_PFIFO_INTR_CACHE_ERROR) {
+			uint32_t mthd, data;
+			int ptr;
+
+			/* NV_PFIFO_CACHE1_GET actually goes to 0xffc before
+			 * wrapping on my G80 chips, but CACHE1 isn't big
+			 * enough for this much data.. Tests show that it
+			 * wraps around to the start at GET=0x800.. No clue
+			 * as to why..
+			 */
+			ptr = (get & 0x7ff) >> 2;
+
+			if (dev_priv->card_type < NV_40) {
+				mthd = nv_rd32(dev,
+					NV04_PFIFO_CACHE1_METHOD(ptr));
+				data = nv_rd32(dev,
+					NV04_PFIFO_CACHE1_DATA(ptr));
+			} else {
+				mthd = nv_rd32(dev,
+					NV40_PFIFO_CACHE1_METHOD(ptr));
+				data = nv_rd32(dev,
+					NV40_PFIFO_CACHE1_DATA(ptr));
+			}
+
+			if (!chan || !nouveau_fifo_swmthd(chan, mthd, data)) {
+				NV_INFO(dev, "PFIFO_CACHE_ERROR - Ch %d/%d "
+					     "Mthd 0x%04x Data 0x%08x\n",
+					chid, (mthd >> 13) & 7, mthd & 0x1ffc,
+					data);
+			}
+
+			nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 0);
+			nv_wr32(dev, NV03_PFIFO_INTR_0,
+						NV_PFIFO_INTR_CACHE_ERROR);
+
+			nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0,
+				nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH0) & ~1);
+			nv_wr32(dev, NV03_PFIFO_CACHE1_GET, get + 4);
+			nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0,
+				nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH0) | 1);
+			nv_wr32(dev, NV04_PFIFO_CACHE1_HASH, 0);
+
+			nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUSH,
+				nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUSH) | 1);
+			nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, 1);
+
+			status &= ~NV_PFIFO_INTR_CACHE_ERROR;
+		}
+
+		if (status & NV_PFIFO_INTR_DMA_PUSHER) {
+			NV_INFO(dev, "PFIFO_DMA_PUSHER - Ch %d\n", chid);
+
+			status &= ~NV_PFIFO_INTR_DMA_PUSHER;
+			nv_wr32(dev, NV03_PFIFO_INTR_0,
+						NV_PFIFO_INTR_DMA_PUSHER);
+
+			nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_STATE, 0x00000000);
+			if (nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUT) != get)
+				nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_GET,
+								get + 4);
+		}
+
+		if (status) {
+			NV_INFO(dev, "PFIFO_INTR 0x%08x - Ch %d\n",
+				status, chid);
+			nv_wr32(dev, NV03_PFIFO_INTR_0, status);
+			status = 0;
+		}
+
+		nv_wr32(dev, NV03_PFIFO_CACHES, reassign);
+	}
+
+	if (status) {
+		NV_INFO(dev, "PFIFO still angry after %d spins, halt\n", cnt);
+		nv_wr32(dev, 0x2140, 0);
+		nv_wr32(dev, 0x140, 0);
+	}
+
+	nv_wr32(dev, NV03_PMC_INTR_0, NV_PMC_INTR_0_PFIFO_PENDING);
+}
+
+struct nouveau_bitfield_names {
+	uint32_t mask;
+	const char *name;
+};
+
+static struct nouveau_bitfield_names nstatus_names[] =
+{
+	{ NV04_PGRAPH_NSTATUS_STATE_IN_USE,       "STATE_IN_USE" },
+	{ NV04_PGRAPH_NSTATUS_INVALID_STATE,      "INVALID_STATE" },
+	{ NV04_PGRAPH_NSTATUS_BAD_ARGUMENT,       "BAD_ARGUMENT" },
+	{ NV04_PGRAPH_NSTATUS_PROTECTION_FAULT,   "PROTECTION_FAULT" }
+};
+
+static struct nouveau_bitfield_names nstatus_names_nv10[] =
+{
+	{ NV10_PGRAPH_NSTATUS_STATE_IN_USE,       "STATE_IN_USE" },
+	{ NV10_PGRAPH_NSTATUS_INVALID_STATE,      "INVALID_STATE" },
+	{ NV10_PGRAPH_NSTATUS_BAD_ARGUMENT,       "BAD_ARGUMENT" },
+	{ NV10_PGRAPH_NSTATUS_PROTECTION_FAULT,   "PROTECTION_FAULT" }
+};
+
+static struct nouveau_bitfield_names nsource_names[] =
+{
+	{ NV03_PGRAPH_NSOURCE_NOTIFICATION,       "NOTIFICATION" },
+	{ NV03_PGRAPH_NSOURCE_DATA_ERROR,         "DATA_ERROR" },
+	{ NV03_PGRAPH_NSOURCE_PROTECTION_ERROR,   "PROTECTION_ERROR" },
+	{ NV03_PGRAPH_NSOURCE_RANGE_EXCEPTION,    "RANGE_EXCEPTION" },
+	{ NV03_PGRAPH_NSOURCE_LIMIT_COLOR,        "LIMIT_COLOR" },
+	{ NV03_PGRAPH_NSOURCE_LIMIT_ZETA,         "LIMIT_ZETA" },
+	{ NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD,       "ILLEGAL_MTHD" },
+	{ NV03_PGRAPH_NSOURCE_DMA_R_PROTECTION,   "DMA_R_PROTECTION" },
+	{ NV03_PGRAPH_NSOURCE_DMA_W_PROTECTION,   "DMA_W_PROTECTION" },
+	{ NV03_PGRAPH_NSOURCE_FORMAT_EXCEPTION,   "FORMAT_EXCEPTION" },
+	{ NV03_PGRAPH_NSOURCE_PATCH_EXCEPTION,    "PATCH_EXCEPTION" },
+	{ NV03_PGRAPH_NSOURCE_STATE_INVALID,      "STATE_INVALID" },
+	{ NV03_PGRAPH_NSOURCE_DOUBLE_NOTIFY,      "DOUBLE_NOTIFY" },
+	{ NV03_PGRAPH_NSOURCE_NOTIFY_IN_USE,      "NOTIFY_IN_USE" },
+	{ NV03_PGRAPH_NSOURCE_METHOD_CNT,         "METHOD_CNT" },
+	{ NV03_PGRAPH_NSOURCE_BFR_NOTIFICATION,   "BFR_NOTIFICATION" },
+	{ NV03_PGRAPH_NSOURCE_DMA_VTX_PROTECTION, "DMA_VTX_PROTECTION" },
+	{ NV03_PGRAPH_NSOURCE_DMA_WIDTH_A,        "DMA_WIDTH_A" },
+	{ NV03_PGRAPH_NSOURCE_DMA_WIDTH_B,        "DMA_WIDTH_B" },
+};
+
+static void
+nouveau_print_bitfield_names_(uint32_t value,
+				const struct nouveau_bitfield_names *namelist,
+				const int namelist_len)
+{
+	/*
+	 * Caller must have already printed the KERN_* log level for us.
+	 * Also the caller is responsible for adding the newline.
+	 */
+	int i;
+	for (i = 0; i < namelist_len; ++i) {
+		uint32_t mask = namelist[i].mask;
+		if (value & mask) {
+			printk(" %s", namelist[i].name);
+			value &= ~mask;
+		}
+	}
+	if (value)
+		printk(" (unknown bits 0x%08x)", value);
+}
+#define nouveau_print_bitfield_names(val, namelist) \
+	nouveau_print_bitfield_names_((val), (namelist), ARRAY_SIZE(namelist))
+
+
+static int
+nouveau_graph_chid_from_grctx(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	uint32_t inst;
+	int i;
+
+	if (dev_priv->card_type < NV_40)
+		return dev_priv->engine.fifo.channels;
+	else
+	if (dev_priv->card_type < NV_50) {
+		inst = (nv_rd32(dev, 0x40032c) & 0xfffff) << 4;
+
+		for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
+			struct nouveau_channel *chan = dev_priv->fifos[i];
+
+			if (!chan || !chan->ramin_grctx)
+				continue;
+
+			if (inst == chan->ramin_grctx->instance)
+				break;
+		}
+	} else {
+		inst = (nv_rd32(dev, 0x40032c) & 0xfffff) << 12;
+
+		for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
+			struct nouveau_channel *chan = dev_priv->fifos[i];
+
+			if (!chan || !chan->ramin)
+				continue;
+
+			if (inst == chan->ramin->instance)
+				break;
+		}
+	}
+
+
+	return i;
+}
+
+static int
+nouveau_graph_trapped_channel(struct drm_device *dev, int *channel_ret)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_engine *engine = &dev_priv->engine;
+	int channel;
+
+	if (dev_priv->card_type < NV_10)
+		channel = (nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR) >> 24) & 0xf;
+	else
+	if (dev_priv->card_type < NV_40)
+		channel = (nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR) >> 20) & 0x1f;
+	else
+		channel = nouveau_graph_chid_from_grctx(dev);
+
+	if (channel >= engine->fifo.channels || !dev_priv->fifos[channel]) {
+		NV_ERROR(dev, "AIII, invalid/inactive channel id %d\n", channel);
+		return -EINVAL;
+	}
+
+	*channel_ret = channel;
+	return 0;
+}
+
+struct nouveau_pgraph_trap {
+	int channel;
+	int class;
+	int subc, mthd, size;
+	uint32_t data, data2;
+	uint32_t nsource, nstatus;
+};
+
+static void
+nouveau_graph_trap_info(struct drm_device *dev,
+			struct nouveau_pgraph_trap *trap)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	uint32_t address;
+
+	trap->nsource = trap->nstatus = 0;
+	if (dev_priv->card_type < NV_50) {
+		trap->nsource = nv_rd32(dev, NV03_PGRAPH_NSOURCE);
+		trap->nstatus = nv_rd32(dev, NV03_PGRAPH_NSTATUS);
+	}
+
+	if (nouveau_graph_trapped_channel(dev, &trap->channel))
+		trap->channel = -1;
+	address = nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR);
+
+	trap->mthd = address & 0x1FFC;
+	trap->data = nv_rd32(dev, NV04_PGRAPH_TRAPPED_DATA);
+	if (dev_priv->card_type < NV_10) {
+		trap->subc  = (address >> 13) & 0x7;
+	} else {
+		trap->subc  = (address >> 16) & 0x7;
+		trap->data2 = nv_rd32(dev, NV10_PGRAPH_TRAPPED_DATA_HIGH);
+	}
+
+	if (dev_priv->card_type < NV_10)
+		trap->class = nv_rd32(dev, 0x400180 + trap->subc*4) & 0xFF;
+	else if (dev_priv->card_type < NV_40)
+		trap->class = nv_rd32(dev, 0x400160 + trap->subc*4) & 0xFFF;
+	else if (dev_priv->card_type < NV_50)
+		trap->class = nv_rd32(dev, 0x400160 + trap->subc*4) & 0xFFFF;
+	else
+		trap->class = nv_rd32(dev, 0x400814);
+}
+
+static void
+nouveau_graph_dump_trap_info(struct drm_device *dev, const char *id,
+			     struct nouveau_pgraph_trap *trap)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	uint32_t nsource = trap->nsource, nstatus = trap->nstatus;
+
+	NV_INFO(dev, "%s - nSource:", id);
+	nouveau_print_bitfield_names(nsource, nsource_names);
+	printk(", nStatus:");
+	if (dev_priv->card_type < NV_10)
+		nouveau_print_bitfield_names(nstatus, nstatus_names);
+	else
+		nouveau_print_bitfield_names(nstatus, nstatus_names_nv10);
+	printk("\n");
+
+	NV_INFO(dev, "%s - Ch %d/%d Class 0x%04x Mthd 0x%04x "
+					"Data 0x%08x:0x%08x\n",
+					id, trap->channel, trap->subc,
+					trap->class, trap->mthd,
+					trap->data2, trap->data);
+}
+
+static int
+nouveau_pgraph_intr_swmthd(struct drm_device *dev,
+			   struct nouveau_pgraph_trap *trap)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	if (trap->channel < 0 ||
+	    trap->channel >= dev_priv->engine.fifo.channels ||
+	    !dev_priv->fifos[trap->channel])
+		return -ENODEV;
+
+	return nouveau_call_method(dev_priv->fifos[trap->channel],
+				   trap->class, trap->mthd, trap->data);
+}
+
+static inline void
+nouveau_pgraph_intr_notify(struct drm_device *dev, uint32_t nsource)
+{
+	struct nouveau_pgraph_trap trap;
+	int unhandled = 0;
+
+	nouveau_graph_trap_info(dev, &trap);
+
+	if (nsource & NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD) {
+		if (nouveau_pgraph_intr_swmthd(dev, &trap))
+			unhandled = 1;
+	} else {
+		unhandled = 1;
+	}
+
+	if (unhandled)
+		nouveau_graph_dump_trap_info(dev, "PGRAPH_NOTIFY", &trap);
+}
+
+static DEFINE_RATELIMIT_STATE(nouveau_ratelimit_state, 3 * HZ, 20);
+
+static int nouveau_ratelimit(void)
+{
+	return __ratelimit(&nouveau_ratelimit_state);
+}
+
+
+static inline void
+nouveau_pgraph_intr_error(struct drm_device *dev, uint32_t nsource)
+{
+	struct nouveau_pgraph_trap trap;
+	int unhandled = 0;
+
+	nouveau_graph_trap_info(dev, &trap);
+	trap.nsource = nsource;
+
+	if (nsource & NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD) {
+		if (nouveau_pgraph_intr_swmthd(dev, &trap))
+			unhandled = 1;
+	} else {
+		unhandled = 1;
+	}
+
+	if (unhandled && nouveau_ratelimit())
+		nouveau_graph_dump_trap_info(dev, "PGRAPH_ERROR", &trap);
+}
+
+static inline void
+nouveau_pgraph_intr_context_switch(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_engine *engine = &dev_priv->engine;
+	uint32_t chid;
+
+	chid = engine->fifo.channel_id(dev);
+	NV_DEBUG(dev, "PGRAPH context switch interrupt channel %x\n", chid);
+
+	switch (dev_priv->card_type) {
+	case NV_04:
+		nv04_graph_context_switch(dev);
+		break;
+	case NV_10:
+		nv10_graph_context_switch(dev);
+		break;
+	default:
+		NV_ERROR(dev, "Context switch not implemented\n");
+		break;
+	}
+}
+
+static void
+nouveau_pgraph_irq_handler(struct drm_device *dev)
+{
+	uint32_t status;
+
+	while ((status = nv_rd32(dev, NV03_PGRAPH_INTR))) {
+		uint32_t nsource = nv_rd32(dev, NV03_PGRAPH_NSOURCE);
+
+		if (status & NV_PGRAPH_INTR_NOTIFY) {
+			nouveau_pgraph_intr_notify(dev, nsource);
+
+			status &= ~NV_PGRAPH_INTR_NOTIFY;
+			nv_wr32(dev, NV03_PGRAPH_INTR, NV_PGRAPH_INTR_NOTIFY);
+		}
+
+		if (status & NV_PGRAPH_INTR_ERROR) {
+			nouveau_pgraph_intr_error(dev, nsource);
+
+			status &= ~NV_PGRAPH_INTR_ERROR;
+			nv_wr32(dev, NV03_PGRAPH_INTR, NV_PGRAPH_INTR_ERROR);
+		}
+
+		if (status & NV_PGRAPH_INTR_CONTEXT_SWITCH) {
+			nouveau_pgraph_intr_context_switch(dev);
+
+			status &= ~NV_PGRAPH_INTR_CONTEXT_SWITCH;
+			nv_wr32(dev, NV03_PGRAPH_INTR,
+				 NV_PGRAPH_INTR_CONTEXT_SWITCH);
+		}
+
+		if (status) {
+			NV_INFO(dev, "Unhandled PGRAPH_INTR - 0x%08x\n", status);
+			nv_wr32(dev, NV03_PGRAPH_INTR, status);
+		}
+
+		if ((nv_rd32(dev, NV04_PGRAPH_FIFO) & (1 << 0)) == 0)
+			nv_wr32(dev, NV04_PGRAPH_FIFO, 1);
+	}
+
+	nv_wr32(dev, NV03_PMC_INTR_0, NV_PMC_INTR_0_PGRAPH_PENDING);
+}
+
+static void
+nv50_pgraph_irq_handler(struct drm_device *dev)
+{
+	uint32_t status, nsource;
+
+	status = nv_rd32(dev, NV03_PGRAPH_INTR);
+	nsource = nv_rd32(dev, NV03_PGRAPH_NSOURCE);
+
+	if (status & 0x00000001) {
+		nouveau_pgraph_intr_notify(dev, nsource);
+		status &= ~0x00000001;
+		nv_wr32(dev, NV03_PGRAPH_INTR, 0x00000001);
+	}
+
+	if (status & 0x00000010) {
+		nouveau_pgraph_intr_error(dev, nsource |
+					  NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD);
+
+		status &= ~0x00000010;
+		nv_wr32(dev, NV03_PGRAPH_INTR, 0x00000010);
+	}
+
+	if (status & 0x00001000) {
+		nv_wr32(dev, 0x400500, 0x00000000);
+		nv_wr32(dev, NV03_PGRAPH_INTR, NV_PGRAPH_INTR_CONTEXT_SWITCH);
+		nv_wr32(dev, NV40_PGRAPH_INTR_EN, nv_rd32(dev,
+			NV40_PGRAPH_INTR_EN) & ~NV_PGRAPH_INTR_CONTEXT_SWITCH);
+		nv_wr32(dev, 0x400500, 0x00010001);
+
+		nv50_graph_context_switch(dev);
+
+		status &= ~NV_PGRAPH_INTR_CONTEXT_SWITCH;
+	}
+
+	if (status & 0x00100000) {
+		nouveau_pgraph_intr_error(dev, nsource |
+					  NV03_PGRAPH_NSOURCE_DATA_ERROR);
+
+		status &= ~0x00100000;
+		nv_wr32(dev, NV03_PGRAPH_INTR, 0x00100000);
+	}
+
+	if (status & 0x00200000) {
+		int r;
+
+		nouveau_pgraph_intr_error(dev, nsource |
+					  NV03_PGRAPH_NSOURCE_PROTECTION_ERROR);
+
+		NV_ERROR(dev, "magic set 1:\n");
+		for (r = 0x408900; r <= 0x408910; r += 4)
+			NV_ERROR(dev, "\t0x%08x: 0x%08x\n", r, nv_rd32(dev, r));
+		nv_wr32(dev, 0x408900, nv_rd32(dev, 0x408904) | 0xc0000000);
+		for (r = 0x408e08; r <= 0x408e24; r += 4)
+			NV_ERROR(dev, "\t0x%08x: 0x%08x\n", r, nv_rd32(dev, r));
+		nv_wr32(dev, 0x408e08, nv_rd32(dev, 0x408e08) | 0xc0000000);
+
+		NV_ERROR(dev, "magic set 2:\n");
+		for (r = 0x409900; r <= 0x409910; r += 4)
+			NV_ERROR(dev, "\t0x%08x: 0x%08x\n", r, nv_rd32(dev, r));
+		nv_wr32(dev, 0x409900, nv_rd32(dev, 0x409904) | 0xc0000000);
+		for (r = 0x409e08; r <= 0x409e24; r += 4)
+			NV_ERROR(dev, "\t0x%08x: 0x%08x\n", r, nv_rd32(dev, r));
+		nv_wr32(dev, 0x409e08, nv_rd32(dev, 0x409e08) | 0xc0000000);
+
+		status &= ~0x00200000;
+		nv_wr32(dev, NV03_PGRAPH_NSOURCE, nsource);
+		nv_wr32(dev, NV03_PGRAPH_INTR, 0x00200000);
+	}
+
+	if (status) {
+		NV_INFO(dev, "Unhandled PGRAPH_INTR - 0x%08x\n", status);
+		nv_wr32(dev, NV03_PGRAPH_INTR, status);
+	}
+
+	{
+		const int isb = (1 << 16) | (1 << 0);
+
+		if ((nv_rd32(dev, 0x400500) & isb) != isb)
+			nv_wr32(dev, 0x400500, nv_rd32(dev, 0x400500) | isb);
+	}
+
+	nv_wr32(dev, NV03_PMC_INTR_0, NV_PMC_INTR_0_PGRAPH_PENDING);
+}
+
+static void
+nouveau_crtc_irq_handler(struct drm_device *dev, int crtc)
+{
+	if (crtc & 1)
+		nv_wr32(dev, NV_CRTC0_INTSTAT, NV_CRTC_INTR_VBLANK);
+
+	if (crtc & 2)
+		nv_wr32(dev, NV_CRTC1_INTSTAT, NV_CRTC_INTR_VBLANK);
+}
+
+irqreturn_t
+nouveau_irq_handler(DRM_IRQ_ARGS)
+{
+	struct drm_device *dev = (struct drm_device *)arg;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	uint32_t status, fbdev_flags = 0;
+
+	status = nv_rd32(dev, NV03_PMC_INTR_0);
+	if (!status)
+		return IRQ_NONE;
+
+	if (dev_priv->fbdev_info) {
+		fbdev_flags = dev_priv->fbdev_info->flags;
+		dev_priv->fbdev_info->flags |= FBINFO_HWACCEL_DISABLED;
+	}
+
+	if (status & NV_PMC_INTR_0_PFIFO_PENDING) {
+		nouveau_fifo_irq_handler(dev);
+		status &= ~NV_PMC_INTR_0_PFIFO_PENDING;
+	}
+
+	if (status & NV_PMC_INTR_0_PGRAPH_PENDING) {
+		if (dev_priv->card_type >= NV_50)
+			nv50_pgraph_irq_handler(dev);
+		else
+			nouveau_pgraph_irq_handler(dev);
+
+		status &= ~NV_PMC_INTR_0_PGRAPH_PENDING;
+	}
+
+	if (status & NV_PMC_INTR_0_CRTCn_PENDING) {
+		nouveau_crtc_irq_handler(dev, (status>>24)&3);
+		status &= ~NV_PMC_INTR_0_CRTCn_PENDING;
+	}
+
+	if (status & (NV_PMC_INTR_0_NV50_DISPLAY_PENDING |
+		      NV_PMC_INTR_0_NV50_I2C_PENDING)) {
+		nv50_display_irq_handler(dev);
+		status &= ~(NV_PMC_INTR_0_NV50_DISPLAY_PENDING |
+			    NV_PMC_INTR_0_NV50_I2C_PENDING);
+	}
+
+	if (status)
+		NV_ERROR(dev, "Unhandled PMC INTR status bits 0x%08x\n", status);
+
+	if (dev_priv->fbdev_info)
+		dev_priv->fbdev_info->flags = fbdev_flags;
+
+	return IRQ_HANDLED;
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c
new file mode 100644
index 0000000..0275571
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_mem.c
@@ -0,0 +1,568 @@
+/*
+ * Copyright (C) The Weather Channel, Inc.  2002.  All Rights Reserved.
+ * Copyright 2005 Stephane Marchesin
+ *
+ * The Weather Channel (TM) funded Tungsten Graphics to develop the
+ * initial release of the Radeon 8500 driver under the XFree86 license.
+ * This notice must be preserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Keith Whitwell <keith@tungstengraphics.com>
+ */
+
+
+#include "drmP.h"
+#include "drm.h"
+#include "drm_sarea.h"
+#include "nouveau_drv.h"
+
+static struct mem_block *
+split_block(struct mem_block *p, uint64_t start, uint64_t size,
+	    struct drm_file *file_priv)
+{
+	/* Maybe cut off the start of an existing block */
+	if (start > p->start) {
+		struct mem_block *newblock =
+			kmalloc(sizeof(*newblock), GFP_KERNEL);
+		if (!newblock)
+			goto out;
+		newblock->start = start;
+		newblock->size = p->size - (start - p->start);
+		newblock->file_priv = NULL;
+		newblock->next = p->next;
+		newblock->prev = p;
+		p->next->prev = newblock;
+		p->next = newblock;
+		p->size -= newblock->size;
+		p = newblock;
+	}
+
+	/* Maybe cut off the end of an existing block */
+	if (size < p->size) {
+		struct mem_block *newblock =
+			kmalloc(sizeof(*newblock), GFP_KERNEL);
+		if (!newblock)
+			goto out;
+		newblock->start = start + size;
+		newblock->size = p->size - size;
+		newblock->file_priv = NULL;
+		newblock->next = p->next;
+		newblock->prev = p;
+		p->next->prev = newblock;
+		p->next = newblock;
+		p->size = size;
+	}
+
+out:
+	/* Our block is in the middle */
+	p->file_priv = file_priv;
+	return p;
+}
+
+struct mem_block *
+nouveau_mem_alloc_block(struct mem_block *heap, uint64_t size,
+			int align2, struct drm_file *file_priv, int tail)
+{
+	struct mem_block *p;
+	uint64_t mask = (1 << align2) - 1;
+
+	if (!heap)
+		return NULL;
+
+	if (tail) {
+		list_for_each_prev(p, heap) {
+			uint64_t start = ((p->start + p->size) - size) & ~mask;
+
+			if (p->file_priv == NULL && start >= p->start &&
+			    start + size <= p->start + p->size)
+				return split_block(p, start, size, file_priv);
+		}
+	} else {
+		list_for_each(p, heap) {
+			uint64_t start = (p->start + mask) & ~mask;
+
+			if (p->file_priv == NULL &&
+			    start + size <= p->start + p->size)
+				return split_block(p, start, size, file_priv);
+		}
+	}
+
+	return NULL;
+}
+
+void nouveau_mem_free_block(struct mem_block *p)
+{
+	p->file_priv = NULL;
+
+	/* Assumes a single contiguous range.  Needs a special file_priv in
+	 * 'heap' to stop it being subsumed.
+	 */
+	if (p->next->file_priv == NULL) {
+		struct mem_block *q = p->next;
+		p->size += q->size;
+		p->next = q->next;
+		p->next->prev = p;
+		kfree(q);
+	}
+
+	if (p->prev->file_priv == NULL) {
+		struct mem_block *q = p->prev;
+		q->size += p->size;
+		q->next = p->next;
+		q->next->prev = q;
+		kfree(p);
+	}
+}
+
+/* Initialize.  How to check for an uninitialized heap?
+ */
+int nouveau_mem_init_heap(struct mem_block **heap, uint64_t start,
+			  uint64_t size)
+{
+	struct mem_block *blocks = kmalloc(sizeof(*blocks), GFP_KERNEL);
+
+	if (!blocks)
+		return -ENOMEM;
+
+	*heap = kmalloc(sizeof(**heap), GFP_KERNEL);
+	if (!*heap) {
+		kfree(blocks);
+		return -ENOMEM;
+	}
+
+	blocks->start = start;
+	blocks->size = size;
+	blocks->file_priv = NULL;
+	blocks->next = blocks->prev = *heap;
+
+	memset(*heap, 0, sizeof(**heap));
+	(*heap)->file_priv = (struct drm_file *) -1;
+	(*heap)->next = (*heap)->prev = blocks;
+	return 0;
+}
+
+/*
+ * Free all blocks associated with the releasing file_priv
+ */
+void nouveau_mem_release(struct drm_file *file_priv, struct mem_block *heap)
+{
+	struct mem_block *p;
+
+	if (!heap || !heap->next)
+		return;
+
+	list_for_each(p, heap) {
+		if (p->file_priv == file_priv)
+			p->file_priv = NULL;
+	}
+
+	/* Assumes a single contiguous range.  Needs a special file_priv in
+	 * 'heap' to stop it being subsumed.
+	 */
+	list_for_each(p, heap) {
+		while ((p->file_priv == NULL) &&
+					(p->next->file_priv == NULL) &&
+					(p->next != heap)) {
+			struct mem_block *q = p->next;
+			p->size += q->size;
+			p->next = q->next;
+			p->next->prev = p;
+			kfree(q);
+		}
+	}
+}
+
+/*
+ * NV50 VM helpers
+ */
+int
+nv50_mem_vm_bind_linear(struct drm_device *dev, uint64_t virt, uint32_t size,
+			uint32_t flags, uint64_t phys)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_gpuobj **pgt;
+	unsigned psz, pfl, pages;
+
+	if (virt >= dev_priv->vm_gart_base &&
+	    (virt + size) < (dev_priv->vm_gart_base + dev_priv->vm_gart_size)) {
+		psz = 12;
+		pgt = &dev_priv->gart_info.sg_ctxdma;
+		pfl = 0x21;
+		virt -= dev_priv->vm_gart_base;
+	} else
+	if (virt >= dev_priv->vm_vram_base &&
+	    (virt + size) < (dev_priv->vm_vram_base + dev_priv->vm_vram_size)) {
+		psz = 16;
+		pgt = dev_priv->vm_vram_pt;
+		pfl = 0x01;
+		virt -= dev_priv->vm_vram_base;
+	} else {
+		NV_ERROR(dev, "Invalid address: 0x%16llx-0x%16llx\n",
+			 virt, virt + size - 1);
+		return -EINVAL;
+	}
+
+	pages = size >> psz;
+
+	dev_priv->engine.instmem.prepare_access(dev, true);
+	if (flags & 0x80000000) {
+		while (pages--) {
+			struct nouveau_gpuobj *pt = pgt[virt >> 29];
+			unsigned pte = ((virt & 0x1fffffffULL) >> psz) << 1;
+
+			nv_wo32(dev, pt, pte++, 0x00000000);
+			nv_wo32(dev, pt, pte++, 0x00000000);
+
+			virt += (1 << psz);
+		}
+	} else {
+		while (pages--) {
+			struct nouveau_gpuobj *pt = pgt[virt >> 29];
+			unsigned pte = ((virt & 0x1fffffffULL) >> psz) << 1;
+			unsigned offset_h = upper_32_bits(phys) & 0xff;
+			unsigned offset_l = lower_32_bits(phys);
+
+			nv_wo32(dev, pt, pte++, offset_l | pfl);
+			nv_wo32(dev, pt, pte++, offset_h | flags);
+
+			phys += (1 << psz);
+			virt += (1 << psz);
+		}
+	}
+	dev_priv->engine.instmem.finish_access(dev);
+
+	nv_wr32(dev, 0x100c80, 0x00050001);
+	if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
+		NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
+		NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
+		return -EBUSY;
+	}
+
+	nv_wr32(dev, 0x100c80, 0x00000001);
+	if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
+		NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
+		NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+void
+nv50_mem_vm_unbind(struct drm_device *dev, uint64_t virt, uint32_t size)
+{
+	nv50_mem_vm_bind_linear(dev, virt, size, 0x80000000, 0);
+}
+
+/*
+ * Cleanup everything
+ */
+void nouveau_mem_takedown(struct mem_block **heap)
+{
+	struct mem_block *p;
+
+	if (!*heap)
+		return;
+
+	for (p = (*heap)->next; p != *heap;) {
+		struct mem_block *q = p;
+		p = p->next;
+		kfree(q);
+	}
+
+	kfree(*heap);
+	*heap = NULL;
+}
+
+void nouveau_mem_close(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	if (dev_priv->ttm.bdev.man[TTM_PL_PRIV0].has_type)
+		ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_PRIV0);
+	ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_VRAM);
+
+	ttm_bo_device_release(&dev_priv->ttm.bdev);
+
+	nouveau_ttm_global_release(dev_priv);
+
+	if (drm_core_has_AGP(dev) && dev->agp &&
+	    drm_core_check_feature(dev, DRIVER_MODESET)) {
+		struct drm_agp_mem *entry, *tempe;
+
+		/* Remove AGP resources, but leave dev->agp
+		   intact until drv_cleanup is called. */
+		list_for_each_entry_safe(entry, tempe, &dev->agp->memory, head) {
+			if (entry->bound)
+				drm_unbind_agp(entry->memory);
+			drm_free_agp(entry->memory, entry->pages);
+			kfree(entry);
+		}
+		INIT_LIST_HEAD(&dev->agp->memory);
+
+		if (dev->agp->acquired)
+			drm_agp_release(dev);
+
+		dev->agp->acquired = 0;
+		dev->agp->enabled = 0;
+	}
+
+	if (dev_priv->fb_mtrr) {
+		drm_mtrr_del(dev_priv->fb_mtrr, drm_get_resource_start(dev, 1),
+			     drm_get_resource_len(dev, 1), DRM_MTRR_WC);
+		dev_priv->fb_mtrr = 0;
+	}
+}
+
+/*XXX won't work on BSD because of pci_read_config_dword */
+static uint32_t
+nouveau_mem_fb_amount_igp(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct pci_dev *bridge;
+	uint32_t mem;
+
+	bridge = pci_get_bus_and_slot(0, PCI_DEVFN(0, 1));
+	if (!bridge) {
+		NV_ERROR(dev, "no bridge device\n");
+		return 0;
+	}
+
+	if (dev_priv->flags&NV_NFORCE) {
+		pci_read_config_dword(bridge, 0x7C, &mem);
+		return (uint64_t)(((mem >> 6) & 31) + 1)*1024*1024;
+	} else
+	if (dev_priv->flags&NV_NFORCE2) {
+		pci_read_config_dword(bridge, 0x84, &mem);
+		return (uint64_t)(((mem >> 4) & 127) + 1)*1024*1024;
+	}
+
+	NV_ERROR(dev, "impossible!\n");
+	return 0;
+}
+
+/* returns the amount of FB ram in bytes */
+uint64_t nouveau_mem_fb_amount(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	uint32_t boot0;
+
+	switch (dev_priv->card_type) {
+	case NV_04:
+		boot0 = nv_rd32(dev, NV03_BOOT_0);
+		if (boot0 & 0x00000100)
+			return (((boot0 >> 12) & 0xf) * 2 + 2) * 1024 * 1024;
+
+		switch (boot0 & NV03_BOOT_0_RAM_AMOUNT) {
+		case NV04_BOOT_0_RAM_AMOUNT_32MB:
+			return 32 * 1024 * 1024;
+		case NV04_BOOT_0_RAM_AMOUNT_16MB:
+			return 16 * 1024 * 1024;
+		case NV04_BOOT_0_RAM_AMOUNT_8MB:
+			return 8 * 1024 * 1024;
+		case NV04_BOOT_0_RAM_AMOUNT_4MB:
+			return 4 * 1024 * 1024;
+		}
+		break;
+	case NV_10:
+	case NV_20:
+	case NV_30:
+	case NV_40:
+	case NV_50:
+	default:
+		if (dev_priv->flags & (NV_NFORCE | NV_NFORCE2)) {
+			return nouveau_mem_fb_amount_igp(dev);
+		} else {
+			uint64_t mem;
+			mem = (nv_rd32(dev, NV04_FIFO_DATA) &
+					NV10_FIFO_DATA_RAM_AMOUNT_MB_MASK) >>
+					NV10_FIFO_DATA_RAM_AMOUNT_MB_SHIFT;
+			return mem * 1024 * 1024;
+		}
+		break;
+	}
+
+	NV_ERROR(dev,
+		"Unable to detect video ram size. Please report your setup to "
+							DRIVER_EMAIL "\n");
+	return 0;
+}
+
+static void nouveau_mem_reset_agp(struct drm_device *dev)
+{
+	uint32_t saved_pci_nv_1, saved_pci_nv_19, pmc_enable;
+
+	saved_pci_nv_1 = nv_rd32(dev, NV04_PBUS_PCI_NV_1);
+	saved_pci_nv_19 = nv_rd32(dev, NV04_PBUS_PCI_NV_19);
+
+	/* clear busmaster bit */
+	nv_wr32(dev, NV04_PBUS_PCI_NV_1, saved_pci_nv_1 & ~0x4);
+	/* clear SBA and AGP bits */
+	nv_wr32(dev, NV04_PBUS_PCI_NV_19, saved_pci_nv_19 & 0xfffff0ff);
+
+	/* power cycle pgraph, if enabled */
+	pmc_enable = nv_rd32(dev, NV03_PMC_ENABLE);
+	if (pmc_enable & NV_PMC_ENABLE_PGRAPH) {
+		nv_wr32(dev, NV03_PMC_ENABLE,
+				pmc_enable & ~NV_PMC_ENABLE_PGRAPH);
+		nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) |
+				NV_PMC_ENABLE_PGRAPH);
+	}
+
+	/* and restore (gives effect of resetting AGP) */
+	nv_wr32(dev, NV04_PBUS_PCI_NV_19, saved_pci_nv_19);
+	nv_wr32(dev, NV04_PBUS_PCI_NV_1, saved_pci_nv_1);
+}
+
+int
+nouveau_mem_init_agp(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct drm_agp_info info;
+	struct drm_agp_mode mode;
+	int ret;
+
+	if (nouveau_noagp)
+		return 0;
+
+	nouveau_mem_reset_agp(dev);
+
+	if (!dev->agp->acquired) {
+		ret = drm_agp_acquire(dev);
+		if (ret) {
+			NV_ERROR(dev, "Unable to acquire AGP: %d\n", ret);
+			return ret;
+		}
+	}
+
+	ret = drm_agp_info(dev, &info);
+	if (ret) {
+		NV_ERROR(dev, "Unable to get AGP info: %d\n", ret);
+		return ret;
+	}
+
+	/* see agp.h for the AGPSTAT_* modes available */
+	mode.mode = info.mode;
+	ret = drm_agp_enable(dev, mode);
+	if (ret) {
+		NV_ERROR(dev, "Unable to enable AGP: %d\n", ret);
+		return ret;
+	}
+
+	dev_priv->gart_info.type	= NOUVEAU_GART_AGP;
+	dev_priv->gart_info.aper_base	= info.aperture_base;
+	dev_priv->gart_info.aper_size	= info.aperture_size;
+	return 0;
+}
+
+int
+nouveau_mem_init(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct ttm_bo_device *bdev = &dev_priv->ttm.bdev;
+	int ret, dma_bits = 32;
+
+	dev_priv->fb_phys = drm_get_resource_start(dev, 1);
+	dev_priv->gart_info.type = NOUVEAU_GART_NONE;
+
+	if (dev_priv->card_type >= NV_50 &&
+	    pci_dma_supported(dev->pdev, DMA_BIT_MASK(40)))
+		dma_bits = 40;
+
+	ret = pci_set_dma_mask(dev->pdev, DMA_BIT_MASK(dma_bits));
+	if (ret) {
+		NV_ERROR(dev, "Error setting DMA mask: %d\n", ret);
+		return ret;
+	}
+
+	ret = nouveau_ttm_global_init(dev_priv);
+	if (ret)
+		return ret;
+
+	ret = ttm_bo_device_init(&dev_priv->ttm.bdev,
+				 dev_priv->ttm.bo_global_ref.ref.object,
+				 &nouveau_bo_driver, DRM_FILE_PAGE_OFFSET,
+				 dma_bits <= 32 ? true : false);
+	if (ret) {
+		NV_ERROR(dev, "Error initialising bo driver: %d\n", ret);
+		return ret;
+	}
+
+	INIT_LIST_HEAD(&dev_priv->ttm.bo_list);
+	spin_lock_init(&dev_priv->ttm.bo_list_lock);
+
+	dev_priv->fb_available_size = nouveau_mem_fb_amount(dev);
+
+	dev_priv->fb_mappable_pages = dev_priv->fb_available_size;
+	if (dev_priv->fb_mappable_pages > drm_get_resource_len(dev, 1))
+		dev_priv->fb_mappable_pages = drm_get_resource_len(dev, 1);
+	dev_priv->fb_mappable_pages >>= PAGE_SHIFT;
+
+	NV_INFO(dev, "%d MiB VRAM\n", (int)(dev_priv->fb_available_size >> 20));
+
+	/* remove reserved space at end of vram from available amount */
+	dev_priv->fb_available_size -= dev_priv->ramin_rsvd_vram;
+	dev_priv->fb_aper_free = dev_priv->fb_available_size;
+
+	/* mappable vram */
+	ret = ttm_bo_init_mm(bdev, TTM_PL_VRAM,
+			     dev_priv->fb_available_size >> PAGE_SHIFT);
+	if (ret) {
+		NV_ERROR(dev, "Failed VRAM mm init: %d\n", ret);
+		return ret;
+	}
+
+	/* GART */
+#if !defined(__powerpc__) && !defined(__ia64__)
+	if (drm_device_is_agp(dev) && dev->agp) {
+		ret = nouveau_mem_init_agp(dev);
+		if (ret)
+			NV_ERROR(dev, "Error initialising AGP: %d\n", ret);
+	}
+#endif
+
+	if (dev_priv->gart_info.type == NOUVEAU_GART_NONE) {
+		ret = nouveau_sgdma_init(dev);
+		if (ret) {
+			NV_ERROR(dev, "Error initialising PCI(E): %d\n", ret);
+			return ret;
+		}
+	}
+
+	NV_INFO(dev, "%d MiB GART (aperture)\n",
+		(int)(dev_priv->gart_info.aper_size >> 20));
+	dev_priv->gart_info.aper_free = dev_priv->gart_info.aper_size;
+
+	ret = ttm_bo_init_mm(bdev, TTM_PL_TT,
+			     dev_priv->gart_info.aper_size >> PAGE_SHIFT);
+	if (ret) {
+		NV_ERROR(dev, "Failed TT mm init: %d\n", ret);
+		return ret;
+	}
+
+	dev_priv->fb_mtrr = drm_mtrr_add(drm_get_resource_start(dev, 1),
+					 drm_get_resource_len(dev, 1),
+					 DRM_MTRR_WC);
+	return 0;
+}
+
+
diff --git a/drivers/gpu/drm/nouveau/nouveau_notifier.c b/drivers/gpu/drm/nouveau/nouveau_notifier.c
new file mode 100644
index 0000000..6c66a34
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_notifier.c
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2007 Ben Skeggs.
+ *
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "drmP.h"
+#include "drm.h"
+#include "nouveau_drv.h"
+
+int
+nouveau_notifier_init_channel(struct nouveau_channel *chan)
+{
+	struct drm_device *dev = chan->dev;
+	struct nouveau_bo *ntfy = NULL;
+	int ret;
+
+	ret = nouveau_gem_new(dev, NULL, PAGE_SIZE, 0, nouveau_vram_notify ?
+			      TTM_PL_FLAG_VRAM : TTM_PL_FLAG_TT,
+			      0, 0x0000, false, true, &ntfy);
+	if (ret)
+		return ret;
+
+	ret = nouveau_bo_pin(ntfy, TTM_PL_FLAG_VRAM);
+	if (ret)
+		goto out_err;
+
+	ret = nouveau_bo_map(ntfy);
+	if (ret)
+		goto out_err;
+
+	ret = nouveau_mem_init_heap(&chan->notifier_heap, 0, ntfy->bo.mem.size);
+	if (ret)
+		goto out_err;
+
+	chan->notifier_bo = ntfy;
+out_err:
+	if (ret) {
+		mutex_lock(&dev->struct_mutex);
+		drm_gem_object_unreference(ntfy->gem);
+		mutex_unlock(&dev->struct_mutex);
+	}
+
+	return ret;
+}
+
+void
+nouveau_notifier_takedown_channel(struct nouveau_channel *chan)
+{
+	struct drm_device *dev = chan->dev;
+
+	if (!chan->notifier_bo)
+		return;
+
+	nouveau_bo_unmap(chan->notifier_bo);
+	mutex_lock(&dev->struct_mutex);
+	nouveau_bo_unpin(chan->notifier_bo);
+	drm_gem_object_unreference(chan->notifier_bo->gem);
+	mutex_unlock(&dev->struct_mutex);
+	nouveau_mem_takedown(&chan->notifier_heap);
+}
+
+static void
+nouveau_notifier_gpuobj_dtor(struct drm_device *dev,
+			     struct nouveau_gpuobj *gpuobj)
+{
+	NV_DEBUG(dev, "\n");
+
+	if (gpuobj->priv)
+		nouveau_mem_free_block(gpuobj->priv);
+}
+
+int
+nouveau_notifier_alloc(struct nouveau_channel *chan, uint32_t handle,
+		       int size, uint32_t *b_offset)
+{
+	struct drm_device *dev = chan->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_gpuobj *nobj = NULL;
+	struct mem_block *mem;
+	uint32_t offset;
+	int target, ret;
+
+	if (!chan->notifier_heap) {
+		NV_ERROR(dev, "Channel %d doesn't have a notifier heap!\n",
+			 chan->id);
+		return -EINVAL;
+	}
+
+	mem = nouveau_mem_alloc_block(chan->notifier_heap, size, 0,
+				      (struct drm_file *)-2, 0);
+	if (!mem) {
+		NV_ERROR(dev, "Channel %d notifier block full\n", chan->id);
+		return -ENOMEM;
+	}
+
+	offset = chan->notifier_bo->bo.mem.mm_node->start << PAGE_SHIFT;
+	if (chan->notifier_bo->bo.mem.mem_type == TTM_PL_VRAM) {
+		target = NV_DMA_TARGET_VIDMEM;
+	} else
+	if (chan->notifier_bo->bo.mem.mem_type == TTM_PL_TT) {
+		if (dev_priv->gart_info.type == NOUVEAU_GART_SGDMA &&
+		    dev_priv->card_type < NV_50) {
+			ret = nouveau_sgdma_get_page(dev, offset, &offset);
+			if (ret)
+				return ret;
+			target = NV_DMA_TARGET_PCI;
+		} else {
+			target = NV_DMA_TARGET_AGP;
+		}
+	} else {
+		NV_ERROR(dev, "Bad DMA target, mem_type %d!\n",
+			 chan->notifier_bo->bo.mem.mem_type);
+		return -EINVAL;
+	}
+	offset += mem->start;
+
+	ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, offset,
+				     mem->size, NV_DMA_ACCESS_RW, target,
+				     &nobj);
+	if (ret) {
+		nouveau_mem_free_block(mem);
+		NV_ERROR(dev, "Error creating notifier ctxdma: %d\n", ret);
+		return ret;
+	}
+	nobj->dtor   = nouveau_notifier_gpuobj_dtor;
+	nobj->priv   = mem;
+
+	ret = nouveau_gpuobj_ref_add(dev, chan, handle, nobj, NULL);
+	if (ret) {
+		nouveau_gpuobj_del(dev, &nobj);
+		nouveau_mem_free_block(mem);
+		NV_ERROR(dev, "Error referencing notifier ctxdma: %d\n", ret);
+		return ret;
+	}
+
+	*b_offset = mem->start;
+	return 0;
+}
+
+int
+nouveau_notifier_offset(struct nouveau_gpuobj *nobj, uint32_t *poffset)
+{
+	if (!nobj || nobj->dtor != nouveau_notifier_gpuobj_dtor)
+		return -EINVAL;
+
+	if (poffset) {
+		struct mem_block *mem = nobj->priv;
+
+		if (*poffset >= mem->size)
+			return false;
+
+		*poffset += mem->start;
+	}
+
+	return 0;
+}
+
+int
+nouveau_ioctl_notifier_alloc(struct drm_device *dev, void *data,
+			     struct drm_file *file_priv)
+{
+	struct drm_nouveau_notifierobj_alloc *na = data;
+	struct nouveau_channel *chan;
+	int ret;
+
+	NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
+	NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(na->channel, file_priv, chan);
+
+	ret = nouveau_notifier_alloc(chan, na->handle, na->size, &na->offset);
+	if (ret)
+		return ret;
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_object.c b/drivers/gpu/drm/nouveau/nouveau_object.c
new file mode 100644
index 0000000..93379bb
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_object.c
@@ -0,0 +1,1294 @@
+/*
+ * Copyright (C) 2006 Ben Skeggs.
+ *
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+/*
+ * Authors:
+ *   Ben Skeggs <darktama@iinet.net.au>
+ */
+
+#include "drmP.h"
+#include "drm.h"
+#include "nouveau_drv.h"
+#include "nouveau_drm.h"
+
+/* NVidia uses context objects to drive drawing operations.
+
+   Context objects can be selected into 8 subchannels in the FIFO,
+   and then used via DMA command buffers.
+
+   A context object is referenced by a user defined handle (CARD32). The HW
+   looks up graphics objects in a hash table in the instance RAM.
+
+   An entry in the hash table consists of 2 CARD32. The first CARD32 contains
+   the handle, the second one a bitfield, that contains the address of the
+   object in instance RAM.
+
+   The format of the second CARD32 seems to be:
+
+   NV4 to NV30:
+
+   15: 0  instance_addr >> 4
+   17:16  engine (here uses 1 = graphics)
+   28:24  channel id (here uses 0)
+   31	  valid (use 1)
+
+   NV40:
+
+   15: 0  instance_addr >> 4   (maybe 19-0)
+   21:20  engine (here uses 1 = graphics)
+   I'm unsure about the other bits, but using 0 seems to work.
+
+   The key into the hash table depends on the object handle and channel id and
+   is given as:
+*/
+static uint32_t
+nouveau_ramht_hash_handle(struct drm_device *dev, int channel, uint32_t handle)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	uint32_t hash = 0;
+	int i;
+
+	NV_DEBUG(dev, "ch%d handle=0x%08x\n", channel, handle);
+
+	for (i = 32; i > 0; i -= dev_priv->ramht_bits) {
+		hash ^= (handle & ((1 << dev_priv->ramht_bits) - 1));
+		handle >>= dev_priv->ramht_bits;
+	}
+
+	if (dev_priv->card_type < NV_50)
+		hash ^= channel << (dev_priv->ramht_bits - 4);
+	hash <<= 3;
+
+	NV_DEBUG(dev, "hash=0x%08x\n", hash);
+	return hash;
+}
+
+static int
+nouveau_ramht_entry_valid(struct drm_device *dev, struct nouveau_gpuobj *ramht,
+			  uint32_t offset)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	uint32_t ctx = nv_ro32(dev, ramht, (offset + 4)/4);
+
+	if (dev_priv->card_type < NV_40)
+		return ((ctx & NV_RAMHT_CONTEXT_VALID) != 0);
+	return (ctx != 0);
+}
+
+static int
+nouveau_ramht_insert(struct drm_device *dev, struct nouveau_gpuobj_ref *ref)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem;
+	struct nouveau_channel *chan = ref->channel;
+	struct nouveau_gpuobj *ramht = chan->ramht ? chan->ramht->gpuobj : NULL;
+	uint32_t ctx, co, ho;
+
+	if (!ramht) {
+		NV_ERROR(dev, "No hash table!\n");
+		return -EINVAL;
+	}
+
+	if (dev_priv->card_type < NV_40) {
+		ctx = NV_RAMHT_CONTEXT_VALID | (ref->instance >> 4) |
+		      (chan->id << NV_RAMHT_CONTEXT_CHANNEL_SHIFT) |
+		      (ref->gpuobj->engine << NV_RAMHT_CONTEXT_ENGINE_SHIFT);
+	} else
+	if (dev_priv->card_type < NV_50) {
+		ctx = (ref->instance >> 4) |
+		      (chan->id << NV40_RAMHT_CONTEXT_CHANNEL_SHIFT) |
+		      (ref->gpuobj->engine << NV40_RAMHT_CONTEXT_ENGINE_SHIFT);
+	} else {
+		if (ref->gpuobj->engine == NVOBJ_ENGINE_DISPLAY) {
+			ctx = (ref->instance << 10) | 2;
+		} else {
+			ctx = (ref->instance >> 4) |
+			      ((ref->gpuobj->engine <<
+				NV40_RAMHT_CONTEXT_ENGINE_SHIFT));
+		}
+	}
+
+	instmem->prepare_access(dev, true);
+	co = ho = nouveau_ramht_hash_handle(dev, chan->id, ref->handle);
+	do {
+		if (!nouveau_ramht_entry_valid(dev, ramht, co)) {
+			NV_DEBUG(dev,
+				 "insert ch%d 0x%08x: h=0x%08x, c=0x%08x\n",
+				 chan->id, co, ref->handle, ctx);
+			nv_wo32(dev, ramht, (co + 0)/4, ref->handle);
+			nv_wo32(dev, ramht, (co + 4)/4, ctx);
+
+			list_add_tail(&ref->list, &chan->ramht_refs);
+			instmem->finish_access(dev);
+			return 0;
+		}
+		NV_DEBUG(dev, "collision ch%d 0x%08x: h=0x%08x\n",
+			 chan->id, co, nv_ro32(dev, ramht, co/4));
+
+		co += 8;
+		if (co >= dev_priv->ramht_size)
+			co = 0;
+	} while (co != ho);
+	instmem->finish_access(dev);
+
+	NV_ERROR(dev, "RAMHT space exhausted. ch=%d\n", chan->id);
+	return -ENOMEM;
+}
+
+static void
+nouveau_ramht_remove(struct drm_device *dev, struct nouveau_gpuobj_ref *ref)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem;
+	struct nouveau_channel *chan = ref->channel;
+	struct nouveau_gpuobj *ramht = chan->ramht ? chan->ramht->gpuobj : NULL;
+	uint32_t co, ho;
+
+	if (!ramht) {
+		NV_ERROR(dev, "No hash table!\n");
+		return;
+	}
+
+	instmem->prepare_access(dev, true);
+	co = ho = nouveau_ramht_hash_handle(dev, chan->id, ref->handle);
+	do {
+		if (nouveau_ramht_entry_valid(dev, ramht, co) &&
+		    (ref->handle == nv_ro32(dev, ramht, (co/4)))) {
+			NV_DEBUG(dev,
+				 "remove ch%d 0x%08x: h=0x%08x, c=0x%08x\n",
+				 chan->id, co, ref->handle,
+				 nv_ro32(dev, ramht, (co + 4)));
+			nv_wo32(dev, ramht, (co + 0)/4, 0x00000000);
+			nv_wo32(dev, ramht, (co + 4)/4, 0x00000000);
+
+			list_del(&ref->list);
+			instmem->finish_access(dev);
+			return;
+		}
+
+		co += 8;
+		if (co >= dev_priv->ramht_size)
+			co = 0;
+	} while (co != ho);
+	list_del(&ref->list);
+	instmem->finish_access(dev);
+
+	NV_ERROR(dev, "RAMHT entry not found. ch=%d, handle=0x%08x\n",
+		 chan->id, ref->handle);
+}
+
+int
+nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan,
+		   uint32_t size, int align, uint32_t flags,
+		   struct nouveau_gpuobj **gpuobj_ret)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_engine *engine = &dev_priv->engine;
+	struct nouveau_gpuobj *gpuobj;
+	struct mem_block *pramin = NULL;
+	int ret;
+
+	NV_DEBUG(dev, "ch%d size=%u align=%d flags=0x%08x\n",
+		 chan ? chan->id : -1, size, align, flags);
+
+	if (!dev_priv || !gpuobj_ret || *gpuobj_ret != NULL)
+		return -EINVAL;
+
+	gpuobj = kzalloc(sizeof(*gpuobj), GFP_KERNEL);
+	if (!gpuobj)
+		return -ENOMEM;
+	NV_DEBUG(dev, "gpuobj %p\n", gpuobj);
+	gpuobj->flags = flags;
+	gpuobj->im_channel = chan;
+
+	list_add_tail(&gpuobj->list, &dev_priv->gpuobj_list);
+
+	/* Choose between global instmem heap, and per-channel private
+	 * instmem heap.  On <NV50 allow requests for private instmem
+	 * to be satisfied from global heap if no per-channel area
+	 * available.
+	 */
+	if (chan) {
+		if (chan->ramin_heap) {
+			NV_DEBUG(dev, "private heap\n");
+			pramin = chan->ramin_heap;
+		} else
+		if (dev_priv->card_type < NV_50) {
+			NV_DEBUG(dev, "global heap fallback\n");
+			pramin = dev_priv->ramin_heap;
+		}
+	} else {
+		NV_DEBUG(dev, "global heap\n");
+		pramin = dev_priv->ramin_heap;
+	}
+
+	if (!pramin) {
+		NV_ERROR(dev, "No PRAMIN heap!\n");
+		return -EINVAL;
+	}
+
+	if (!chan) {
+		ret = engine->instmem.populate(dev, gpuobj, &size);
+		if (ret) {
+			nouveau_gpuobj_del(dev, &gpuobj);
+			return ret;
+		}
+	}
+
+	/* Allocate a chunk of the PRAMIN aperture */
+	gpuobj->im_pramin = nouveau_mem_alloc_block(pramin, size,
+						    drm_order(align),
+						    (struct drm_file *)-2, 0);
+	if (!gpuobj->im_pramin) {
+		nouveau_gpuobj_del(dev, &gpuobj);
+		return -ENOMEM;
+	}
+
+	if (!chan) {
+		ret = engine->instmem.bind(dev, gpuobj);
+		if (ret) {
+			nouveau_gpuobj_del(dev, &gpuobj);
+			return ret;
+		}
+	}
+
+	if (gpuobj->flags & NVOBJ_FLAG_ZERO_ALLOC) {
+		int i;
+
+		engine->instmem.prepare_access(dev, true);
+		for (i = 0; i < gpuobj->im_pramin->size; i += 4)
+			nv_wo32(dev, gpuobj, i/4, 0);
+		engine->instmem.finish_access(dev);
+	}
+
+	*gpuobj_ret = gpuobj;
+	return 0;
+}
+
+int
+nouveau_gpuobj_early_init(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	NV_DEBUG(dev, "\n");
+
+	INIT_LIST_HEAD(&dev_priv->gpuobj_list);
+
+	return 0;
+}
+
+int
+nouveau_gpuobj_init(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	int ret;
+
+	NV_DEBUG(dev, "\n");
+
+	if (dev_priv->card_type < NV_50) {
+		ret = nouveau_gpuobj_new_fake(dev,
+			dev_priv->ramht_offset, ~0, dev_priv->ramht_size,
+			NVOBJ_FLAG_ZERO_ALLOC | NVOBJ_FLAG_ALLOW_NO_REFS,
+						&dev_priv->ramht, NULL);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+void
+nouveau_gpuobj_takedown(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	NV_DEBUG(dev, "\n");
+
+	nouveau_gpuobj_del(dev, &dev_priv->ramht);
+}
+
+void
+nouveau_gpuobj_late_takedown(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_gpuobj *gpuobj = NULL;
+	struct list_head *entry, *tmp;
+
+	NV_DEBUG(dev, "\n");
+
+	list_for_each_safe(entry, tmp, &dev_priv->gpuobj_list) {
+		gpuobj = list_entry(entry, struct nouveau_gpuobj, list);
+
+		NV_ERROR(dev, "gpuobj %p still exists at takedown, refs=%d\n",
+			 gpuobj, gpuobj->refcount);
+		gpuobj->refcount = 0;
+		nouveau_gpuobj_del(dev, &gpuobj);
+	}
+}
+
+int
+nouveau_gpuobj_del(struct drm_device *dev, struct nouveau_gpuobj **pgpuobj)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_engine *engine = &dev_priv->engine;
+	struct nouveau_gpuobj *gpuobj;
+	int i;
+
+	NV_DEBUG(dev, "gpuobj %p\n", pgpuobj ? *pgpuobj : NULL);
+
+	if (!dev_priv || !pgpuobj || !(*pgpuobj))
+		return -EINVAL;
+	gpuobj = *pgpuobj;
+
+	if (gpuobj->refcount != 0) {
+		NV_ERROR(dev, "gpuobj refcount is %d\n", gpuobj->refcount);
+		return -EINVAL;
+	}
+
+	if (gpuobj->im_pramin && (gpuobj->flags & NVOBJ_FLAG_ZERO_FREE)) {
+		engine->instmem.prepare_access(dev, true);
+		for (i = 0; i < gpuobj->im_pramin->size; i += 4)
+			nv_wo32(dev, gpuobj, i/4, 0);
+		engine->instmem.finish_access(dev);
+	}
+
+	if (gpuobj->dtor)
+		gpuobj->dtor(dev, gpuobj);
+
+	if (gpuobj->im_backing && !(gpuobj->flags & NVOBJ_FLAG_FAKE))
+		engine->instmem.clear(dev, gpuobj);
+
+	if (gpuobj->im_pramin) {
+		if (gpuobj->flags & NVOBJ_FLAG_FAKE)
+			kfree(gpuobj->im_pramin);
+		else
+			nouveau_mem_free_block(gpuobj->im_pramin);
+	}
+
+	list_del(&gpuobj->list);
+
+	*pgpuobj = NULL;
+	kfree(gpuobj);
+	return 0;
+}
+
+static int
+nouveau_gpuobj_instance_get(struct drm_device *dev,
+			    struct nouveau_channel *chan,
+			    struct nouveau_gpuobj *gpuobj, uint32_t *inst)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_gpuobj *cpramin;
+
+	/* <NV50 use PRAMIN address everywhere */
+	if (dev_priv->card_type < NV_50) {
+		*inst = gpuobj->im_pramin->start;
+		return 0;
+	}
+
+	if (chan && gpuobj->im_channel != chan) {
+		NV_ERROR(dev, "Channel mismatch: obj %d, ref %d\n",
+			 gpuobj->im_channel->id, chan->id);
+		return -EINVAL;
+	}
+
+	/* NV50 channel-local instance */
+	if (chan) {
+		cpramin = chan->ramin->gpuobj;
+		*inst = gpuobj->im_pramin->start - cpramin->im_pramin->start;
+		return 0;
+	}
+
+	/* NV50 global (VRAM) instance */
+	if (!gpuobj->im_channel) {
+		/* ...from global heap */
+		if (!gpuobj->im_backing) {
+			NV_ERROR(dev, "AII, no VRAM backing gpuobj\n");
+			return -EINVAL;
+		}
+		*inst = gpuobj->im_backing_start;
+		return 0;
+	} else {
+		/* ...from local heap */
+		cpramin = gpuobj->im_channel->ramin->gpuobj;
+		*inst = cpramin->im_backing_start +
+			(gpuobj->im_pramin->start - cpramin->im_pramin->start);
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+int
+nouveau_gpuobj_ref_add(struct drm_device *dev, struct nouveau_channel *chan,
+		       uint32_t handle, struct nouveau_gpuobj *gpuobj,
+		       struct nouveau_gpuobj_ref **ref_ret)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_gpuobj_ref *ref;
+	uint32_t instance;
+	int ret;
+
+	NV_DEBUG(dev, "ch%d h=0x%08x gpuobj=%p\n",
+		 chan ? chan->id : -1, handle, gpuobj);
+
+	if (!dev_priv || !gpuobj || (ref_ret && *ref_ret != NULL))
+		return -EINVAL;
+
+	if (!chan && !ref_ret)
+		return -EINVAL;
+
+	if (gpuobj->engine == NVOBJ_ENGINE_SW && !gpuobj->im_pramin) {
+		/* sw object */
+		instance = 0x40;
+	} else {
+		ret = nouveau_gpuobj_instance_get(dev, chan, gpuobj, &instance);
+		if (ret)
+			return ret;
+	}
+
+	ref = kzalloc(sizeof(*ref), GFP_KERNEL);
+	if (!ref)
+		return -ENOMEM;
+	INIT_LIST_HEAD(&ref->list);
+	ref->gpuobj   = gpuobj;
+	ref->channel  = chan;
+	ref->instance = instance;
+
+	if (!ref_ret) {
+		ref->handle = handle;
+
+		ret = nouveau_ramht_insert(dev, ref);
+		if (ret) {
+			kfree(ref);
+			return ret;
+		}
+	} else {
+		ref->handle = ~0;
+		*ref_ret = ref;
+	}
+
+	ref->gpuobj->refcount++;
+	return 0;
+}
+
+int nouveau_gpuobj_ref_del(struct drm_device *dev, struct nouveau_gpuobj_ref **pref)
+{
+	struct nouveau_gpuobj_ref *ref;
+
+	NV_DEBUG(dev, "ref %p\n", pref ? *pref : NULL);
+
+	if (!dev || !pref || *pref == NULL)
+		return -EINVAL;
+	ref = *pref;
+
+	if (ref->handle != ~0)
+		nouveau_ramht_remove(dev, ref);
+
+	if (ref->gpuobj) {
+		ref->gpuobj->refcount--;
+
+		if (ref->gpuobj->refcount == 0) {
+			if (!(ref->gpuobj->flags & NVOBJ_FLAG_ALLOW_NO_REFS))
+				nouveau_gpuobj_del(dev, &ref->gpuobj);
+		}
+	}
+
+	*pref = NULL;
+	kfree(ref);
+	return 0;
+}
+
+int
+nouveau_gpuobj_new_ref(struct drm_device *dev,
+		       struct nouveau_channel *oc, struct nouveau_channel *rc,
+		       uint32_t handle, uint32_t size, int align,
+		       uint32_t flags, struct nouveau_gpuobj_ref **ref)
+{
+	struct nouveau_gpuobj *gpuobj = NULL;
+	int ret;
+
+	ret = nouveau_gpuobj_new(dev, oc, size, align, flags, &gpuobj);
+	if (ret)
+		return ret;
+
+	ret = nouveau_gpuobj_ref_add(dev, rc, handle, gpuobj, ref);
+	if (ret) {
+		nouveau_gpuobj_del(dev, &gpuobj);
+		return ret;
+	}
+
+	return 0;
+}
+
+int
+nouveau_gpuobj_ref_find(struct nouveau_channel *chan, uint32_t handle,
+			struct nouveau_gpuobj_ref **ref_ret)
+{
+	struct nouveau_gpuobj_ref *ref;
+	struct list_head *entry, *tmp;
+
+	list_for_each_safe(entry, tmp, &chan->ramht_refs) {
+		ref = list_entry(entry, struct nouveau_gpuobj_ref, list);
+
+		if (ref->handle == handle) {
+			if (ref_ret)
+				*ref_ret = ref;
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+int
+nouveau_gpuobj_new_fake(struct drm_device *dev, uint32_t p_offset,
+			uint32_t b_offset, uint32_t size,
+			uint32_t flags, struct nouveau_gpuobj **pgpuobj,
+			struct nouveau_gpuobj_ref **pref)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_gpuobj *gpuobj = NULL;
+	int i;
+
+	NV_DEBUG(dev,
+		 "p_offset=0x%08x b_offset=0x%08x size=0x%08x flags=0x%08x\n",
+		 p_offset, b_offset, size, flags);
+
+	gpuobj = kzalloc(sizeof(*gpuobj), GFP_KERNEL);
+	if (!gpuobj)
+		return -ENOMEM;
+	NV_DEBUG(dev, "gpuobj %p\n", gpuobj);
+	gpuobj->im_channel = NULL;
+	gpuobj->flags      = flags | NVOBJ_FLAG_FAKE;
+
+	list_add_tail(&gpuobj->list, &dev_priv->gpuobj_list);
+
+	if (p_offset != ~0) {
+		gpuobj->im_pramin = kzalloc(sizeof(struct mem_block),
+					    GFP_KERNEL);
+		if (!gpuobj->im_pramin) {
+			nouveau_gpuobj_del(dev, &gpuobj);
+			return -ENOMEM;
+		}
+		gpuobj->im_pramin->start = p_offset;
+		gpuobj->im_pramin->size  = size;
+	}
+
+	if (b_offset != ~0) {
+		gpuobj->im_backing = (struct nouveau_bo *)-1;
+		gpuobj->im_backing_start = b_offset;
+	}
+
+	if (gpuobj->flags & NVOBJ_FLAG_ZERO_ALLOC) {
+		dev_priv->engine.instmem.prepare_access(dev, true);
+		for (i = 0; i < gpuobj->im_pramin->size; i += 4)
+			nv_wo32(dev, gpuobj, i/4, 0);
+		dev_priv->engine.instmem.finish_access(dev);
+	}
+
+	if (pref) {
+		i = nouveau_gpuobj_ref_add(dev, NULL, 0, gpuobj, pref);
+		if (i) {
+			nouveau_gpuobj_del(dev, &gpuobj);
+			return i;
+		}
+	}
+
+	if (pgpuobj)
+		*pgpuobj = gpuobj;
+	return 0;
+}
+
+
+static uint32_t
+nouveau_gpuobj_class_instmem_size(struct drm_device *dev, int class)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	/*XXX: dodgy hack for now */
+	if (dev_priv->card_type >= NV_50)
+		return 24;
+	if (dev_priv->card_type >= NV_40)
+		return 32;
+	return 16;
+}
+
+/*
+   DMA objects are used to reference a piece of memory in the
+   framebuffer, PCI or AGP address space. Each object is 16 bytes big
+   and looks as follows:
+
+   entry[0]
+   11:0  class (seems like I can always use 0 here)
+   12    page table present?
+   13    page entry linear?
+   15:14 access: 0 rw, 1 ro, 2 wo
+   17:16 target: 0 NV memory, 1 NV memory tiled, 2 PCI, 3 AGP
+   31:20 dma adjust (bits 0-11 of the address)
+   entry[1]
+   dma limit (size of transfer)
+   entry[X]
+   1     0 readonly, 1 readwrite
+   31:12 dma frame address of the page (bits 12-31 of the address)
+   entry[N]
+   page table terminator, same value as the first pte, as does nvidia
+   rivatv uses 0xffffffff
+
+   Non linear page tables need a list of frame addresses afterwards,
+   the rivatv project has some info on this.
+
+   The method below creates a DMA object in instance RAM and returns a handle
+   to it that can be used to set up context objects.
+*/
+int
+nouveau_gpuobj_dma_new(struct nouveau_channel *chan, int class,
+		       uint64_t offset, uint64_t size, int access,
+		       int target, struct nouveau_gpuobj **gpuobj)
+{
+	struct drm_device *dev = chan->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem;
+	int ret;
+
+	NV_DEBUG(dev, "ch%d class=0x%04x offset=0x%llx size=0x%llx\n",
+		 chan->id, class, offset, size);
+	NV_DEBUG(dev, "access=%d target=%d\n", access, target);
+
+	switch (target) {
+	case NV_DMA_TARGET_AGP:
+		offset += dev_priv->gart_info.aper_base;
+		break;
+	default:
+		break;
+	}
+
+	ret = nouveau_gpuobj_new(dev, chan,
+				 nouveau_gpuobj_class_instmem_size(dev, class),
+				 16, NVOBJ_FLAG_ZERO_ALLOC |
+				 NVOBJ_FLAG_ZERO_FREE, gpuobj);
+	if (ret) {
+		NV_ERROR(dev, "Error creating gpuobj: %d\n", ret);
+		return ret;
+	}
+
+	instmem->prepare_access(dev, true);
+
+	if (dev_priv->card_type < NV_50) {
+		uint32_t frame, adjust, pte_flags = 0;
+
+		if (access != NV_DMA_ACCESS_RO)
+			pte_flags |= (1<<1);
+		adjust = offset &  0x00000fff;
+		frame  = offset & ~0x00000fff;
+
+		nv_wo32(dev, *gpuobj, 0, ((1<<12) | (1<<13) |
+				(adjust << 20) |
+				 (access << 14) |
+				 (target << 16) |
+				  class));
+		nv_wo32(dev, *gpuobj, 1, size - 1);
+		nv_wo32(dev, *gpuobj, 2, frame | pte_flags);
+		nv_wo32(dev, *gpuobj, 3, frame | pte_flags);
+	} else {
+		uint64_t limit = offset + size - 1;
+		uint32_t flags0, flags5;
+
+		if (target == NV_DMA_TARGET_VIDMEM) {
+			flags0 = 0x00190000;
+			flags5 = 0x00010000;
+		} else {
+			flags0 = 0x7fc00000;
+			flags5 = 0x00080000;
+		}
+
+		nv_wo32(dev, *gpuobj, 0, flags0 | class);
+		nv_wo32(dev, *gpuobj, 1, lower_32_bits(limit));
+		nv_wo32(dev, *gpuobj, 2, lower_32_bits(offset));
+		nv_wo32(dev, *gpuobj, 3, ((upper_32_bits(limit) & 0xff) << 24) |
+					(upper_32_bits(offset) & 0xff));
+		nv_wo32(dev, *gpuobj, 5, flags5);
+	}
+
+	instmem->finish_access(dev);
+
+	(*gpuobj)->engine = NVOBJ_ENGINE_SW;
+	(*gpuobj)->class  = class;
+	return 0;
+}
+
+int
+nouveau_gpuobj_gart_dma_new(struct nouveau_channel *chan,
+			    uint64_t offset, uint64_t size, int access,
+			    struct nouveau_gpuobj **gpuobj,
+			    uint32_t *o_ret)
+{
+	struct drm_device *dev = chan->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	int ret;
+
+	if (dev_priv->gart_info.type == NOUVEAU_GART_AGP ||
+	    (dev_priv->card_type >= NV_50 &&
+	     dev_priv->gart_info.type == NOUVEAU_GART_SGDMA)) {
+		ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY,
+					     offset + dev_priv->vm_gart_base,
+					     size, access, NV_DMA_TARGET_AGP,
+					     gpuobj);
+		if (o_ret)
+			*o_ret = 0;
+	} else
+	if (dev_priv->gart_info.type == NOUVEAU_GART_SGDMA) {
+		*gpuobj = dev_priv->gart_info.sg_ctxdma;
+		if (offset & ~0xffffffffULL) {
+			NV_ERROR(dev, "obj offset exceeds 32-bits\n");
+			return -EINVAL;
+		}
+		if (o_ret)
+			*o_ret = (uint32_t)offset;
+		ret = (*gpuobj != NULL) ? 0 : -EINVAL;
+	} else {
+		NV_ERROR(dev, "Invalid GART type %d\n", dev_priv->gart_info.type);
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+/* Context objects in the instance RAM have the following structure.
+ * On NV40 they are 32 byte long, on NV30 and smaller 16 bytes.
+
+   NV4 - NV30:
+
+   entry[0]
+   11:0 class
+   12   chroma key enable
+   13   user clip enable
+   14   swizzle enable
+   17:15 patch config:
+       scrcopy_and, rop_and, blend_and, scrcopy, srccopy_pre, blend_pre
+   18   synchronize enable
+   19   endian: 1 big, 0 little
+   21:20 dither mode
+   23    single step enable
+   24    patch status: 0 invalid, 1 valid
+   25    context_surface 0: 1 valid
+   26    context surface 1: 1 valid
+   27    context pattern: 1 valid
+   28    context rop: 1 valid
+   29,30 context beta, beta4
+   entry[1]
+   7:0   mono format
+   15:8  color format
+   31:16 notify instance address
+   entry[2]
+   15:0  dma 0 instance address
+   31:16 dma 1 instance address
+   entry[3]
+   dma method traps
+
+   NV40:
+   No idea what the exact format is. Here's what can be deducted:
+
+   entry[0]:
+   11:0  class  (maybe uses more bits here?)
+   17    user clip enable
+   21:19 patch config
+   25    patch status valid ?
+   entry[1]:
+   15:0  DMA notifier  (maybe 20:0)
+   entry[2]:
+   15:0  DMA 0 instance (maybe 20:0)
+   24    big endian
+   entry[3]:
+   15:0  DMA 1 instance (maybe 20:0)
+   entry[4]:
+   entry[5]:
+   set to 0?
+*/
+int
+nouveau_gpuobj_gr_new(struct nouveau_channel *chan, int class,
+		      struct nouveau_gpuobj **gpuobj)
+{
+	struct drm_device *dev = chan->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	int ret;
+
+	NV_DEBUG(dev, "ch%d class=0x%04x\n", chan->id, class);
+
+	ret = nouveau_gpuobj_new(dev, chan,
+				 nouveau_gpuobj_class_instmem_size(dev, class),
+				 16,
+				 NVOBJ_FLAG_ZERO_ALLOC | NVOBJ_FLAG_ZERO_FREE,
+				 gpuobj);
+	if (ret) {
+		NV_ERROR(dev, "Error creating gpuobj: %d\n", ret);
+		return ret;
+	}
+
+	dev_priv->engine.instmem.prepare_access(dev, true);
+	if (dev_priv->card_type >= NV_50) {
+		nv_wo32(dev, *gpuobj, 0, class);
+		nv_wo32(dev, *gpuobj, 5, 0x00010000);
+	} else {
+		switch (class) {
+		case NV_CLASS_NULL:
+			nv_wo32(dev, *gpuobj, 0, 0x00001030);
+			nv_wo32(dev, *gpuobj, 1, 0xFFFFFFFF);
+			break;
+		default:
+			if (dev_priv->card_type >= NV_40) {
+				nv_wo32(dev, *gpuobj, 0, class);
+#ifdef __BIG_ENDIAN
+				nv_wo32(dev, *gpuobj, 2, 0x01000000);
+#endif
+			} else {
+#ifdef __BIG_ENDIAN
+				nv_wo32(dev, *gpuobj, 0, class | 0x00080000);
+#else
+				nv_wo32(dev, *gpuobj, 0, class);
+#endif
+			}
+		}
+	}
+	dev_priv->engine.instmem.finish_access(dev);
+
+	(*gpuobj)->engine = NVOBJ_ENGINE_GR;
+	(*gpuobj)->class  = class;
+	return 0;
+}
+
+static int
+nouveau_gpuobj_sw_new(struct nouveau_channel *chan, int class,
+		      struct nouveau_gpuobj **gpuobj_ret)
+{
+	struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
+	struct nouveau_gpuobj *gpuobj;
+
+	if (!chan || !gpuobj_ret || *gpuobj_ret != NULL)
+		return -EINVAL;
+
+	gpuobj = kzalloc(sizeof(*gpuobj), GFP_KERNEL);
+	if (!gpuobj)
+		return -ENOMEM;
+	gpuobj->engine = NVOBJ_ENGINE_SW;
+	gpuobj->class = class;
+
+	list_add_tail(&gpuobj->list, &dev_priv->gpuobj_list);
+	*gpuobj_ret = gpuobj;
+	return 0;
+}
+
+static int
+nouveau_gpuobj_channel_init_pramin(struct nouveau_channel *chan)
+{
+	struct drm_device *dev = chan->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_gpuobj *pramin = NULL;
+	uint32_t size;
+	uint32_t base;
+	int ret;
+
+	NV_DEBUG(dev, "ch%d\n", chan->id);
+
+	/* Base amount for object storage (4KiB enough?) */
+	size = 0x1000;
+	base = 0;
+
+	/* PGRAPH context */
+
+	if (dev_priv->card_type == NV_50) {
+		/* Various fixed table thingos */
+		size += 0x1400; /* mostly unknown stuff */
+		size += 0x4000; /* vm pd */
+		base  = 0x6000;
+		/* RAMHT, not sure about setting size yet, 32KiB to be safe */
+		size += 0x8000;
+		/* RAMFC */
+		size += 0x1000;
+		/* PGRAPH context */
+		size += 0x70000;
+	}
+
+	NV_DEBUG(dev, "ch%d PRAMIN size: 0x%08x bytes, base alloc=0x%08x\n",
+		 chan->id, size, base);
+	ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, size, 0x1000, 0,
+				     &chan->ramin);
+	if (ret) {
+		NV_ERROR(dev, "Error allocating channel PRAMIN: %d\n", ret);
+		return ret;
+	}
+	pramin = chan->ramin->gpuobj;
+
+	ret = nouveau_mem_init_heap(&chan->ramin_heap,
+				    pramin->im_pramin->start + base, size);
+	if (ret) {
+		NV_ERROR(dev, "Error creating PRAMIN heap: %d\n", ret);
+		nouveau_gpuobj_ref_del(dev, &chan->ramin);
+		return ret;
+	}
+
+	return 0;
+}
+
+int
+nouveau_gpuobj_channel_init(struct nouveau_channel *chan,
+			    uint32_t vram_h, uint32_t tt_h)
+{
+	struct drm_device *dev = chan->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem;
+	struct nouveau_gpuobj *vram = NULL, *tt = NULL;
+	int ret, i;
+
+	INIT_LIST_HEAD(&chan->ramht_refs);
+
+	NV_DEBUG(dev, "ch%d vram=0x%08x tt=0x%08x\n", chan->id, vram_h, tt_h);
+
+	/* Reserve a block of PRAMIN for the channel
+	 *XXX: maybe on <NV50 too at some point
+	 */
+	if (0 || dev_priv->card_type == NV_50) {
+		ret = nouveau_gpuobj_channel_init_pramin(chan);
+		if (ret) {
+			NV_ERROR(dev, "init pramin\n");
+			return ret;
+		}
+	}
+
+	/* NV50 VM
+	 *  - Allocate per-channel page-directory
+	 *  - Map GART and VRAM into the channel's address space at the
+	 *    locations determined during init.
+	 */
+	if (dev_priv->card_type >= NV_50) {
+		uint32_t vm_offset, pde;
+
+		instmem->prepare_access(dev, true);
+
+		vm_offset = (dev_priv->chipset & 0xf0) == 0x50 ? 0x1400 : 0x200;
+		vm_offset += chan->ramin->gpuobj->im_pramin->start;
+
+		ret = nouveau_gpuobj_new_fake(dev, vm_offset, ~0, 0x4000,
+							0, &chan->vm_pd, NULL);
+		if (ret) {
+			instmem->finish_access(dev);
+			return ret;
+		}
+		for (i = 0; i < 0x4000; i += 8) {
+			nv_wo32(dev, chan->vm_pd, (i+0)/4, 0x00000000);
+			nv_wo32(dev, chan->vm_pd, (i+4)/4, 0xdeadcafe);
+		}
+
+		pde = (dev_priv->vm_gart_base / (512*1024*1024)) * 2;
+		ret = nouveau_gpuobj_ref_add(dev, NULL, 0,
+					     dev_priv->gart_info.sg_ctxdma,
+					     &chan->vm_gart_pt);
+		if (ret) {
+			instmem->finish_access(dev);
+			return ret;
+		}
+		nv_wo32(dev, chan->vm_pd, pde++,
+			    chan->vm_gart_pt->instance | 0x03);
+		nv_wo32(dev, chan->vm_pd, pde++, 0x00000000);
+
+		pde = (dev_priv->vm_vram_base / (512*1024*1024)) * 2;
+		for (i = 0; i < dev_priv->vm_vram_pt_nr; i++) {
+			ret = nouveau_gpuobj_ref_add(dev, NULL, 0,
+						     dev_priv->vm_vram_pt[i],
+						     &chan->vm_vram_pt[i]);
+			if (ret) {
+				instmem->finish_access(dev);
+				return ret;
+			}
+
+			nv_wo32(dev, chan->vm_pd, pde++,
+				    chan->vm_vram_pt[i]->instance | 0x61);
+			nv_wo32(dev, chan->vm_pd, pde++, 0x00000000);
+		}
+
+		instmem->finish_access(dev);
+	}
+
+	/* RAMHT */
+	if (dev_priv->card_type < NV_50) {
+		ret = nouveau_gpuobj_ref_add(dev, NULL, 0, dev_priv->ramht,
+					     &chan->ramht);
+		if (ret)
+			return ret;
+	} else {
+		ret = nouveau_gpuobj_new_ref(dev, chan, chan, 0,
+					     0x8000, 16,
+					     NVOBJ_FLAG_ZERO_ALLOC,
+					     &chan->ramht);
+		if (ret)
+			return ret;
+	}
+
+	/* VRAM ctxdma */
+	if (dev_priv->card_type >= NV_50) {
+		ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY,
+					     0, dev_priv->vm_end,
+					     NV_DMA_ACCESS_RW,
+					     NV_DMA_TARGET_AGP, &vram);
+		if (ret) {
+			NV_ERROR(dev, "Error creating VRAM ctxdma: %d\n", ret);
+			return ret;
+		}
+	} else {
+		ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY,
+						0, dev_priv->fb_available_size,
+						NV_DMA_ACCESS_RW,
+						NV_DMA_TARGET_VIDMEM, &vram);
+		if (ret) {
+			NV_ERROR(dev, "Error creating VRAM ctxdma: %d\n", ret);
+			return ret;
+		}
+	}
+
+	ret = nouveau_gpuobj_ref_add(dev, chan, vram_h, vram, NULL);
+	if (ret) {
+		NV_ERROR(dev, "Error referencing VRAM ctxdma: %d\n", ret);
+		return ret;
+	}
+
+	/* TT memory ctxdma */
+	if (dev_priv->card_type >= NV_50) {
+		tt = vram;
+	} else
+	if (dev_priv->gart_info.type != NOUVEAU_GART_NONE) {
+		ret = nouveau_gpuobj_gart_dma_new(chan, 0,
+						  dev_priv->gart_info.aper_size,
+						  NV_DMA_ACCESS_RW, &tt, NULL);
+	} else {
+		NV_ERROR(dev, "Invalid GART type %d\n", dev_priv->gart_info.type);
+		ret = -EINVAL;
+	}
+
+	if (ret) {
+		NV_ERROR(dev, "Error creating TT ctxdma: %d\n", ret);
+		return ret;
+	}
+
+	ret = nouveau_gpuobj_ref_add(dev, chan, tt_h, tt, NULL);
+	if (ret) {
+		NV_ERROR(dev, "Error referencing TT ctxdma: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+void
+nouveau_gpuobj_channel_takedown(struct nouveau_channel *chan)
+{
+	struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
+	struct drm_device *dev = chan->dev;
+	struct list_head *entry, *tmp;
+	struct nouveau_gpuobj_ref *ref;
+	int i;
+
+	NV_DEBUG(dev, "ch%d\n", chan->id);
+
+	if (!chan->ramht_refs.next)
+		return;
+
+	list_for_each_safe(entry, tmp, &chan->ramht_refs) {
+		ref = list_entry(entry, struct nouveau_gpuobj_ref, list);
+
+		nouveau_gpuobj_ref_del(dev, &ref);
+	}
+
+	nouveau_gpuobj_ref_del(dev, &chan->ramht);
+
+	nouveau_gpuobj_del(dev, &chan->vm_pd);
+	nouveau_gpuobj_ref_del(dev, &chan->vm_gart_pt);
+	for (i = 0; i < dev_priv->vm_vram_pt_nr; i++)
+		nouveau_gpuobj_ref_del(dev, &chan->vm_vram_pt[i]);
+
+	if (chan->ramin_heap)
+		nouveau_mem_takedown(&chan->ramin_heap);
+	if (chan->ramin)
+		nouveau_gpuobj_ref_del(dev, &chan->ramin);
+
+}
+
+int
+nouveau_gpuobj_suspend(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_gpuobj *gpuobj;
+	int i;
+
+	if (dev_priv->card_type < NV_50) {
+		dev_priv->susres.ramin_copy = vmalloc(dev_priv->ramin_rsvd_vram);
+		if (!dev_priv->susres.ramin_copy)
+			return -ENOMEM;
+
+		for (i = 0; i < dev_priv->ramin_rsvd_vram; i += 4)
+			dev_priv->susres.ramin_copy[i/4] = nv_ri32(dev, i);
+		return 0;
+	}
+
+	list_for_each_entry(gpuobj, &dev_priv->gpuobj_list, list) {
+		if (!gpuobj->im_backing || (gpuobj->flags & NVOBJ_FLAG_FAKE))
+			continue;
+
+		gpuobj->im_backing_suspend = vmalloc(gpuobj->im_pramin->size);
+		if (!gpuobj->im_backing_suspend) {
+			nouveau_gpuobj_resume(dev);
+			return -ENOMEM;
+		}
+
+		dev_priv->engine.instmem.prepare_access(dev, false);
+		for (i = 0; i < gpuobj->im_pramin->size / 4; i++)
+			gpuobj->im_backing_suspend[i] = nv_ro32(dev, gpuobj, i);
+		dev_priv->engine.instmem.finish_access(dev);
+	}
+
+	return 0;
+}
+
+void
+nouveau_gpuobj_suspend_cleanup(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_gpuobj *gpuobj;
+
+	if (dev_priv->card_type < NV_50) {
+		vfree(dev_priv->susres.ramin_copy);
+		dev_priv->susres.ramin_copy = NULL;
+		return;
+	}
+
+	list_for_each_entry(gpuobj, &dev_priv->gpuobj_list, list) {
+		if (!gpuobj->im_backing_suspend)
+			continue;
+
+		vfree(gpuobj->im_backing_suspend);
+		gpuobj->im_backing_suspend = NULL;
+	}
+}
+
+void
+nouveau_gpuobj_resume(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_gpuobj *gpuobj;
+	int i;
+
+	if (dev_priv->card_type < NV_50) {
+		for (i = 0; i < dev_priv->ramin_rsvd_vram; i += 4)
+			nv_wi32(dev, i, dev_priv->susres.ramin_copy[i/4]);
+		nouveau_gpuobj_suspend_cleanup(dev);
+		return;
+	}
+
+	list_for_each_entry(gpuobj, &dev_priv->gpuobj_list, list) {
+		if (!gpuobj->im_backing_suspend)
+			continue;
+
+		dev_priv->engine.instmem.prepare_access(dev, true);
+		for (i = 0; i < gpuobj->im_pramin->size / 4; i++)
+			nv_wo32(dev, gpuobj, i, gpuobj->im_backing_suspend[i]);
+		dev_priv->engine.instmem.finish_access(dev);
+	}
+
+	nouveau_gpuobj_suspend_cleanup(dev);
+}
+
+int nouveau_ioctl_grobj_alloc(struct drm_device *dev, void *data,
+			      struct drm_file *file_priv)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct drm_nouveau_grobj_alloc *init = data;
+	struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
+	struct nouveau_pgraph_object_class *grc;
+	struct nouveau_gpuobj *gr = NULL;
+	struct nouveau_channel *chan;
+	int ret;
+
+	NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
+	NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(init->channel, file_priv, chan);
+
+	if (init->handle == ~0)
+		return -EINVAL;
+
+	grc = pgraph->grclass;
+	while (grc->id) {
+		if (grc->id == init->class)
+			break;
+		grc++;
+	}
+
+	if (!grc->id) {
+		NV_ERROR(dev, "Illegal object class: 0x%x\n", init->class);
+		return -EPERM;
+	}
+
+	if (nouveau_gpuobj_ref_find(chan, init->handle, NULL) == 0)
+		return -EEXIST;
+
+	if (!grc->software)
+		ret = nouveau_gpuobj_gr_new(chan, grc->id, &gr);
+	else
+		ret = nouveau_gpuobj_sw_new(chan, grc->id, &gr);
+
+	if (ret) {
+		NV_ERROR(dev, "Error creating object: %d (%d/0x%08x)\n",
+			 ret, init->channel, init->handle);
+		return ret;
+	}
+
+	ret = nouveau_gpuobj_ref_add(dev, chan, init->handle, gr, NULL);
+	if (ret) {
+		NV_ERROR(dev, "Error referencing object: %d (%d/0x%08x)\n",
+			 ret, init->channel, init->handle);
+		nouveau_gpuobj_del(dev, &gr);
+		return ret;
+	}
+
+	return 0;
+}
+
+int nouveau_ioctl_gpuobj_free(struct drm_device *dev, void *data,
+			      struct drm_file *file_priv)
+{
+	struct drm_nouveau_gpuobj_free *objfree = data;
+	struct nouveau_gpuobj_ref *ref;
+	struct nouveau_channel *chan;
+	int ret;
+
+	NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
+	NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(objfree->channel, file_priv, chan);
+
+	ret = nouveau_gpuobj_ref_find(chan, objfree->handle, &ref);
+	if (ret)
+		return ret;
+	nouveau_gpuobj_ref_del(dev, &ref);
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_reg.h b/drivers/gpu/drm/nouveau/nouveau_reg.h
new file mode 100644
index 0000000..fa1b0e7
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_reg.h
@@ -0,0 +1,836 @@
+
+
+#define NV03_BOOT_0                                        0x00100000
+#    define NV03_BOOT_0_RAM_AMOUNT                         0x00000003
+#    define NV03_BOOT_0_RAM_AMOUNT_8MB                     0x00000000
+#    define NV03_BOOT_0_RAM_AMOUNT_2MB                     0x00000001
+#    define NV03_BOOT_0_RAM_AMOUNT_4MB                     0x00000002
+#    define NV03_BOOT_0_RAM_AMOUNT_8MB_SDRAM               0x00000003
+#    define NV04_BOOT_0_RAM_AMOUNT_32MB                    0x00000000
+#    define NV04_BOOT_0_RAM_AMOUNT_4MB                     0x00000001
+#    define NV04_BOOT_0_RAM_AMOUNT_8MB                     0x00000002
+#    define NV04_BOOT_0_RAM_AMOUNT_16MB                    0x00000003
+
+#define NV04_FIFO_DATA                                     0x0010020c
+#    define NV10_FIFO_DATA_RAM_AMOUNT_MB_MASK              0xfff00000
+#    define NV10_FIFO_DATA_RAM_AMOUNT_MB_SHIFT             20
+
+#define NV_RAMIN                                           0x00700000
+
+#define NV_RAMHT_HANDLE_OFFSET                             0
+#define NV_RAMHT_CONTEXT_OFFSET                            4
+#    define NV_RAMHT_CONTEXT_VALID                         (1<<31)
+#    define NV_RAMHT_CONTEXT_CHANNEL_SHIFT                 24
+#    define NV_RAMHT_CONTEXT_ENGINE_SHIFT                  16
+#        define NV_RAMHT_CONTEXT_ENGINE_SOFTWARE           0
+#        define NV_RAMHT_CONTEXT_ENGINE_GRAPHICS           1
+#    define NV_RAMHT_CONTEXT_INSTANCE_SHIFT                0
+#    define NV40_RAMHT_CONTEXT_CHANNEL_SHIFT               23
+#    define NV40_RAMHT_CONTEXT_ENGINE_SHIFT                20
+#    define NV40_RAMHT_CONTEXT_INSTANCE_SHIFT              0
+
+/* DMA object defines */
+#define NV_DMA_ACCESS_RW 0
+#define NV_DMA_ACCESS_RO 1
+#define NV_DMA_ACCESS_WO 2
+#define NV_DMA_TARGET_VIDMEM 0
+#define NV_DMA_TARGET_PCI    2
+#define NV_DMA_TARGET_AGP    3
+/* The following is not a real value used by the card, it's changed by
+ * nouveau_object_dma_create */
+#define NV_DMA_TARGET_PCI_NONLINEAR 8
+
+/* Some object classes we care about in the drm */
+#define NV_CLASS_DMA_FROM_MEMORY                           0x00000002
+#define NV_CLASS_DMA_TO_MEMORY                             0x00000003
+#define NV_CLASS_NULL                                      0x00000030
+#define NV_CLASS_DMA_IN_MEMORY                             0x0000003D
+
+#define NV03_USER(i)                             (0x00800000+(i*NV03_USER_SIZE))
+#define NV03_USER__SIZE                                                       16
+#define NV10_USER__SIZE                                                       32
+#define NV03_USER_SIZE                                                0x00010000
+#define NV03_USER_DMA_PUT(i)                     (0x00800040+(i*NV03_USER_SIZE))
+#define NV03_USER_DMA_PUT__SIZE                                               16
+#define NV10_USER_DMA_PUT__SIZE                                               32
+#define NV03_USER_DMA_GET(i)                     (0x00800044+(i*NV03_USER_SIZE))
+#define NV03_USER_DMA_GET__SIZE                                               16
+#define NV10_USER_DMA_GET__SIZE                                               32
+#define NV03_USER_REF_CNT(i)                     (0x00800048+(i*NV03_USER_SIZE))
+#define NV03_USER_REF_CNT__SIZE                                               16
+#define NV10_USER_REF_CNT__SIZE                                               32
+
+#define NV40_USER(i)                             (0x00c00000+(i*NV40_USER_SIZE))
+#define NV40_USER_SIZE                                                0x00001000
+#define NV40_USER_DMA_PUT(i)                     (0x00c00040+(i*NV40_USER_SIZE))
+#define NV40_USER_DMA_PUT__SIZE                                               32
+#define NV40_USER_DMA_GET(i)                     (0x00c00044+(i*NV40_USER_SIZE))
+#define NV40_USER_DMA_GET__SIZE                                               32
+#define NV40_USER_REF_CNT(i)                     (0x00c00048+(i*NV40_USER_SIZE))
+#define NV40_USER_REF_CNT__SIZE                                               32
+
+#define NV50_USER(i)                             (0x00c00000+(i*NV50_USER_SIZE))
+#define NV50_USER_SIZE                                                0x00002000
+#define NV50_USER_DMA_PUT(i)                     (0x00c00040+(i*NV50_USER_SIZE))
+#define NV50_USER_DMA_PUT__SIZE                                              128
+#define NV50_USER_DMA_GET(i)                     (0x00c00044+(i*NV50_USER_SIZE))
+#define NV50_USER_DMA_GET__SIZE                                              128
+#define NV50_USER_REF_CNT(i)                     (0x00c00048+(i*NV50_USER_SIZE))
+#define NV50_USER_REF_CNT__SIZE                                              128
+
+#define NV03_FIFO_SIZE                                     0x8000UL
+
+#define NV03_PMC_BOOT_0                                    0x00000000
+#define NV03_PMC_BOOT_1                                    0x00000004
+#define NV03_PMC_INTR_0                                    0x00000100
+#    define NV_PMC_INTR_0_PFIFO_PENDING                        (1<<8)
+#    define NV_PMC_INTR_0_PGRAPH_PENDING                      (1<<12)
+#    define NV_PMC_INTR_0_NV50_I2C_PENDING                    (1<<21)
+#    define NV_PMC_INTR_0_CRTC0_PENDING                       (1<<24)
+#    define NV_PMC_INTR_0_CRTC1_PENDING                       (1<<25)
+#    define NV_PMC_INTR_0_NV50_DISPLAY_PENDING                (1<<26)
+#    define NV_PMC_INTR_0_CRTCn_PENDING                       (3<<24)
+#define NV03_PMC_INTR_EN_0                                 0x00000140
+#    define NV_PMC_INTR_EN_0_MASTER_ENABLE                     (1<<0)
+#define NV03_PMC_ENABLE                                    0x00000200
+#    define NV_PMC_ENABLE_PFIFO                                (1<<8)
+#    define NV_PMC_ENABLE_PGRAPH                              (1<<12)
+/* Disabling the below bit breaks newer (G7X only?) mobile chipsets,
+ * the card will hang early on in the X init process.
+ */
+#    define NV_PMC_ENABLE_UNK13                               (1<<13)
+#define NV40_PMC_BACKLIGHT				   0x000015f0
+#	define NV40_PMC_BACKLIGHT_MASK			   0x001f0000
+#define NV40_PMC_1700                                      0x00001700
+#define NV40_PMC_1704                                      0x00001704
+#define NV40_PMC_1708                                      0x00001708
+#define NV40_PMC_170C                                      0x0000170C
+
+/* probably PMC ? */
+#define NV50_PUNK_BAR0_PRAMIN                              0x00001700
+#define NV50_PUNK_BAR_CFG_BASE                             0x00001704
+#define NV50_PUNK_BAR_CFG_BASE_VALID                          (1<<30)
+#define NV50_PUNK_BAR1_CTXDMA                              0x00001708
+#define NV50_PUNK_BAR1_CTXDMA_VALID                           (1<<31)
+#define NV50_PUNK_BAR3_CTXDMA                              0x0000170C
+#define NV50_PUNK_BAR3_CTXDMA_VALID                           (1<<31)
+#define NV50_PUNK_UNK1710                                  0x00001710
+
+#define NV04_PBUS_PCI_NV_1                                 0x00001804
+#define NV04_PBUS_PCI_NV_19                                0x0000184C
+#define NV04_PBUS_PCI_NV_20				0x00001850
+#	define NV04_PBUS_PCI_NV_20_ROM_SHADOW_DISABLED		(0 << 0)
+#	define NV04_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED		(1 << 0)
+
+#define NV04_PTIMER_INTR_0                                 0x00009100
+#define NV04_PTIMER_INTR_EN_0                              0x00009140
+#define NV04_PTIMER_NUMERATOR                              0x00009200
+#define NV04_PTIMER_DENOMINATOR                            0x00009210
+#define NV04_PTIMER_TIME_0                                 0x00009400
+#define NV04_PTIMER_TIME_1                                 0x00009410
+#define NV04_PTIMER_ALARM_0                                0x00009420
+
+#define NV04_PFB_CFG0                                      0x00100200
+#define NV04_PFB_CFG1                                      0x00100204
+#define NV40_PFB_020C                                      0x0010020C
+#define NV10_PFB_TILE(i)                                   (0x00100240 + (i*16))
+#define NV10_PFB_TILE__SIZE                                8
+#define NV10_PFB_TLIMIT(i)                                 (0x00100244 + (i*16))
+#define NV10_PFB_TSIZE(i)                                  (0x00100248 + (i*16))
+#define NV10_PFB_TSTATUS(i)                                (0x0010024C + (i*16))
+#define NV10_PFB_CLOSE_PAGE2                               0x0010033C
+#define NV40_PFB_TILE(i)                                   (0x00100600 + (i*16))
+#define NV40_PFB_TILE__SIZE_0                              12
+#define NV40_PFB_TILE__SIZE_1                              15
+#define NV40_PFB_TLIMIT(i)                                 (0x00100604 + (i*16))
+#define NV40_PFB_TSIZE(i)                                  (0x00100608 + (i*16))
+#define NV40_PFB_TSTATUS(i)                                (0x0010060C + (i*16))
+#define NV40_PFB_UNK_800					0x00100800
+
+#define NV04_PGRAPH_DEBUG_0                                0x00400080
+#define NV04_PGRAPH_DEBUG_1                                0x00400084
+#define NV04_PGRAPH_DEBUG_2                                0x00400088
+#define NV04_PGRAPH_DEBUG_3                                0x0040008c
+#define NV10_PGRAPH_DEBUG_4                                0x00400090
+#define NV03_PGRAPH_INTR                                   0x00400100
+#define NV03_PGRAPH_NSTATUS                                0x00400104
+#    define NV04_PGRAPH_NSTATUS_STATE_IN_USE                  (1<<11)
+#    define NV04_PGRAPH_NSTATUS_INVALID_STATE                 (1<<12)
+#    define NV04_PGRAPH_NSTATUS_BAD_ARGUMENT                  (1<<13)
+#    define NV04_PGRAPH_NSTATUS_PROTECTION_FAULT              (1<<14)
+#    define NV10_PGRAPH_NSTATUS_STATE_IN_USE                  (1<<23)
+#    define NV10_PGRAPH_NSTATUS_INVALID_STATE                 (1<<24)
+#    define NV10_PGRAPH_NSTATUS_BAD_ARGUMENT                  (1<<25)
+#    define NV10_PGRAPH_NSTATUS_PROTECTION_FAULT              (1<<26)
+#define NV03_PGRAPH_NSOURCE                                0x00400108
+#    define NV03_PGRAPH_NSOURCE_NOTIFICATION                   (1<<0)
+#    define NV03_PGRAPH_NSOURCE_DATA_ERROR                     (1<<1)
+#    define NV03_PGRAPH_NSOURCE_PROTECTION_ERROR               (1<<2)
+#    define NV03_PGRAPH_NSOURCE_RANGE_EXCEPTION                (1<<3)
+#    define NV03_PGRAPH_NSOURCE_LIMIT_COLOR                    (1<<4)
+#    define NV03_PGRAPH_NSOURCE_LIMIT_ZETA                     (1<<5)
+#    define NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD                   (1<<6)
+#    define NV03_PGRAPH_NSOURCE_DMA_R_PROTECTION               (1<<7)
+#    define NV03_PGRAPH_NSOURCE_DMA_W_PROTECTION               (1<<8)
+#    define NV03_PGRAPH_NSOURCE_FORMAT_EXCEPTION               (1<<9)
+#    define NV03_PGRAPH_NSOURCE_PATCH_EXCEPTION               (1<<10)
+#    define NV03_PGRAPH_NSOURCE_STATE_INVALID                 (1<<11)
+#    define NV03_PGRAPH_NSOURCE_DOUBLE_NOTIFY                 (1<<12)
+#    define NV03_PGRAPH_NSOURCE_NOTIFY_IN_USE                 (1<<13)
+#    define NV03_PGRAPH_NSOURCE_METHOD_CNT                    (1<<14)
+#    define NV03_PGRAPH_NSOURCE_BFR_NOTIFICATION              (1<<15)
+#    define NV03_PGRAPH_NSOURCE_DMA_VTX_PROTECTION            (1<<16)
+#    define NV03_PGRAPH_NSOURCE_DMA_WIDTH_A                   (1<<17)
+#    define NV03_PGRAPH_NSOURCE_DMA_WIDTH_B                   (1<<18)
+#define NV03_PGRAPH_INTR_EN                                0x00400140
+#define NV40_PGRAPH_INTR_EN                                0x0040013C
+#    define NV_PGRAPH_INTR_NOTIFY                              (1<<0)
+#    define NV_PGRAPH_INTR_MISSING_HW                          (1<<4)
+#    define NV_PGRAPH_INTR_CONTEXT_SWITCH                     (1<<12)
+#    define NV_PGRAPH_INTR_BUFFER_NOTIFY                      (1<<16)
+#    define NV_PGRAPH_INTR_ERROR                              (1<<20)
+#define NV10_PGRAPH_CTX_CONTROL                            0x00400144
+#define NV10_PGRAPH_CTX_USER                               0x00400148
+#define NV10_PGRAPH_CTX_SWITCH1                            0x0040014C
+#define NV10_PGRAPH_CTX_SWITCH2                            0x00400150
+#define NV10_PGRAPH_CTX_SWITCH3                            0x00400154
+#define NV10_PGRAPH_CTX_SWITCH4                            0x00400158
+#define NV10_PGRAPH_CTX_SWITCH5                            0x0040015C
+#define NV04_PGRAPH_CTX_SWITCH1                            0x00400160
+#define NV10_PGRAPH_CTX_CACHE1                             0x00400160
+#define NV04_PGRAPH_CTX_SWITCH2                            0x00400164
+#define NV04_PGRAPH_CTX_SWITCH3                            0x00400168
+#define NV04_PGRAPH_CTX_SWITCH4                            0x0040016C
+#define NV04_PGRAPH_CTX_CONTROL                            0x00400170
+#define NV04_PGRAPH_CTX_USER                               0x00400174
+#define NV04_PGRAPH_CTX_CACHE1                             0x00400180
+#define NV10_PGRAPH_CTX_CACHE2                             0x00400180
+#define NV03_PGRAPH_CTX_CONTROL                            0x00400190
+#define NV03_PGRAPH_CTX_USER                               0x00400194
+#define NV04_PGRAPH_CTX_CACHE2                             0x004001A0
+#define NV10_PGRAPH_CTX_CACHE3                             0x004001A0
+#define NV04_PGRAPH_CTX_CACHE3                             0x004001C0
+#define NV10_PGRAPH_CTX_CACHE4                             0x004001C0
+#define NV04_PGRAPH_CTX_CACHE4                             0x004001E0
+#define NV10_PGRAPH_CTX_CACHE5                             0x004001E0
+#define NV40_PGRAPH_CTXCTL_0304                            0x00400304
+#define NV40_PGRAPH_CTXCTL_0304_XFER_CTX                   0x00000001
+#define NV40_PGRAPH_CTXCTL_UCODE_STAT                      0x00400308
+#define NV40_PGRAPH_CTXCTL_UCODE_STAT_IP_MASK              0xff000000
+#define NV40_PGRAPH_CTXCTL_UCODE_STAT_IP_SHIFT                     24
+#define NV40_PGRAPH_CTXCTL_UCODE_STAT_OP_MASK              0x00ffffff
+#define NV40_PGRAPH_CTXCTL_0310                            0x00400310
+#define NV40_PGRAPH_CTXCTL_0310_XFER_SAVE                  0x00000020
+#define NV40_PGRAPH_CTXCTL_0310_XFER_LOAD                  0x00000040
+#define NV40_PGRAPH_CTXCTL_030C                            0x0040030c
+#define NV40_PGRAPH_CTXCTL_UCODE_INDEX                     0x00400324
+#define NV40_PGRAPH_CTXCTL_UCODE_DATA                      0x00400328
+#define NV40_PGRAPH_CTXCTL_CUR                             0x0040032c
+#define NV40_PGRAPH_CTXCTL_CUR_LOADED                      0x01000000
+#define NV40_PGRAPH_CTXCTL_CUR_INSTANCE                    0x000FFFFF
+#define NV40_PGRAPH_CTXCTL_NEXT                            0x00400330
+#define NV40_PGRAPH_CTXCTL_NEXT_INSTANCE                   0x000fffff
+#define NV50_PGRAPH_CTXCTL_CUR                             0x0040032c
+#define NV50_PGRAPH_CTXCTL_CUR_LOADED                      0x80000000
+#define NV50_PGRAPH_CTXCTL_CUR_INSTANCE                    0x00ffffff
+#define NV50_PGRAPH_CTXCTL_NEXT                            0x00400330
+#define NV50_PGRAPH_CTXCTL_NEXT_INSTANCE                   0x00ffffff
+#define NV03_PGRAPH_ABS_X_RAM                              0x00400400
+#define NV03_PGRAPH_ABS_Y_RAM                              0x00400480
+#define NV03_PGRAPH_X_MISC                                 0x00400500
+#define NV03_PGRAPH_Y_MISC                                 0x00400504
+#define NV04_PGRAPH_VALID1                                 0x00400508
+#define NV04_PGRAPH_SOURCE_COLOR                           0x0040050C
+#define NV04_PGRAPH_MISC24_0                               0x00400510
+#define NV03_PGRAPH_XY_LOGIC_MISC0                         0x00400514
+#define NV03_PGRAPH_XY_LOGIC_MISC1                         0x00400518
+#define NV03_PGRAPH_XY_LOGIC_MISC2                         0x0040051C
+#define NV03_PGRAPH_XY_LOGIC_MISC3                         0x00400520
+#define NV03_PGRAPH_CLIPX_0                                0x00400524
+#define NV03_PGRAPH_CLIPX_1                                0x00400528
+#define NV03_PGRAPH_CLIPY_0                                0x0040052C
+#define NV03_PGRAPH_CLIPY_1                                0x00400530
+#define NV03_PGRAPH_ABS_ICLIP_XMAX                         0x00400534
+#define NV03_PGRAPH_ABS_ICLIP_YMAX                         0x00400538
+#define NV03_PGRAPH_ABS_UCLIP_XMIN                         0x0040053C
+#define NV03_PGRAPH_ABS_UCLIP_YMIN                         0x00400540
+#define NV03_PGRAPH_ABS_UCLIP_XMAX                         0x00400544
+#define NV03_PGRAPH_ABS_UCLIP_YMAX                         0x00400548
+#define NV03_PGRAPH_ABS_UCLIPA_XMIN                        0x00400560
+#define NV03_PGRAPH_ABS_UCLIPA_YMIN                        0x00400564
+#define NV03_PGRAPH_ABS_UCLIPA_XMAX                        0x00400568
+#define NV03_PGRAPH_ABS_UCLIPA_YMAX                        0x0040056C
+#define NV04_PGRAPH_MISC24_1                               0x00400570
+#define NV04_PGRAPH_MISC24_2                               0x00400574
+#define NV04_PGRAPH_VALID2                                 0x00400578
+#define NV04_PGRAPH_PASSTHRU_0                             0x0040057C
+#define NV04_PGRAPH_PASSTHRU_1                             0x00400580
+#define NV04_PGRAPH_PASSTHRU_2                             0x00400584
+#define NV10_PGRAPH_DIMX_TEXTURE                           0x00400588
+#define NV10_PGRAPH_WDIMX_TEXTURE                          0x0040058C
+#define NV04_PGRAPH_COMBINE_0_ALPHA                        0x00400590
+#define NV04_PGRAPH_COMBINE_0_COLOR                        0x00400594
+#define NV04_PGRAPH_COMBINE_1_ALPHA                        0x00400598
+#define NV04_PGRAPH_COMBINE_1_COLOR                        0x0040059C
+#define NV04_PGRAPH_FORMAT_0                               0x004005A8
+#define NV04_PGRAPH_FORMAT_1                               0x004005AC
+#define NV04_PGRAPH_FILTER_0                               0x004005B0
+#define NV04_PGRAPH_FILTER_1                               0x004005B4
+#define NV03_PGRAPH_MONO_COLOR0                            0x00400600
+#define NV04_PGRAPH_ROP3                                   0x00400604
+#define NV04_PGRAPH_BETA_AND                               0x00400608
+#define NV04_PGRAPH_BETA_PREMULT                           0x0040060C
+#define NV04_PGRAPH_LIMIT_VIOL_PIX                         0x00400610
+#define NV04_PGRAPH_FORMATS                                0x00400618
+#define NV10_PGRAPH_DEBUG_2                                0x00400620
+#define NV04_PGRAPH_BOFFSET0                               0x00400640
+#define NV04_PGRAPH_BOFFSET1                               0x00400644
+#define NV04_PGRAPH_BOFFSET2                               0x00400648
+#define NV04_PGRAPH_BOFFSET3                               0x0040064C
+#define NV04_PGRAPH_BOFFSET4                               0x00400650
+#define NV04_PGRAPH_BOFFSET5                               0x00400654
+#define NV04_PGRAPH_BBASE0                                 0x00400658
+#define NV04_PGRAPH_BBASE1                                 0x0040065C
+#define NV04_PGRAPH_BBASE2                                 0x00400660
+#define NV04_PGRAPH_BBASE3                                 0x00400664
+#define NV04_PGRAPH_BBASE4                                 0x00400668
+#define NV04_PGRAPH_BBASE5                                 0x0040066C
+#define NV04_PGRAPH_BPITCH0                                0x00400670
+#define NV04_PGRAPH_BPITCH1                                0x00400674
+#define NV04_PGRAPH_BPITCH2                                0x00400678
+#define NV04_PGRAPH_BPITCH3                                0x0040067C
+#define NV04_PGRAPH_BPITCH4                                0x00400680
+#define NV04_PGRAPH_BLIMIT0                                0x00400684
+#define NV04_PGRAPH_BLIMIT1                                0x00400688
+#define NV04_PGRAPH_BLIMIT2                                0x0040068C
+#define NV04_PGRAPH_BLIMIT3                                0x00400690
+#define NV04_PGRAPH_BLIMIT4                                0x00400694
+#define NV04_PGRAPH_BLIMIT5                                0x00400698
+#define NV04_PGRAPH_BSWIZZLE2                              0x0040069C
+#define NV04_PGRAPH_BSWIZZLE5                              0x004006A0
+#define NV03_PGRAPH_STATUS                                 0x004006B0
+#define NV04_PGRAPH_STATUS                                 0x00400700
+#define NV04_PGRAPH_TRAPPED_ADDR                           0x00400704
+#define NV04_PGRAPH_TRAPPED_DATA                           0x00400708
+#define NV04_PGRAPH_SURFACE                                0x0040070C
+#define NV10_PGRAPH_TRAPPED_DATA_HIGH                      0x0040070C
+#define NV04_PGRAPH_STATE                                  0x00400710
+#define NV10_PGRAPH_SURFACE                                0x00400710
+#define NV04_PGRAPH_NOTIFY                                 0x00400714
+#define NV10_PGRAPH_STATE                                  0x00400714
+#define NV10_PGRAPH_NOTIFY                                 0x00400718
+
+#define NV04_PGRAPH_FIFO                                   0x00400720
+
+#define NV04_PGRAPH_BPIXEL                                 0x00400724
+#define NV10_PGRAPH_RDI_INDEX                              0x00400750
+#define NV04_PGRAPH_FFINTFC_ST2                            0x00400754
+#define NV10_PGRAPH_RDI_DATA                               0x00400754
+#define NV04_PGRAPH_DMA_PITCH                              0x00400760
+#define NV10_PGRAPH_FFINTFC_ST2                            0x00400764
+#define NV04_PGRAPH_DVD_COLORFMT                           0x00400764
+#define NV04_PGRAPH_SCALED_FORMAT                          0x00400768
+#define NV10_PGRAPH_DMA_PITCH                              0x00400770
+#define NV10_PGRAPH_DVD_COLORFMT                           0x00400774
+#define NV10_PGRAPH_SCALED_FORMAT                          0x00400778
+#define NV20_PGRAPH_CHANNEL_CTX_TABLE                      0x00400780
+#define NV20_PGRAPH_CHANNEL_CTX_POINTER                    0x00400784
+#define NV20_PGRAPH_CHANNEL_CTX_XFER                       0x00400788
+#define NV20_PGRAPH_CHANNEL_CTX_XFER_LOAD                  0x00000001
+#define NV20_PGRAPH_CHANNEL_CTX_XFER_SAVE                  0x00000002
+#define NV04_PGRAPH_PATT_COLOR0                            0x00400800
+#define NV04_PGRAPH_PATT_COLOR1                            0x00400804
+#define NV04_PGRAPH_PATTERN                                0x00400808
+#define NV04_PGRAPH_PATTERN_SHAPE                          0x00400810
+#define NV04_PGRAPH_CHROMA                                 0x00400814
+#define NV04_PGRAPH_CONTROL0                               0x00400818
+#define NV04_PGRAPH_CONTROL1                               0x0040081C
+#define NV04_PGRAPH_CONTROL2                               0x00400820
+#define NV04_PGRAPH_BLEND                                  0x00400824
+#define NV04_PGRAPH_STORED_FMT                             0x00400830
+#define NV04_PGRAPH_PATT_COLORRAM                          0x00400900
+#define NV40_PGRAPH_TILE0(i)                               (0x00400900 + (i*16))
+#define NV40_PGRAPH_TLIMIT0(i)                             (0x00400904 + (i*16))
+#define NV40_PGRAPH_TSIZE0(i)                              (0x00400908 + (i*16))
+#define NV40_PGRAPH_TSTATUS0(i)                            (0x0040090C + (i*16))
+#define NV10_PGRAPH_TILE(i)                                (0x00400B00 + (i*16))
+#define NV10_PGRAPH_TLIMIT(i)                              (0x00400B04 + (i*16))
+#define NV10_PGRAPH_TSIZE(i)                               (0x00400B08 + (i*16))
+#define NV10_PGRAPH_TSTATUS(i)                             (0x00400B0C + (i*16))
+#define NV04_PGRAPH_U_RAM                                  0x00400D00
+#define NV47_PGRAPH_TILE0(i)                               (0x00400D00 + (i*16))
+#define NV47_PGRAPH_TLIMIT0(i)                             (0x00400D04 + (i*16))
+#define NV47_PGRAPH_TSIZE0(i)                              (0x00400D08 + (i*16))
+#define NV47_PGRAPH_TSTATUS0(i)                            (0x00400D0C + (i*16))
+#define NV04_PGRAPH_V_RAM                                  0x00400D40
+#define NV04_PGRAPH_W_RAM                                  0x00400D80
+#define NV10_PGRAPH_COMBINER0_IN_ALPHA                     0x00400E40
+#define NV10_PGRAPH_COMBINER1_IN_ALPHA                     0x00400E44
+#define NV10_PGRAPH_COMBINER0_IN_RGB                       0x00400E48
+#define NV10_PGRAPH_COMBINER1_IN_RGB                       0x00400E4C
+#define NV10_PGRAPH_COMBINER_COLOR0                        0x00400E50
+#define NV10_PGRAPH_COMBINER_COLOR1                        0x00400E54
+#define NV10_PGRAPH_COMBINER0_OUT_ALPHA                    0x00400E58
+#define NV10_PGRAPH_COMBINER1_OUT_ALPHA                    0x00400E5C
+#define NV10_PGRAPH_COMBINER0_OUT_RGB                      0x00400E60
+#define NV10_PGRAPH_COMBINER1_OUT_RGB                      0x00400E64
+#define NV10_PGRAPH_COMBINER_FINAL0                        0x00400E68
+#define NV10_PGRAPH_COMBINER_FINAL1                        0x00400E6C
+#define NV10_PGRAPH_WINDOWCLIP_HORIZONTAL                  0x00400F00
+#define NV10_PGRAPH_WINDOWCLIP_VERTICAL                    0x00400F20
+#define NV10_PGRAPH_XFMODE0                                0x00400F40
+#define NV10_PGRAPH_XFMODE1                                0x00400F44
+#define NV10_PGRAPH_GLOBALSTATE0                           0x00400F48
+#define NV10_PGRAPH_GLOBALSTATE1                           0x00400F4C
+#define NV10_PGRAPH_PIPE_ADDRESS                           0x00400F50
+#define NV10_PGRAPH_PIPE_DATA                              0x00400F54
+#define NV04_PGRAPH_DMA_START_0                            0x00401000
+#define NV04_PGRAPH_DMA_START_1                            0x00401004
+#define NV04_PGRAPH_DMA_LENGTH                             0x00401008
+#define NV04_PGRAPH_DMA_MISC                               0x0040100C
+#define NV04_PGRAPH_DMA_DATA_0                             0x00401020
+#define NV04_PGRAPH_DMA_DATA_1                             0x00401024
+#define NV04_PGRAPH_DMA_RM                                 0x00401030
+#define NV04_PGRAPH_DMA_A_XLATE_INST                       0x00401040
+#define NV04_PGRAPH_DMA_A_CONTROL                          0x00401044
+#define NV04_PGRAPH_DMA_A_LIMIT                            0x00401048
+#define NV04_PGRAPH_DMA_A_TLB_PTE                          0x0040104C
+#define NV04_PGRAPH_DMA_A_TLB_TAG                          0x00401050
+#define NV04_PGRAPH_DMA_A_ADJ_OFFSET                       0x00401054
+#define NV04_PGRAPH_DMA_A_OFFSET                           0x00401058
+#define NV04_PGRAPH_DMA_A_SIZE                             0x0040105C
+#define NV04_PGRAPH_DMA_A_Y_SIZE                           0x00401060
+#define NV04_PGRAPH_DMA_B_XLATE_INST                       0x00401080
+#define NV04_PGRAPH_DMA_B_CONTROL                          0x00401084
+#define NV04_PGRAPH_DMA_B_LIMIT                            0x00401088
+#define NV04_PGRAPH_DMA_B_TLB_PTE                          0x0040108C
+#define NV04_PGRAPH_DMA_B_TLB_TAG                          0x00401090
+#define NV04_PGRAPH_DMA_B_ADJ_OFFSET                       0x00401094
+#define NV04_PGRAPH_DMA_B_OFFSET                           0x00401098
+#define NV04_PGRAPH_DMA_B_SIZE                             0x0040109C
+#define NV04_PGRAPH_DMA_B_Y_SIZE                           0x004010A0
+#define NV40_PGRAPH_TILE1(i)                               (0x00406900 + (i*16))
+#define NV40_PGRAPH_TLIMIT1(i)                             (0x00406904 + (i*16))
+#define NV40_PGRAPH_TSIZE1(i)                              (0x00406908 + (i*16))
+#define NV40_PGRAPH_TSTATUS1(i)                            (0x0040690C + (i*16))
+
+
+/* It's a guess that this works on NV03. Confirmed on NV04, though */
+#define NV04_PFIFO_DELAY_0                                 0x00002040
+#define NV04_PFIFO_DMA_TIMESLICE                           0x00002044
+#define NV04_PFIFO_NEXT_CHANNEL                            0x00002050
+#define NV03_PFIFO_INTR_0                                  0x00002100
+#define NV03_PFIFO_INTR_EN_0                               0x00002140
+#    define NV_PFIFO_INTR_CACHE_ERROR                          (1<<0)
+#    define NV_PFIFO_INTR_RUNOUT                               (1<<4)
+#    define NV_PFIFO_INTR_RUNOUT_OVERFLOW                      (1<<8)
+#    define NV_PFIFO_INTR_DMA_PUSHER                          (1<<12)
+#    define NV_PFIFO_INTR_DMA_PT                              (1<<16)
+#    define NV_PFIFO_INTR_SEMAPHORE                           (1<<20)
+#    define NV_PFIFO_INTR_ACQUIRE_TIMEOUT                     (1<<24)
+#define NV03_PFIFO_RAMHT                                   0x00002210
+#define NV03_PFIFO_RAMFC                                   0x00002214
+#define NV03_PFIFO_RAMRO                                   0x00002218
+#define NV40_PFIFO_RAMFC                                   0x00002220
+#define NV03_PFIFO_CACHES                                  0x00002500
+#define NV04_PFIFO_MODE                                    0x00002504
+#define NV04_PFIFO_DMA                                     0x00002508
+#define NV04_PFIFO_SIZE                                    0x0000250c
+#define NV50_PFIFO_CTX_TABLE(c)                        (0x2600+(c)*4)
+#define NV50_PFIFO_CTX_TABLE__SIZE                                128
+#define NV50_PFIFO_CTX_TABLE_CHANNEL_ENABLED                  (1<<31)
+#define NV50_PFIFO_CTX_TABLE_UNK30_BAD                        (1<<30)
+#define NV50_PFIFO_CTX_TABLE_INSTANCE_MASK_G80             0x0FFFFFFF
+#define NV50_PFIFO_CTX_TABLE_INSTANCE_MASK_G84             0x00FFFFFF
+#define NV03_PFIFO_CACHE0_PUSH0                            0x00003000
+#define NV03_PFIFO_CACHE0_PULL0                            0x00003040
+#define NV04_PFIFO_CACHE0_PULL0                            0x00003050
+#define NV04_PFIFO_CACHE0_PULL1                            0x00003054
+#define NV03_PFIFO_CACHE1_PUSH0                            0x00003200
+#define NV03_PFIFO_CACHE1_PUSH1                            0x00003204
+#define NV03_PFIFO_CACHE1_PUSH1_DMA                            (1<<8)
+#define NV40_PFIFO_CACHE1_PUSH1_DMA                           (1<<16)
+#define NV03_PFIFO_CACHE1_PUSH1_CHID_MASK                  0x0000000f
+#define NV10_PFIFO_CACHE1_PUSH1_CHID_MASK                  0x0000001f
+#define NV50_PFIFO_CACHE1_PUSH1_CHID_MASK                  0x0000007f
+#define NV03_PFIFO_CACHE1_PUT                              0x00003210
+#define NV04_PFIFO_CACHE1_DMA_PUSH                         0x00003220
+#define NV04_PFIFO_CACHE1_DMA_FETCH                        0x00003224
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_8_BYTES         0x00000000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_16_BYTES        0x00000008
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_24_BYTES        0x00000010
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_32_BYTES        0x00000018
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_40_BYTES        0x00000020
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_48_BYTES        0x00000028
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_56_BYTES        0x00000030
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_64_BYTES        0x00000038
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_72_BYTES        0x00000040
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_80_BYTES        0x00000048
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_88_BYTES        0x00000050
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_96_BYTES        0x00000058
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_104_BYTES       0x00000060
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_112_BYTES       0x00000068
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_120_BYTES       0x00000070
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES       0x00000078
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_136_BYTES       0x00000080
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_144_BYTES       0x00000088
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_152_BYTES       0x00000090
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_160_BYTES       0x00000098
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_168_BYTES       0x000000A0
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_176_BYTES       0x000000A8
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_184_BYTES       0x000000B0
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_192_BYTES       0x000000B8
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_200_BYTES       0x000000C0
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_208_BYTES       0x000000C8
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_216_BYTES       0x000000D0
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_224_BYTES       0x000000D8
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_232_BYTES       0x000000E0
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_240_BYTES       0x000000E8
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_248_BYTES       0x000000F0
+#    define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_256_BYTES       0x000000F8
+#    define NV_PFIFO_CACHE1_DMA_FETCH_SIZE                 0x0000E000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_32_BYTES        0x00000000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_64_BYTES        0x00002000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_96_BYTES        0x00004000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES       0x00006000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_160_BYTES       0x00008000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_192_BYTES       0x0000A000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_224_BYTES       0x0000C000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_256_BYTES       0x0000E000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS             0x001F0000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_0           0x00000000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_1           0x00010000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_2           0x00020000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_3           0x00030000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_4           0x00040000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_5           0x00050000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_6           0x00060000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_7           0x00070000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8           0x00080000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_9           0x00090000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_10          0x000A0000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_11          0x000B0000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_12          0x000C0000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_13          0x000D0000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_14          0x000E0000
+#    define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_15          0x000F0000
+#    define NV_PFIFO_CACHE1_ENDIAN                         0x80000000
+#    define NV_PFIFO_CACHE1_LITTLE_ENDIAN                  0x7FFFFFFF
+#    define NV_PFIFO_CACHE1_BIG_ENDIAN                     0x80000000
+#define NV04_PFIFO_CACHE1_DMA_STATE                        0x00003228
+#define NV04_PFIFO_CACHE1_DMA_INSTANCE                     0x0000322c
+#define NV04_PFIFO_CACHE1_DMA_CTL                          0x00003230
+#define NV04_PFIFO_CACHE1_DMA_PUT                          0x00003240
+#define NV04_PFIFO_CACHE1_DMA_GET                          0x00003244
+#define NV10_PFIFO_CACHE1_REF_CNT                          0x00003248
+#define NV10_PFIFO_CACHE1_DMA_SUBROUTINE                   0x0000324C
+#define NV03_PFIFO_CACHE1_PULL0                            0x00003240
+#define NV04_PFIFO_CACHE1_PULL0                            0x00003250
+#define NV03_PFIFO_CACHE1_PULL1                            0x00003250
+#define NV04_PFIFO_CACHE1_PULL1                            0x00003254
+#define NV04_PFIFO_CACHE1_HASH                             0x00003258
+#define NV10_PFIFO_CACHE1_ACQUIRE_TIMEOUT                  0x00003260
+#define NV10_PFIFO_CACHE1_ACQUIRE_TIMESTAMP                0x00003264
+#define NV10_PFIFO_CACHE1_ACQUIRE_VALUE                    0x00003268
+#define NV10_PFIFO_CACHE1_SEMAPHORE                        0x0000326C
+#define NV03_PFIFO_CACHE1_GET                              0x00003270
+#define NV04_PFIFO_CACHE1_ENGINE                           0x00003280
+#define NV04_PFIFO_CACHE1_DMA_DCOUNT                       0x000032A0
+#define NV40_PFIFO_GRCTX_INSTANCE                          0x000032E0
+#define NV40_PFIFO_UNK32E4                                 0x000032E4
+#define NV04_PFIFO_CACHE1_METHOD(i)                (0x00003800+(i*8))
+#define NV04_PFIFO_CACHE1_DATA(i)                  (0x00003804+(i*8))
+#define NV40_PFIFO_CACHE1_METHOD(i)                (0x00090000+(i*8))
+#define NV40_PFIFO_CACHE1_DATA(i)                  (0x00090004+(i*8))
+
+#define NV_CRTC0_INTSTAT                                   0x00600100
+#define NV_CRTC0_INTEN                                     0x00600140
+#define NV_CRTC1_INTSTAT                                   0x00602100
+#define NV_CRTC1_INTEN                                     0x00602140
+#    define NV_CRTC_INTR_VBLANK                                (1<<0)
+
+#define NV04_PRAMIN						0x00700000
+
+/* Fifo commands. These are not regs, neither masks */
+#define NV03_FIFO_CMD_JUMP                                 0x20000000
+#define NV03_FIFO_CMD_JUMP_OFFSET_MASK                     0x1ffffffc
+#define NV03_FIFO_CMD_REWIND                               (NV03_FIFO_CMD_JUMP | (0 & NV03_FIFO_CMD_JUMP_OFFSET_MASK))
+
+/* This is a partial import from rules-ng, a few things may be duplicated.
+ * Eventually we should completely import everything from rules-ng.
+ * For the moment check rules-ng for docs.
+  */
+
+#define NV50_PMC                                            0x00000000
+#define NV50_PMC__LEN                                              0x1
+#define NV50_PMC__ESIZE                                         0x2000
+#    define NV50_PMC_BOOT_0                                 0x00000000
+#        define NV50_PMC_BOOT_0_REVISION                    0x000000ff
+#        define NV50_PMC_BOOT_0_REVISION__SHIFT                      0
+#        define NV50_PMC_BOOT_0_ARCH                        0x0ff00000
+#        define NV50_PMC_BOOT_0_ARCH__SHIFT                         20
+#    define NV50_PMC_INTR_0                                 0x00000100
+#        define NV50_PMC_INTR_0_PFIFO                           (1<<8)
+#        define NV50_PMC_INTR_0_PGRAPH                         (1<<12)
+#        define NV50_PMC_INTR_0_PTIMER                         (1<<20)
+#        define NV50_PMC_INTR_0_HOTPLUG                        (1<<21)
+#        define NV50_PMC_INTR_0_DISPLAY                        (1<<26)
+#    define NV50_PMC_INTR_EN_0                              0x00000140
+#        define NV50_PMC_INTR_EN_0_MASTER                       (1<<0)
+#            define NV50_PMC_INTR_EN_0_MASTER_DISABLED          (0<<0)
+#            define NV50_PMC_INTR_EN_0_MASTER_ENABLED           (1<<0)
+#    define NV50_PMC_ENABLE                                 0x00000200
+#        define NV50_PMC_ENABLE_PFIFO                           (1<<8)
+#        define NV50_PMC_ENABLE_PGRAPH                         (1<<12)
+
+#define NV50_PCONNECTOR                                     0x0000e000
+#define NV50_PCONNECTOR__LEN                                       0x1
+#define NV50_PCONNECTOR__ESIZE                                  0x1000
+#    define NV50_PCONNECTOR_HOTPLUG_INTR                    0x0000e050
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C0          (1<<0)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C1          (1<<1)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C2          (1<<2)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C3          (1<<3)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C0       (1<<16)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C1       (1<<17)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C2       (1<<18)
+#        define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C3       (1<<19)
+#    define NV50_PCONNECTOR_HOTPLUG_CTRL                    0x0000e054
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C0          (1<<0)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C1          (1<<1)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C2          (1<<2)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C3          (1<<3)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C0       (1<<16)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C1       (1<<17)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C2       (1<<18)
+#        define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C3       (1<<19)
+#    define NV50_PCONNECTOR_HOTPLUG_STATE                   0x0000e104
+#        define NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C0 (1<<2)
+#        define NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C1 (1<<6)
+#        define NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C2 (1<<10)
+#        define NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C3 (1<<14)
+#    define NV50_PCONNECTOR_I2C_PORT_0                      0x0000e138
+#    define NV50_PCONNECTOR_I2C_PORT_1                      0x0000e150
+#    define NV50_PCONNECTOR_I2C_PORT_2                      0x0000e168
+#    define NV50_PCONNECTOR_I2C_PORT_3                      0x0000e180
+#    define NV50_PCONNECTOR_I2C_PORT_4                      0x0000e240
+#    define NV50_PCONNECTOR_I2C_PORT_5                      0x0000e258
+
+#define NV50_AUXCH_DATA_OUT(i,n)             ((n) * 4 + (i) * 0x50 + 0x0000e4c0)
+#define NV50_AUXCH_DATA_OUT__SIZE                                             4
+#define NV50_AUXCH_DATA_IN(i,n)              ((n) * 4 + (i) * 0x50 + 0x0000e4d0)
+#define NV50_AUXCH_DATA_IN__SIZE                                              4
+#define NV50_AUXCH_ADDR(i)                             ((i) * 0x50 + 0x0000e4e0)
+#define NV50_AUXCH_CTRL(i)                             ((i) * 0x50 + 0x0000e4e4)
+#define NV50_AUXCH_CTRL_LINKSTAT                                     0x01000000
+#define NV50_AUXCH_CTRL_LINKSTAT_NOT_READY                           0x00000000
+#define NV50_AUXCH_CTRL_LINKSTAT_READY                               0x01000000
+#define NV50_AUXCH_CTRL_LINKEN                                       0x00100000
+#define NV50_AUXCH_CTRL_LINKEN_DISABLED                              0x00000000
+#define NV50_AUXCH_CTRL_LINKEN_ENABLED                               0x00100000
+#define NV50_AUXCH_CTRL_EXEC                                         0x00010000
+#define NV50_AUXCH_CTRL_EXEC_COMPLETE                                0x00000000
+#define NV50_AUXCH_CTRL_EXEC_IN_PROCESS                              0x00010000
+#define NV50_AUXCH_CTRL_CMD                                          0x0000f000
+#define NV50_AUXCH_CTRL_CMD_SHIFT                                            12
+#define NV50_AUXCH_CTRL_LEN                                          0x0000000f
+#define NV50_AUXCH_CTRL_LEN_SHIFT                                             0
+#define NV50_AUXCH_STAT(i)                             ((i) * 0x50 + 0x0000e4e8)
+#define NV50_AUXCH_STAT_STATE                                        0x10000000
+#define NV50_AUXCH_STAT_STATE_NOT_READY                              0x00000000
+#define NV50_AUXCH_STAT_STATE_READY                                  0x10000000
+#define NV50_AUXCH_STAT_REPLY                                        0x000f0000
+#define NV50_AUXCH_STAT_REPLY_AUX                                    0x00030000
+#define NV50_AUXCH_STAT_REPLY_AUX_ACK                                0x00000000
+#define NV50_AUXCH_STAT_REPLY_AUX_NACK                               0x00010000
+#define NV50_AUXCH_STAT_REPLY_AUX_DEFER                              0x00020000
+#define NV50_AUXCH_STAT_REPLY_I2C                                    0x000c0000
+#define NV50_AUXCH_STAT_REPLY_I2C_ACK                                0x00000000
+#define NV50_AUXCH_STAT_REPLY_I2C_NACK                               0x00040000
+#define NV50_AUXCH_STAT_REPLY_I2C_DEFER                              0x00080000
+#define NV50_AUXCH_STAT_COUNT                                        0x0000001f
+
+#define NV50_PBUS                                           0x00088000
+#define NV50_PBUS__LEN                                             0x1
+#define NV50_PBUS__ESIZE                                        0x1000
+#    define NV50_PBUS_PCI_ID                                0x00088000
+#        define NV50_PBUS_PCI_ID_VENDOR_ID                  0x0000ffff
+#        define NV50_PBUS_PCI_ID_VENDOR_ID__SHIFT                    0
+#        define NV50_PBUS_PCI_ID_DEVICE_ID                  0xffff0000
+#        define NV50_PBUS_PCI_ID_DEVICE_ID__SHIFT                   16
+
+#define NV50_PFB                                            0x00100000
+#define NV50_PFB__LEN                                              0x1
+#define NV50_PFB__ESIZE                                         0x1000
+
+#define NV50_PEXTDEV                                        0x00101000
+#define NV50_PEXTDEV__LEN                                          0x1
+#define NV50_PEXTDEV__ESIZE                                     0x1000
+
+#define NV50_PROM                                           0x00300000
+#define NV50_PROM__LEN                                             0x1
+#define NV50_PROM__ESIZE                                       0x10000
+
+#define NV50_PGRAPH                                         0x00400000
+#define NV50_PGRAPH__LEN                                           0x1
+#define NV50_PGRAPH__ESIZE                                     0x10000
+
+#define NV50_PDISPLAY                                                0x00610000
+#define NV50_PDISPLAY_OBJECTS                                        0x00610010
+#define NV50_PDISPLAY_INTR_0                                         0x00610020
+#define NV50_PDISPLAY_INTR_1                                         0x00610024
+#define NV50_PDISPLAY_INTR_1_VBLANK_CRTC                             0x0000000c
+#define NV50_PDISPLAY_INTR_1_VBLANK_CRTC_SHIFT                                2
+#define NV50_PDISPLAY_INTR_1_VBLANK_CRTC_(n)                   (1 << ((n) + 2))
+#define NV50_PDISPLAY_INTR_1_VBLANK_CRTC_0                           0x00000004
+#define NV50_PDISPLAY_INTR_1_VBLANK_CRTC_1                           0x00000008
+#define NV50_PDISPLAY_INTR_1_CLK_UNK10                               0x00000010
+#define NV50_PDISPLAY_INTR_1_CLK_UNK20                               0x00000020
+#define NV50_PDISPLAY_INTR_1_CLK_UNK40                               0x00000040
+#define NV50_PDISPLAY_INTR_EN                                        0x0061002c
+#define NV50_PDISPLAY_INTR_EN_VBLANK_CRTC                            0x0000000c
+#define NV50_PDISPLAY_INTR_EN_VBLANK_CRTC_(n)                   (1 << ((n) + 2))
+#define NV50_PDISPLAY_INTR_EN_VBLANK_CRTC_0                          0x00000004
+#define NV50_PDISPLAY_INTR_EN_VBLANK_CRTC_1                          0x00000008
+#define NV50_PDISPLAY_INTR_EN_CLK_UNK10                              0x00000010
+#define NV50_PDISPLAY_INTR_EN_CLK_UNK20                              0x00000020
+#define NV50_PDISPLAY_INTR_EN_CLK_UNK40                              0x00000040
+#define NV50_PDISPLAY_UNK30_CTRL                                     0x00610030
+#define NV50_PDISPLAY_UNK30_CTRL_UPDATE_VCLK0                        0x00000200
+#define NV50_PDISPLAY_UNK30_CTRL_UPDATE_VCLK1                        0x00000400
+#define NV50_PDISPLAY_UNK30_CTRL_PENDING                             0x80000000
+#define NV50_PDISPLAY_TRAPPED_ADDR                                   0x00610080
+#define NV50_PDISPLAY_TRAPPED_DATA                                   0x00610084
+#define NV50_PDISPLAY_CHANNEL_STAT(i)                  ((i) * 0x10 + 0x00610200)
+#define NV50_PDISPLAY_CHANNEL_STAT_DMA                               0x00000010
+#define NV50_PDISPLAY_CHANNEL_STAT_DMA_DISABLED                      0x00000000
+#define NV50_PDISPLAY_CHANNEL_STAT_DMA_ENABLED                       0x00000010
+#define NV50_PDISPLAY_CHANNEL_DMA_CB(i)                ((i) * 0x10 + 0x00610204)
+#define NV50_PDISPLAY_CHANNEL_DMA_CB_LOCATION                        0x00000002
+#define NV50_PDISPLAY_CHANNEL_DMA_CB_LOCATION_VRAM                   0x00000000
+#define NV50_PDISPLAY_CHANNEL_DMA_CB_LOCATION_SYSTEM                 0x00000002
+#define NV50_PDISPLAY_CHANNEL_DMA_CB_VALID                           0x00000001
+#define NV50_PDISPLAY_CHANNEL_UNK2(i)                  ((i) * 0x10 + 0x00610208)
+#define NV50_PDISPLAY_CHANNEL_UNK3(i)                  ((i) * 0x10 + 0x0061020c)
+
+#define NV50_PDISPLAY_CURSOR                                         0x00610270
+#define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i)           ((i) * 0x10 + 0x00610270)
+#define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_ON                         0x00000001
+#define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS                     0x00030000
+#define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_ACTIVE              0x00010000
+
+#define NV50_PDISPLAY_CTRL_STATE                                     0x00610300
+#define NV50_PDISPLAY_CTRL_STATE_PENDING                             0x80000000
+#define NV50_PDISPLAY_CTRL_STATE_METHOD                              0x00001ffc
+#define NV50_PDISPLAY_CTRL_STATE_ENABLE                              0x00000001
+#define NV50_PDISPLAY_CTRL_VAL                                       0x00610304
+#define NV50_PDISPLAY_UNK_380                                        0x00610380
+#define NV50_PDISPLAY_RAM_AMOUNT                                     0x00610384
+#define NV50_PDISPLAY_UNK_388                                        0x00610388
+#define NV50_PDISPLAY_UNK_38C                                        0x0061038c
+
+#define NV50_PDISPLAY_CRTC_P(i, r)        ((i) * 0x540 + NV50_PDISPLAY_CRTC_##r)
+#define NV50_PDISPLAY_CRTC_C(i, r)    (4 + (i) * 0x540 + NV50_PDISPLAY_CRTC_##r)
+#define NV50_PDISPLAY_CRTC_UNK_0A18 /* mthd 0x0900 */                0x00610a18
+#define NV50_PDISPLAY_CRTC_CLUT_MODE                                 0x00610a24
+#define NV50_PDISPLAY_CRTC_INTERLACE                                 0x00610a48
+#define NV50_PDISPLAY_CRTC_SCALE_CTRL                                0x00610a50
+#define NV50_PDISPLAY_CRTC_CURSOR_CTRL                               0x00610a58
+#define NV50_PDISPLAY_CRTC_UNK0A78 /* mthd 0x0904 */                 0x00610a78
+#define NV50_PDISPLAY_CRTC_UNK0AB8                                   0x00610ab8
+#define NV50_PDISPLAY_CRTC_DEPTH                                     0x00610ac8
+#define NV50_PDISPLAY_CRTC_CLOCK                                     0x00610ad0
+#define NV50_PDISPLAY_CRTC_COLOR_CTRL                                0x00610ae0
+#define NV50_PDISPLAY_CRTC_SYNC_START_TO_BLANK_END                   0x00610ae8
+#define NV50_PDISPLAY_CRTC_MODE_UNK1                                 0x00610af0
+#define NV50_PDISPLAY_CRTC_DISPLAY_TOTAL                             0x00610af8
+#define NV50_PDISPLAY_CRTC_SYNC_DURATION                             0x00610b00
+#define NV50_PDISPLAY_CRTC_MODE_UNK2                                 0x00610b08
+#define NV50_PDISPLAY_CRTC_UNK_0B10 /* mthd 0x0828 */                0x00610b10
+#define NV50_PDISPLAY_CRTC_FB_SIZE                                   0x00610b18
+#define NV50_PDISPLAY_CRTC_FB_PITCH                                  0x00610b20
+#define NV50_PDISPLAY_CRTC_FB_PITCH_LINEAR                           0x00100000
+#define NV50_PDISPLAY_CRTC_FB_POS                                    0x00610b28
+#define NV50_PDISPLAY_CRTC_SCALE_CENTER_OFFSET                       0x00610b38
+#define NV50_PDISPLAY_CRTC_REAL_RES                                  0x00610b40
+#define NV50_PDISPLAY_CRTC_SCALE_RES1                                0x00610b48
+#define NV50_PDISPLAY_CRTC_SCALE_RES2                                0x00610b50
+
+#define NV50_PDISPLAY_DAC_MODE_CTRL_P(i)                (0x00610b58 + (i) * 0x8)
+#define NV50_PDISPLAY_DAC_MODE_CTRL_C(i)                (0x00610b5c + (i) * 0x8)
+#define NV50_PDISPLAY_SOR_MODE_CTRL_P(i)                (0x00610b70 + (i) * 0x8)
+#define NV50_PDISPLAY_SOR_MODE_CTRL_C(i)                (0x00610b74 + (i) * 0x8)
+#define NV50_PDISPLAY_DAC_MODE_CTRL2_P(i)               (0x00610bdc + (i) * 0x8)
+#define NV50_PDISPLAY_DAC_MODE_CTRL2_C(i)               (0x00610be0 + (i) * 0x8)
+
+#define NV90_PDISPLAY_SOR_MODE_CTRL_P(i)                (0x00610794 + (i) * 0x8)
+#define NV90_PDISPLAY_SOR_MODE_CTRL_C(i)                (0x00610798 + (i) * 0x8)
+#define NV90_PDISPLAY_DAC_MODE_CTRL_P(i)                (0x00610b58 + (i) * 0x8)
+#define NV90_PDISPLAY_DAC_MODE_CTRL_C(i)                (0x00610b5c + (i) * 0x8)
+#define NV90_PDISPLAY_DAC_MODE_CTRL2_P(i)               (0x00610b80 + (i) * 0x8)
+#define NV90_PDISPLAY_DAC_MODE_CTRL2_C(i)               (0x00610b84 + (i) * 0x8)
+
+#define NV50_PDISPLAY_CRTC_CLK                                       0x00614000
+#define NV50_PDISPLAY_CRTC_CLK_CTRL1(i)                 ((i) * 0x800 + 0x614100)
+#define NV50_PDISPLAY_CRTC_CLK_CTRL1_CONNECTED                       0x00000600
+#define NV50_PDISPLAY_CRTC_CLK_VPLL_A(i)                ((i) * 0x800 + 0x614104)
+#define NV50_PDISPLAY_CRTC_CLK_VPLL_B(i)                ((i) * 0x800 + 0x614108)
+#define NV50_PDISPLAY_CRTC_CLK_CTRL2(i)                 ((i) * 0x800 + 0x614200)
+
+#define NV50_PDISPLAY_DAC_CLK                                        0x00614000
+#define NV50_PDISPLAY_DAC_CLK_CTRL2(i)                  ((i) * 0x800 + 0x614280)
+
+#define NV50_PDISPLAY_SOR_CLK                                        0x00614000
+#define NV50_PDISPLAY_SOR_CLK_CTRL2(i)                  ((i) * 0x800 + 0x614300)
+
+#define NV50_PDISPLAY_VGACRTC(r)                                ((r) + 0x619400)
+
+#define NV50_PDISPLAY_DAC                                            0x0061a000
+#define NV50_PDISPLAY_DAC_DPMS_CTRL(i)                (0x0061a004 + (i) * 0x800)
+#define NV50_PDISPLAY_DAC_DPMS_CTRL_HSYNC_OFF                        0x00000001
+#define NV50_PDISPLAY_DAC_DPMS_CTRL_VSYNC_OFF                        0x00000004
+#define NV50_PDISPLAY_DAC_DPMS_CTRL_BLANKED                          0x00000010
+#define NV50_PDISPLAY_DAC_DPMS_CTRL_OFF                              0x00000040
+#define NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING                          0x80000000
+#define NV50_PDISPLAY_DAC_LOAD_CTRL(i)                (0x0061a00c + (i) * 0x800)
+#define NV50_PDISPLAY_DAC_LOAD_CTRL_ACTIVE                           0x00100000
+#define NV50_PDISPLAY_DAC_LOAD_CTRL_PRESENT                          0x38000000
+#define NV50_PDISPLAY_DAC_LOAD_CTRL_DONE                             0x80000000
+#define NV50_PDISPLAY_DAC_CLK_CTRL1(i)                (0x0061a010 + (i) * 0x800)
+#define NV50_PDISPLAY_DAC_CLK_CTRL1_CONNECTED                        0x00000600
+
+#define NV50_PDISPLAY_SOR                                            0x0061c000
+#define NV50_PDISPLAY_SOR_DPMS_CTRL(i)                (0x0061c004 + (i) * 0x800)
+#define NV50_PDISPLAY_SOR_DPMS_CTRL_PENDING                          0x80000000
+#define NV50_PDISPLAY_SOR_DPMS_CTRL_ON                               0x00000001
+#define NV50_PDISPLAY_SOR_CLK_CTRL1(i)                (0x0061c008 + (i) * 0x800)
+#define NV50_PDISPLAY_SOR_CLK_CTRL1_CONNECTED                        0x00000600
+#define NV50_PDISPLAY_SOR_DPMS_STATE(i)               (0x0061c030 + (i) * 0x800)
+#define NV50_PDISPLAY_SOR_DPMS_STATE_ACTIVE                          0x00030000
+#define NV50_PDISPLAY_SOR_DPMS_STATE_BLANKED                         0x00080000
+#define NV50_PDISPLAY_SOR_DPMS_STATE_WAIT                            0x10000000
+#define NV50_PDISPLAY_SOR_BACKLIGHT                                  0x0061c084
+#define NV50_PDISPLAY_SOR_BACKLIGHT_ENABLE                           0x80000000
+#define NV50_PDISPLAY_SOR_BACKLIGHT_LEVEL                            0x00000fff
+#define NV50_SOR_DP_CTRL(i,l)            (0x0061c10c + (i) * 0x800 + (l) * 0x80)
+#define NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED                      0x00004000
+#define NV50_SOR_DP_CTRL_LANE_MASK                                   0x001f0000
+#define NV50_SOR_DP_CTRL_LANE_0_ENABLED                              0x00010000
+#define NV50_SOR_DP_CTRL_LANE_1_ENABLED                              0x00020000
+#define NV50_SOR_DP_CTRL_LANE_2_ENABLED                              0x00040000
+#define NV50_SOR_DP_CTRL_LANE_3_ENABLED                              0x00080000
+#define NV50_SOR_DP_CTRL_TRAINING_PATTERN                            0x0f000000
+#define NV50_SOR_DP_CTRL_TRAINING_PATTERN_DISABLED                   0x00000000
+#define NV50_SOR_DP_CTRL_TRAINING_PATTERN_1                          0x01000000
+#define NV50_SOR_DP_CTRL_TRAINING_PATTERN_2                          0x02000000
+#define NV50_SOR_DP_UNK118(i,l)          (0x0061c118 + (i) * 0x800 + (l) * 0x80)
+#define NV50_SOR_DP_UNK120(i,l)          (0x0061c120 + (i) * 0x800 + (l) * 0x80)
+#define NV50_SOR_DP_UNK130(i,l)          (0x0061c130 + (i) * 0x800 + (l) * 0x80)
+
+#define NV50_PDISPLAY_USER(i)                        ((i) * 0x1000 + 0x00640000)
+#define NV50_PDISPLAY_USER_PUT(i)                    ((i) * 0x1000 + 0x00640000)
+#define NV50_PDISPLAY_USER_GET(i)                    ((i) * 0x1000 + 0x00640004)
+
+#define NV50_PDISPLAY_CURSOR_USER                                    0x00647000
+#define NV50_PDISPLAY_CURSOR_USER_POS_CTRL(i)        ((i) * 0x1000 + 0x00647080)
+#define NV50_PDISPLAY_CURSOR_USER_POS(i)             ((i) * 0x1000 + 0x00647084)
diff --git a/drivers/gpu/drm/nouveau/nouveau_sgdma.c b/drivers/gpu/drm/nouveau/nouveau_sgdma.c
new file mode 100644
index 0000000..4c7f1e4
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_sgdma.c
@@ -0,0 +1,321 @@
+#include "drmP.h"
+#include "nouveau_drv.h"
+#include <linux/pagemap.h>
+
+#define NV_CTXDMA_PAGE_SHIFT 12
+#define NV_CTXDMA_PAGE_SIZE  (1 << NV_CTXDMA_PAGE_SHIFT)
+#define NV_CTXDMA_PAGE_MASK  (NV_CTXDMA_PAGE_SIZE - 1)
+
+struct nouveau_sgdma_be {
+	struct ttm_backend backend;
+	struct drm_device *dev;
+
+	dma_addr_t *pages;
+	unsigned nr_pages;
+
+	unsigned pte_start;
+	bool bound;
+};
+
+static int
+nouveau_sgdma_populate(struct ttm_backend *be, unsigned long num_pages,
+		       struct page **pages, struct page *dummy_read_page)
+{
+	struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;
+	struct drm_device *dev = nvbe->dev;
+
+	NV_DEBUG(nvbe->dev, "num_pages = %ld\n", num_pages);
+
+	if (nvbe->pages)
+		return -EINVAL;
+
+	nvbe->pages = kmalloc(sizeof(dma_addr_t) * num_pages, GFP_KERNEL);
+	if (!nvbe->pages)
+		return -ENOMEM;
+
+	nvbe->nr_pages = 0;
+	while (num_pages--) {
+		nvbe->pages[nvbe->nr_pages] =
+			pci_map_page(dev->pdev, pages[nvbe->nr_pages], 0,
+				     PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
+		if (pci_dma_mapping_error(dev->pdev,
+					  nvbe->pages[nvbe->nr_pages])) {
+			be->func->clear(be);
+			return -EFAULT;
+		}
+
+		nvbe->nr_pages++;
+	}
+
+	return 0;
+}
+
+static void
+nouveau_sgdma_clear(struct ttm_backend *be)
+{
+	struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;
+	struct drm_device *dev = nvbe->dev;
+
+	NV_DEBUG(nvbe->dev, "\n");
+
+	if (nvbe && nvbe->pages) {
+		if (nvbe->bound)
+			be->func->unbind(be);
+
+		while (nvbe->nr_pages--) {
+			pci_unmap_page(dev->pdev, nvbe->pages[nvbe->nr_pages],
+				       PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
+		}
+		kfree(nvbe->pages);
+		nvbe->pages = NULL;
+		nvbe->nr_pages = 0;
+	}
+}
+
+static inline unsigned
+nouveau_sgdma_pte(struct drm_device *dev, uint64_t offset)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	unsigned pte = (offset >> NV_CTXDMA_PAGE_SHIFT);
+
+	if (dev_priv->card_type < NV_50)
+		return pte + 2;
+
+	return pte << 1;
+}
+
+static int
+nouveau_sgdma_bind(struct ttm_backend *be, struct ttm_mem_reg *mem)
+{
+	struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;
+	struct drm_device *dev = nvbe->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_gpuobj *gpuobj = dev_priv->gart_info.sg_ctxdma;
+	unsigned i, j, pte;
+
+	NV_DEBUG(dev, "pg=0x%lx\n", mem->mm_node->start);
+
+	dev_priv->engine.instmem.prepare_access(nvbe->dev, true);
+	pte = nouveau_sgdma_pte(nvbe->dev, mem->mm_node->start << PAGE_SHIFT);
+	nvbe->pte_start = pte;
+	for (i = 0; i < nvbe->nr_pages; i++) {
+		dma_addr_t dma_offset = nvbe->pages[i];
+		uint32_t offset_l = lower_32_bits(dma_offset);
+		uint32_t offset_h = upper_32_bits(dma_offset);
+
+		for (j = 0; j < PAGE_SIZE / NV_CTXDMA_PAGE_SIZE; j++) {
+			if (dev_priv->card_type < NV_50)
+				nv_wo32(dev, gpuobj, pte++, offset_l | 3);
+			else {
+				nv_wo32(dev, gpuobj, pte++, offset_l | 0x21);
+				nv_wo32(dev, gpuobj, pte++, offset_h & 0xff);
+			}
+
+			dma_offset += NV_CTXDMA_PAGE_SIZE;
+		}
+	}
+	dev_priv->engine.instmem.finish_access(nvbe->dev);
+
+	if (dev_priv->card_type == NV_50) {
+		nv_wr32(dev, 0x100c80, 0x00050001);
+		if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
+			NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
+			NV_ERROR(dev, "0x100c80 = 0x%08x\n",
+						nv_rd32(dev, 0x100c80));
+			return -EBUSY;
+		}
+
+		nv_wr32(dev, 0x100c80, 0x00000001);
+		if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
+			NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
+			NV_ERROR(dev, "0x100c80 = 0x%08x\n",
+						nv_rd32(dev, 0x100c80));
+			return -EBUSY;
+		}
+	}
+
+	nvbe->bound = true;
+	return 0;
+}
+
+static int
+nouveau_sgdma_unbind(struct ttm_backend *be)
+{
+	struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;
+	struct drm_device *dev = nvbe->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_gpuobj *gpuobj = dev_priv->gart_info.sg_ctxdma;
+	unsigned i, j, pte;
+
+	NV_DEBUG(dev, "\n");
+
+	if (!nvbe->bound)
+		return 0;
+
+	dev_priv->engine.instmem.prepare_access(nvbe->dev, true);
+	pte = nvbe->pte_start;
+	for (i = 0; i < nvbe->nr_pages; i++) {
+		dma_addr_t dma_offset = dev_priv->gart_info.sg_dummy_bus;
+
+		for (j = 0; j < PAGE_SIZE / NV_CTXDMA_PAGE_SIZE; j++) {
+			if (dev_priv->card_type < NV_50)
+				nv_wo32(dev, gpuobj, pte++, dma_offset | 3);
+			else {
+				nv_wo32(dev, gpuobj, pte++, dma_offset | 0x21);
+				nv_wo32(dev, gpuobj, pte++, 0x00000000);
+			}
+
+			dma_offset += NV_CTXDMA_PAGE_SIZE;
+		}
+	}
+	dev_priv->engine.instmem.finish_access(nvbe->dev);
+
+	nvbe->bound = false;
+	return 0;
+}
+
+static void
+nouveau_sgdma_destroy(struct ttm_backend *be)
+{
+	struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;
+
+	if (be) {
+		NV_DEBUG(nvbe->dev, "\n");
+
+		if (nvbe) {
+			if (nvbe->pages)
+				be->func->clear(be);
+			kfree(nvbe);
+		}
+	}
+}
+
+static struct ttm_backend_func nouveau_sgdma_backend = {
+	.populate		= nouveau_sgdma_populate,
+	.clear			= nouveau_sgdma_clear,
+	.bind			= nouveau_sgdma_bind,
+	.unbind			= nouveau_sgdma_unbind,
+	.destroy		= nouveau_sgdma_destroy
+};
+
+struct ttm_backend *
+nouveau_sgdma_init_ttm(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_sgdma_be *nvbe;
+
+	if (!dev_priv->gart_info.sg_ctxdma)
+		return NULL;
+
+	nvbe = kzalloc(sizeof(*nvbe), GFP_KERNEL);
+	if (!nvbe)
+		return NULL;
+
+	nvbe->dev = dev;
+
+	nvbe->backend.func	= &nouveau_sgdma_backend;
+
+	return &nvbe->backend;
+}
+
+int
+nouveau_sgdma_init(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_gpuobj *gpuobj = NULL;
+	uint32_t aper_size, obj_size;
+	int i, ret;
+
+	if (dev_priv->card_type < NV_50) {
+		aper_size = (64 * 1024 * 1024);
+		obj_size  = (aper_size >> NV_CTXDMA_PAGE_SHIFT) * 4;
+		obj_size += 8; /* ctxdma header */
+	} else {
+		/* 1 entire VM page table */
+		aper_size = (512 * 1024 * 1024);
+		obj_size  = (aper_size >> NV_CTXDMA_PAGE_SHIFT) * 8;
+	}
+
+	ret = nouveau_gpuobj_new(dev, NULL, obj_size, 16,
+				      NVOBJ_FLAG_ALLOW_NO_REFS |
+				      NVOBJ_FLAG_ZERO_ALLOC |
+				      NVOBJ_FLAG_ZERO_FREE, &gpuobj);
+	if (ret) {
+		NV_ERROR(dev, "Error creating sgdma object: %d\n", ret);
+		return ret;
+	}
+
+	dev_priv->gart_info.sg_dummy_page =
+		alloc_page(GFP_KERNEL|__GFP_DMA32);
+	set_bit(PG_locked, &dev_priv->gart_info.sg_dummy_page->flags);
+	dev_priv->gart_info.sg_dummy_bus =
+		pci_map_page(dev->pdev, dev_priv->gart_info.sg_dummy_page, 0,
+			     PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
+
+	dev_priv->engine.instmem.prepare_access(dev, true);
+	if (dev_priv->card_type < NV_50) {
+		/* Maybe use NV_DMA_TARGET_AGP for PCIE? NVIDIA do this, and
+		 * confirmed to work on c51.  Perhaps means NV_DMA_TARGET_PCIE
+		 * on those cards? */
+		nv_wo32(dev, gpuobj, 0, NV_CLASS_DMA_IN_MEMORY |
+				       (1 << 12) /* PT present */ |
+				       (0 << 13) /* PT *not* linear */ |
+				       (NV_DMA_ACCESS_RW  << 14) |
+				       (NV_DMA_TARGET_PCI << 16));
+		nv_wo32(dev, gpuobj, 1, aper_size - 1);
+		for (i = 2; i < 2 + (aper_size >> 12); i++) {
+			nv_wo32(dev, gpuobj, i,
+				    dev_priv->gart_info.sg_dummy_bus | 3);
+		}
+	} else {
+		for (i = 0; i < obj_size; i += 8) {
+			nv_wo32(dev, gpuobj, (i+0)/4,
+				    dev_priv->gart_info.sg_dummy_bus | 0x21);
+			nv_wo32(dev, gpuobj, (i+4)/4, 0);
+		}
+	}
+	dev_priv->engine.instmem.finish_access(dev);
+
+	dev_priv->gart_info.type      = NOUVEAU_GART_SGDMA;
+	dev_priv->gart_info.aper_base = 0;
+	dev_priv->gart_info.aper_size = aper_size;
+	dev_priv->gart_info.sg_ctxdma = gpuobj;
+	return 0;
+}
+
+void
+nouveau_sgdma_takedown(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	if (dev_priv->gart_info.sg_dummy_page) {
+		pci_unmap_page(dev->pdev, dev_priv->gart_info.sg_dummy_bus,
+			       NV_CTXDMA_PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
+		unlock_page(dev_priv->gart_info.sg_dummy_page);
+		__free_page(dev_priv->gart_info.sg_dummy_page);
+		dev_priv->gart_info.sg_dummy_page = NULL;
+		dev_priv->gart_info.sg_dummy_bus = 0;
+	}
+
+	nouveau_gpuobj_del(dev, &dev_priv->gart_info.sg_ctxdma);
+}
+
+int
+nouveau_sgdma_get_page(struct drm_device *dev, uint32_t offset, uint32_t *page)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_gpuobj *gpuobj = dev_priv->gart_info.sg_ctxdma;
+	struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem;
+	int pte;
+
+	pte = (offset >> NV_CTXDMA_PAGE_SHIFT);
+	if (dev_priv->card_type < NV_50) {
+		instmem->prepare_access(dev, false);
+		*page = nv_ro32(dev, gpuobj, (pte + 2)) & ~NV_CTXDMA_PAGE_MASK;
+		instmem->finish_access(dev);
+		return 0;
+	}
+
+	NV_ERROR(dev, "Unimplemented on NV50\n");
+	return -EINVAL;
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
new file mode 100644
index 0000000..2ed41d3
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_state.c
@@ -0,0 +1,811 @@
+/*
+ * Copyright 2005 Stephane Marchesin
+ * Copyright 2008 Stuart Bennett
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <linux/swab.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_sarea.h"
+#include "drm_crtc_helper.h"
+#include <linux/vgaarb.h>
+
+#include "nouveau_drv.h"
+#include "nouveau_drm.h"
+#include "nv50_display.h"
+
+static int nouveau_stub_init(struct drm_device *dev) { return 0; }
+static void nouveau_stub_takedown(struct drm_device *dev) {}
+
+static int nouveau_init_engine_ptrs(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_engine *engine = &dev_priv->engine;
+
+	switch (dev_priv->chipset & 0xf0) {
+	case 0x00:
+		engine->instmem.init		= nv04_instmem_init;
+		engine->instmem.takedown	= nv04_instmem_takedown;
+		engine->instmem.suspend		= nv04_instmem_suspend;
+		engine->instmem.resume		= nv04_instmem_resume;
+		engine->instmem.populate	= nv04_instmem_populate;
+		engine->instmem.clear		= nv04_instmem_clear;
+		engine->instmem.bind		= nv04_instmem_bind;
+		engine->instmem.unbind		= nv04_instmem_unbind;
+		engine->instmem.prepare_access	= nv04_instmem_prepare_access;
+		engine->instmem.finish_access	= nv04_instmem_finish_access;
+		engine->mc.init			= nv04_mc_init;
+		engine->mc.takedown		= nv04_mc_takedown;
+		engine->timer.init		= nv04_timer_init;
+		engine->timer.read		= nv04_timer_read;
+		engine->timer.takedown		= nv04_timer_takedown;
+		engine->fb.init			= nv04_fb_init;
+		engine->fb.takedown		= nv04_fb_takedown;
+		engine->graph.grclass		= nv04_graph_grclass;
+		engine->graph.init		= nv04_graph_init;
+		engine->graph.takedown		= nv04_graph_takedown;
+		engine->graph.fifo_access	= nv04_graph_fifo_access;
+		engine->graph.channel		= nv04_graph_channel;
+		engine->graph.create_context	= nv04_graph_create_context;
+		engine->graph.destroy_context	= nv04_graph_destroy_context;
+		engine->graph.load_context	= nv04_graph_load_context;
+		engine->graph.unload_context	= nv04_graph_unload_context;
+		engine->fifo.channels		= 16;
+		engine->fifo.init		= nv04_fifo_init;
+		engine->fifo.takedown		= nouveau_stub_takedown;
+		engine->fifo.disable		= nv04_fifo_disable;
+		engine->fifo.enable		= nv04_fifo_enable;
+		engine->fifo.reassign		= nv04_fifo_reassign;
+		engine->fifo.channel_id		= nv04_fifo_channel_id;
+		engine->fifo.create_context	= nv04_fifo_create_context;
+		engine->fifo.destroy_context	= nv04_fifo_destroy_context;
+		engine->fifo.load_context	= nv04_fifo_load_context;
+		engine->fifo.unload_context	= nv04_fifo_unload_context;
+		break;
+	case 0x10:
+		engine->instmem.init		= nv04_instmem_init;
+		engine->instmem.takedown	= nv04_instmem_takedown;
+		engine->instmem.suspend		= nv04_instmem_suspend;
+		engine->instmem.resume		= nv04_instmem_resume;
+		engine->instmem.populate	= nv04_instmem_populate;
+		engine->instmem.clear		= nv04_instmem_clear;
+		engine->instmem.bind		= nv04_instmem_bind;
+		engine->instmem.unbind		= nv04_instmem_unbind;
+		engine->instmem.prepare_access	= nv04_instmem_prepare_access;
+		engine->instmem.finish_access	= nv04_instmem_finish_access;
+		engine->mc.init			= nv04_mc_init;
+		engine->mc.takedown		= nv04_mc_takedown;
+		engine->timer.init		= nv04_timer_init;
+		engine->timer.read		= nv04_timer_read;
+		engine->timer.takedown		= nv04_timer_takedown;
+		engine->fb.init			= nv10_fb_init;
+		engine->fb.takedown		= nv10_fb_takedown;
+		engine->graph.grclass		= nv10_graph_grclass;
+		engine->graph.init		= nv10_graph_init;
+		engine->graph.takedown		= nv10_graph_takedown;
+		engine->graph.channel		= nv10_graph_channel;
+		engine->graph.create_context	= nv10_graph_create_context;
+		engine->graph.destroy_context	= nv10_graph_destroy_context;
+		engine->graph.fifo_access	= nv04_graph_fifo_access;
+		engine->graph.load_context	= nv10_graph_load_context;
+		engine->graph.unload_context	= nv10_graph_unload_context;
+		engine->fifo.channels		= 32;
+		engine->fifo.init		= nv10_fifo_init;
+		engine->fifo.takedown		= nouveau_stub_takedown;
+		engine->fifo.disable		= nv04_fifo_disable;
+		engine->fifo.enable		= nv04_fifo_enable;
+		engine->fifo.reassign		= nv04_fifo_reassign;
+		engine->fifo.channel_id		= nv10_fifo_channel_id;
+		engine->fifo.create_context	= nv10_fifo_create_context;
+		engine->fifo.destroy_context	= nv10_fifo_destroy_context;
+		engine->fifo.load_context	= nv10_fifo_load_context;
+		engine->fifo.unload_context	= nv10_fifo_unload_context;
+		break;
+	case 0x20:
+		engine->instmem.init		= nv04_instmem_init;
+		engine->instmem.takedown	= nv04_instmem_takedown;
+		engine->instmem.suspend		= nv04_instmem_suspend;
+		engine->instmem.resume		= nv04_instmem_resume;
+		engine->instmem.populate	= nv04_instmem_populate;
+		engine->instmem.clear		= nv04_instmem_clear;
+		engine->instmem.bind		= nv04_instmem_bind;
+		engine->instmem.unbind		= nv04_instmem_unbind;
+		engine->instmem.prepare_access	= nv04_instmem_prepare_access;
+		engine->instmem.finish_access	= nv04_instmem_finish_access;
+		engine->mc.init			= nv04_mc_init;
+		engine->mc.takedown		= nv04_mc_takedown;
+		engine->timer.init		= nv04_timer_init;
+		engine->timer.read		= nv04_timer_read;
+		engine->timer.takedown		= nv04_timer_takedown;
+		engine->fb.init			= nv10_fb_init;
+		engine->fb.takedown		= nv10_fb_takedown;
+		engine->graph.grclass		= nv20_graph_grclass;
+		engine->graph.init		= nv20_graph_init;
+		engine->graph.takedown		= nv20_graph_takedown;
+		engine->graph.channel		= nv10_graph_channel;
+		engine->graph.create_context	= nv20_graph_create_context;
+		engine->graph.destroy_context	= nv20_graph_destroy_context;
+		engine->graph.fifo_access	= nv04_graph_fifo_access;
+		engine->graph.load_context	= nv20_graph_load_context;
+		engine->graph.unload_context	= nv20_graph_unload_context;
+		engine->fifo.channels		= 32;
+		engine->fifo.init		= nv10_fifo_init;
+		engine->fifo.takedown		= nouveau_stub_takedown;
+		engine->fifo.disable		= nv04_fifo_disable;
+		engine->fifo.enable		= nv04_fifo_enable;
+		engine->fifo.reassign		= nv04_fifo_reassign;
+		engine->fifo.channel_id		= nv10_fifo_channel_id;
+		engine->fifo.create_context	= nv10_fifo_create_context;
+		engine->fifo.destroy_context	= nv10_fifo_destroy_context;
+		engine->fifo.load_context	= nv10_fifo_load_context;
+		engine->fifo.unload_context	= nv10_fifo_unload_context;
+		break;
+	case 0x30:
+		engine->instmem.init		= nv04_instmem_init;
+		engine->instmem.takedown	= nv04_instmem_takedown;
+		engine->instmem.suspend		= nv04_instmem_suspend;
+		engine->instmem.resume		= nv04_instmem_resume;
+		engine->instmem.populate	= nv04_instmem_populate;
+		engine->instmem.clear		= nv04_instmem_clear;
+		engine->instmem.bind		= nv04_instmem_bind;
+		engine->instmem.unbind		= nv04_instmem_unbind;
+		engine->instmem.prepare_access	= nv04_instmem_prepare_access;
+		engine->instmem.finish_access	= nv04_instmem_finish_access;
+		engine->mc.init			= nv04_mc_init;
+		engine->mc.takedown		= nv04_mc_takedown;
+		engine->timer.init		= nv04_timer_init;
+		engine->timer.read		= nv04_timer_read;
+		engine->timer.takedown		= nv04_timer_takedown;
+		engine->fb.init			= nv10_fb_init;
+		engine->fb.takedown		= nv10_fb_takedown;
+		engine->graph.grclass		= nv30_graph_grclass;
+		engine->graph.init		= nv30_graph_init;
+		engine->graph.takedown		= nv20_graph_takedown;
+		engine->graph.fifo_access	= nv04_graph_fifo_access;
+		engine->graph.channel		= nv10_graph_channel;
+		engine->graph.create_context	= nv20_graph_create_context;
+		engine->graph.destroy_context	= nv20_graph_destroy_context;
+		engine->graph.load_context	= nv20_graph_load_context;
+		engine->graph.unload_context	= nv20_graph_unload_context;
+		engine->fifo.channels		= 32;
+		engine->fifo.init		= nv10_fifo_init;
+		engine->fifo.takedown		= nouveau_stub_takedown;
+		engine->fifo.disable		= nv04_fifo_disable;
+		engine->fifo.enable		= nv04_fifo_enable;
+		engine->fifo.reassign		= nv04_fifo_reassign;
+		engine->fifo.channel_id		= nv10_fifo_channel_id;
+		engine->fifo.create_context	= nv10_fifo_create_context;
+		engine->fifo.destroy_context	= nv10_fifo_destroy_context;
+		engine->fifo.load_context	= nv10_fifo_load_context;
+		engine->fifo.unload_context	= nv10_fifo_unload_context;
+		break;
+	case 0x40:
+	case 0x60:
+		engine->instmem.init		= nv04_instmem_init;
+		engine->instmem.takedown	= nv04_instmem_takedown;
+		engine->instmem.suspend		= nv04_instmem_suspend;
+		engine->instmem.resume		= nv04_instmem_resume;
+		engine->instmem.populate	= nv04_instmem_populate;
+		engine->instmem.clear		= nv04_instmem_clear;
+		engine->instmem.bind		= nv04_instmem_bind;
+		engine->instmem.unbind		= nv04_instmem_unbind;
+		engine->instmem.prepare_access	= nv04_instmem_prepare_access;
+		engine->instmem.finish_access	= nv04_instmem_finish_access;
+		engine->mc.init			= nv40_mc_init;
+		engine->mc.takedown		= nv40_mc_takedown;
+		engine->timer.init		= nv04_timer_init;
+		engine->timer.read		= nv04_timer_read;
+		engine->timer.takedown		= nv04_timer_takedown;
+		engine->fb.init			= nv40_fb_init;
+		engine->fb.takedown		= nv40_fb_takedown;
+		engine->graph.grclass		= nv40_graph_grclass;
+		engine->graph.init		= nv40_graph_init;
+		engine->graph.takedown		= nv40_graph_takedown;
+		engine->graph.fifo_access	= nv04_graph_fifo_access;
+		engine->graph.channel		= nv40_graph_channel;
+		engine->graph.create_context	= nv40_graph_create_context;
+		engine->graph.destroy_context	= nv40_graph_destroy_context;
+		engine->graph.load_context	= nv40_graph_load_context;
+		engine->graph.unload_context	= nv40_graph_unload_context;
+		engine->fifo.channels		= 32;
+		engine->fifo.init		= nv40_fifo_init;
+		engine->fifo.takedown		= nouveau_stub_takedown;
+		engine->fifo.disable		= nv04_fifo_disable;
+		engine->fifo.enable		= nv04_fifo_enable;
+		engine->fifo.reassign		= nv04_fifo_reassign;
+		engine->fifo.channel_id		= nv10_fifo_channel_id;
+		engine->fifo.create_context	= nv40_fifo_create_context;
+		engine->fifo.destroy_context	= nv40_fifo_destroy_context;
+		engine->fifo.load_context	= nv40_fifo_load_context;
+		engine->fifo.unload_context	= nv40_fifo_unload_context;
+		break;
+	case 0x50:
+	case 0x80: /* gotta love NVIDIA's consistency.. */
+	case 0x90:
+	case 0xA0:
+		engine->instmem.init		= nv50_instmem_init;
+		engine->instmem.takedown	= nv50_instmem_takedown;
+		engine->instmem.suspend		= nv50_instmem_suspend;
+		engine->instmem.resume		= nv50_instmem_resume;
+		engine->instmem.populate	= nv50_instmem_populate;
+		engine->instmem.clear		= nv50_instmem_clear;
+		engine->instmem.bind		= nv50_instmem_bind;
+		engine->instmem.unbind		= nv50_instmem_unbind;
+		engine->instmem.prepare_access	= nv50_instmem_prepare_access;
+		engine->instmem.finish_access	= nv50_instmem_finish_access;
+		engine->mc.init			= nv50_mc_init;
+		engine->mc.takedown		= nv50_mc_takedown;
+		engine->timer.init		= nv04_timer_init;
+		engine->timer.read		= nv04_timer_read;
+		engine->timer.takedown		= nv04_timer_takedown;
+		engine->fb.init			= nouveau_stub_init;
+		engine->fb.takedown		= nouveau_stub_takedown;
+		engine->graph.grclass		= nv50_graph_grclass;
+		engine->graph.init		= nv50_graph_init;
+		engine->graph.takedown		= nv50_graph_takedown;
+		engine->graph.fifo_access	= nv50_graph_fifo_access;
+		engine->graph.channel		= nv50_graph_channel;
+		engine->graph.create_context	= nv50_graph_create_context;
+		engine->graph.destroy_context	= nv50_graph_destroy_context;
+		engine->graph.load_context	= nv50_graph_load_context;
+		engine->graph.unload_context	= nv50_graph_unload_context;
+		engine->fifo.channels		= 128;
+		engine->fifo.init		= nv50_fifo_init;
+		engine->fifo.takedown		= nv50_fifo_takedown;
+		engine->fifo.disable		= nv04_fifo_disable;
+		engine->fifo.enable		= nv04_fifo_enable;
+		engine->fifo.reassign		= nv04_fifo_reassign;
+		engine->fifo.channel_id		= nv50_fifo_channel_id;
+		engine->fifo.create_context	= nv50_fifo_create_context;
+		engine->fifo.destroy_context	= nv50_fifo_destroy_context;
+		engine->fifo.load_context	= nv50_fifo_load_context;
+		engine->fifo.unload_context	= nv50_fifo_unload_context;
+		break;
+	default:
+		NV_ERROR(dev, "NV%02x unsupported\n", dev_priv->chipset);
+		return 1;
+	}
+
+	return 0;
+}
+
+static unsigned int
+nouveau_vga_set_decode(void *priv, bool state)
+{
+	if (state)
+		return VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM |
+		       VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM;
+	else
+		return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM;
+}
+
+int
+nouveau_card_init(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_engine *engine;
+	struct nouveau_gpuobj *gpuobj;
+	int ret;
+
+	NV_DEBUG(dev, "prev state = %d\n", dev_priv->init_state);
+
+	if (dev_priv->init_state == NOUVEAU_CARD_INIT_DONE)
+		return 0;
+
+	vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode);
+
+	/* Initialise internal driver API hooks */
+	ret = nouveau_init_engine_ptrs(dev);
+	if (ret)
+		return ret;
+	engine = &dev_priv->engine;
+	dev_priv->init_state = NOUVEAU_CARD_INIT_FAILED;
+
+	/* Parse BIOS tables / Run init tables if card not POSTed */
+	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+		ret = nouveau_bios_init(dev);
+		if (ret)
+			return ret;
+	}
+
+	ret = nouveau_gpuobj_early_init(dev);
+	if (ret)
+		return ret;
+
+	/* Initialise instance memory, must happen before mem_init so we
+	 * know exactly how much VRAM we're able to use for "normal"
+	 * purposes.
+	 */
+	ret = engine->instmem.init(dev);
+	if (ret)
+		return ret;
+
+	/* Setup the memory manager */
+	ret = nouveau_mem_init(dev);
+	if (ret)
+		return ret;
+
+	ret = nouveau_gpuobj_init(dev);
+	if (ret)
+		return ret;
+
+	/* PMC */
+	ret = engine->mc.init(dev);
+	if (ret)
+		return ret;
+
+	/* PTIMER */
+	ret = engine->timer.init(dev);
+	if (ret)
+		return ret;
+
+	/* PFB */
+	ret = engine->fb.init(dev);
+	if (ret)
+		return ret;
+
+	/* PGRAPH */
+	ret = engine->graph.init(dev);
+	if (ret)
+		return ret;
+
+	/* PFIFO */
+	ret = engine->fifo.init(dev);
+	if (ret)
+		return ret;
+
+	/* this call irq_preinstall, register irq handler and
+	 * call irq_postinstall
+	 */
+	ret = drm_irq_install(dev);
+	if (ret)
+		return ret;
+
+	ret = drm_vblank_init(dev, 0);
+	if (ret)
+		return ret;
+
+	/* what about PVIDEO/PCRTC/PRAMDAC etc? */
+
+	ret = nouveau_channel_alloc(dev, &dev_priv->channel,
+				    (struct drm_file *)-2,
+				    NvDmaFB, NvDmaTT);
+	if (ret)
+		return ret;
+
+	gpuobj = NULL;
+	ret = nouveau_gpuobj_dma_new(dev_priv->channel, NV_CLASS_DMA_IN_MEMORY,
+				     0, nouveau_mem_fb_amount(dev),
+				     NV_DMA_ACCESS_RW, NV_DMA_TARGET_VIDMEM,
+				     &gpuobj);
+	if (ret)
+		return ret;
+
+	ret = nouveau_gpuobj_ref_add(dev, dev_priv->channel, NvDmaVRAM,
+				     gpuobj, NULL);
+	if (ret) {
+		nouveau_gpuobj_del(dev, &gpuobj);
+		return ret;
+	}
+
+	gpuobj = NULL;
+	ret = nouveau_gpuobj_gart_dma_new(dev_priv->channel, 0,
+					  dev_priv->gart_info.aper_size,
+					  NV_DMA_ACCESS_RW, &gpuobj, NULL);
+	if (ret)
+		return ret;
+
+	ret = nouveau_gpuobj_ref_add(dev, dev_priv->channel, NvDmaGART,
+				     gpuobj, NULL);
+	if (ret) {
+		nouveau_gpuobj_del(dev, &gpuobj);
+		return ret;
+	}
+
+	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+		if (dev_priv->card_type >= NV_50) {
+			ret = nv50_display_create(dev);
+			if (ret)
+				return ret;
+		} else {
+			ret = nv04_display_create(dev);
+			if (ret)
+				return ret;
+		}
+	}
+
+	ret = nouveau_backlight_init(dev);
+	if (ret)
+		NV_ERROR(dev, "Error %d registering backlight\n", ret);
+
+	dev_priv->init_state = NOUVEAU_CARD_INIT_DONE;
+
+	if (drm_core_check_feature(dev, DRIVER_MODESET))
+		drm_helper_initial_config(dev);
+
+	return 0;
+}
+
+static void nouveau_card_takedown(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_engine *engine = &dev_priv->engine;
+
+	NV_DEBUG(dev, "prev state = %d\n", dev_priv->init_state);
+
+	if (dev_priv->init_state != NOUVEAU_CARD_INIT_DOWN) {
+		nouveau_backlight_exit(dev);
+
+		if (dev_priv->channel) {
+			nouveau_channel_free(dev_priv->channel);
+			dev_priv->channel = NULL;
+		}
+
+		engine->fifo.takedown(dev);
+		engine->graph.takedown(dev);
+		engine->fb.takedown(dev);
+		engine->timer.takedown(dev);
+		engine->mc.takedown(dev);
+
+		mutex_lock(&dev->struct_mutex);
+		ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_TT);
+		mutex_unlock(&dev->struct_mutex);
+		nouveau_sgdma_takedown(dev);
+
+		nouveau_gpuobj_takedown(dev);
+		nouveau_mem_close(dev);
+		engine->instmem.takedown(dev);
+
+		if (drm_core_check_feature(dev, DRIVER_MODESET))
+			drm_irq_uninstall(dev);
+
+		nouveau_gpuobj_late_takedown(dev);
+		nouveau_bios_takedown(dev);
+
+		vga_client_register(dev->pdev, NULL, NULL, NULL);
+
+		dev_priv->init_state = NOUVEAU_CARD_INIT_DOWN;
+	}
+}
+
+/* here a client dies, release the stuff that was allocated for its
+ * file_priv */
+void nouveau_preclose(struct drm_device *dev, struct drm_file *file_priv)
+{
+	nouveau_channel_cleanup(dev, file_priv);
+}
+
+/* first module load, setup the mmio/fb mapping */
+/* KMS: we need mmio at load time, not when the first drm client opens. */
+int nouveau_firstopen(struct drm_device *dev)
+{
+	return 0;
+}
+
+/* if we have an OF card, copy vbios to RAMIN */
+static void nouveau_OF_copy_vbios_to_ramin(struct drm_device *dev)
+{
+#if defined(__powerpc__)
+	int size, i;
+	const uint32_t *bios;
+	struct device_node *dn = pci_device_to_OF_node(dev->pdev);
+	if (!dn) {
+		NV_INFO(dev, "Unable to get the OF node\n");
+		return;
+	}
+
+	bios = of_get_property(dn, "NVDA,BMP", &size);
+	if (bios) {
+		for (i = 0; i < size; i += 4)
+			nv_wi32(dev, i, bios[i/4]);
+		NV_INFO(dev, "OF bios successfully copied (%d bytes)\n", size);
+	} else {
+		NV_INFO(dev, "Unable to get the OF bios\n");
+	}
+#endif
+}
+
+int nouveau_load(struct drm_device *dev, unsigned long flags)
+{
+	struct drm_nouveau_private *dev_priv;
+	uint32_t reg0;
+	resource_size_t mmio_start_offs;
+
+	dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL);
+	if (!dev_priv)
+		return -ENOMEM;
+	dev->dev_private = dev_priv;
+	dev_priv->dev = dev;
+
+	dev_priv->flags = flags & NOUVEAU_FLAGS;
+	dev_priv->init_state = NOUVEAU_CARD_INIT_DOWN;
+
+	NV_DEBUG(dev, "vendor: 0x%X device: 0x%X class: 0x%X\n",
+		 dev->pci_vendor, dev->pci_device, dev->pdev->class);
+
+	dev_priv->acpi_dsm = nouveau_dsm_probe(dev);
+
+	if (dev_priv->acpi_dsm)
+		nouveau_hybrid_setup(dev);
+
+	dev_priv->wq = create_workqueue("nouveau");
+	if (!dev_priv->wq)
+		return -EINVAL;
+
+	/* resource 0 is mmio regs */
+	/* resource 1 is linear FB */
+	/* resource 2 is RAMIN (mmio regs + 0x1000000) */
+	/* resource 6 is bios */
+
+	/* map the mmio regs */
+	mmio_start_offs = pci_resource_start(dev->pdev, 0);
+	dev_priv->mmio = ioremap(mmio_start_offs, 0x00800000);
+	if (!dev_priv->mmio) {
+		NV_ERROR(dev, "Unable to initialize the mmio mapping. "
+			 "Please report your setup to " DRIVER_EMAIL "\n");
+		return -EINVAL;
+	}
+	NV_DEBUG(dev, "regs mapped ok at 0x%llx\n",
+					(unsigned long long)mmio_start_offs);
+
+#ifdef __BIG_ENDIAN
+	/* Put the card in BE mode if it's not */
+	if (nv_rd32(dev, NV03_PMC_BOOT_1))
+		nv_wr32(dev, NV03_PMC_BOOT_1, 0x00000001);
+
+	DRM_MEMORYBARRIER();
+#endif
+
+	/* Time to determine the card architecture */
+	reg0 = nv_rd32(dev, NV03_PMC_BOOT_0);
+
+	/* We're dealing with >=NV10 */
+	if ((reg0 & 0x0f000000) > 0) {
+		/* Bit 27-20 contain the architecture in hex */
+		dev_priv->chipset = (reg0 & 0xff00000) >> 20;
+	/* NV04 or NV05 */
+	} else if ((reg0 & 0xff00fff0) == 0x20004000) {
+		dev_priv->chipset = 0x04;
+	} else
+		dev_priv->chipset = 0xff;
+
+	switch (dev_priv->chipset & 0xf0) {
+	case 0x00:
+	case 0x10:
+	case 0x20:
+	case 0x30:
+		dev_priv->card_type = dev_priv->chipset & 0xf0;
+		break;
+	case 0x40:
+	case 0x60:
+		dev_priv->card_type = NV_40;
+		break;
+	case 0x50:
+	case 0x80:
+	case 0x90:
+	case 0xa0:
+		dev_priv->card_type = NV_50;
+		break;
+	default:
+		NV_INFO(dev, "Unsupported chipset 0x%08x\n", reg0);
+		return -EINVAL;
+	}
+
+	NV_INFO(dev, "Detected an NV%2x generation card (0x%08x)\n",
+		dev_priv->card_type, reg0);
+
+	/* map larger RAMIN aperture on NV40 cards */
+	dev_priv->ramin  = NULL;
+	if (dev_priv->card_type >= NV_40) {
+		int ramin_bar = 2;
+		if (pci_resource_len(dev->pdev, ramin_bar) == 0)
+			ramin_bar = 3;
+
+		dev_priv->ramin_size = pci_resource_len(dev->pdev, ramin_bar);
+		dev_priv->ramin = ioremap(
+				pci_resource_start(dev->pdev, ramin_bar),
+				dev_priv->ramin_size);
+		if (!dev_priv->ramin) {
+			NV_ERROR(dev, "Failed to init RAMIN mapping, "
+				      "limited instance memory available\n");
+		}
+	}
+
+	/* On older cards (or if the above failed), create a map covering
+	 * the BAR0 PRAMIN aperture */
+	if (!dev_priv->ramin) {
+		dev_priv->ramin_size = 1 * 1024 * 1024;
+		dev_priv->ramin = ioremap(mmio_start_offs + NV_RAMIN,
+							dev_priv->ramin_size);
+		if (!dev_priv->ramin) {
+			NV_ERROR(dev, "Failed to map BAR0 PRAMIN.\n");
+			return -ENOMEM;
+		}
+	}
+
+	nouveau_OF_copy_vbios_to_ramin(dev);
+
+	/* Special flags */
+	if (dev->pci_device == 0x01a0)
+		dev_priv->flags |= NV_NFORCE;
+	else if (dev->pci_device == 0x01f0)
+		dev_priv->flags |= NV_NFORCE2;
+
+	/* For kernel modesetting, init card now and bring up fbcon */
+	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+		int ret = nouveau_card_init(dev);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static void nouveau_close(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	/* In the case of an error dev_priv may not be be allocated yet */
+	if (dev_priv && dev_priv->card_type)
+		nouveau_card_takedown(dev);
+}
+
+/* KMS: we need mmio at load time, not when the first drm client opens. */
+void nouveau_lastclose(struct drm_device *dev)
+{
+	if (drm_core_check_feature(dev, DRIVER_MODESET))
+		return;
+
+	nouveau_close(dev);
+}
+
+int nouveau_unload(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+		if (dev_priv->card_type >= NV_50)
+			nv50_display_destroy(dev);
+		else
+			nv04_display_destroy(dev);
+		nouveau_close(dev);
+	}
+
+	iounmap(dev_priv->mmio);
+	iounmap(dev_priv->ramin);
+
+	kfree(dev_priv);
+	dev->dev_private = NULL;
+	return 0;
+}
+
+int
+nouveau_ioctl_card_init(struct drm_device *dev, void *data,
+			struct drm_file *file_priv)
+{
+	return nouveau_card_init(dev);
+}
+
+int nouveau_ioctl_getparam(struct drm_device *dev, void *data,
+						struct drm_file *file_priv)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct drm_nouveau_getparam *getparam = data;
+
+	NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
+
+	switch (getparam->param) {
+	case NOUVEAU_GETPARAM_CHIPSET_ID:
+		getparam->value = dev_priv->chipset;
+		break;
+	case NOUVEAU_GETPARAM_PCI_VENDOR:
+		getparam->value = dev->pci_vendor;
+		break;
+	case NOUVEAU_GETPARAM_PCI_DEVICE:
+		getparam->value = dev->pci_device;
+		break;
+	case NOUVEAU_GETPARAM_BUS_TYPE:
+		if (drm_device_is_agp(dev))
+			getparam->value = NV_AGP;
+		else if (drm_device_is_pcie(dev))
+			getparam->value = NV_PCIE;
+		else
+			getparam->value = NV_PCI;
+		break;
+	case NOUVEAU_GETPARAM_FB_PHYSICAL:
+		getparam->value = dev_priv->fb_phys;
+		break;
+	case NOUVEAU_GETPARAM_AGP_PHYSICAL:
+		getparam->value = dev_priv->gart_info.aper_base;
+		break;
+	case NOUVEAU_GETPARAM_PCI_PHYSICAL:
+		if (dev->sg) {
+			getparam->value = (unsigned long)dev->sg->virtual;
+		} else {
+			NV_ERROR(dev, "Requested PCIGART address, "
+					"while no PCIGART was created\n");
+			return -EINVAL;
+		}
+		break;
+	case NOUVEAU_GETPARAM_FB_SIZE:
+		getparam->value = dev_priv->fb_available_size;
+		break;
+	case NOUVEAU_GETPARAM_AGP_SIZE:
+		getparam->value = dev_priv->gart_info.aper_size;
+		break;
+	case NOUVEAU_GETPARAM_VM_VRAM_BASE:
+		getparam->value = dev_priv->vm_vram_base;
+		break;
+	default:
+		NV_ERROR(dev, "unknown parameter %lld\n", getparam->param);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int
+nouveau_ioctl_setparam(struct drm_device *dev, void *data,
+		       struct drm_file *file_priv)
+{
+	struct drm_nouveau_setparam *setparam = data;
+
+	NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
+
+	switch (setparam->param) {
+	default:
+		NV_ERROR(dev, "unknown parameter %lld\n", setparam->param);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* Wait until (value(reg) & mask) == val, up until timeout has hit */
+bool nouveau_wait_until(struct drm_device *dev, uint64_t timeout,
+			uint32_t reg, uint32_t mask, uint32_t val)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer;
+	uint64_t start = ptimer->read(dev);
+
+	do {
+		if ((nv_rd32(dev, reg) & mask) == val)
+			return true;
+	} while (ptimer->read(dev) - start < timeout);
+
+	return false;
+}
+
+/* Waits for PGRAPH to go completely idle */
+bool nouveau_wait_for_idle(struct drm_device *dev)
+{
+	if (!nv_wait(NV04_PGRAPH_STATUS, 0xffffffff, 0x00000000)) {
+		NV_ERROR(dev, "PGRAPH idle timed out with status 0x%08x\n",
+			 nv_rd32(dev, NV04_PGRAPH_STATUS));
+		return false;
+	}
+
+	return true;
+}
+
diff --git a/drivers/gpu/drm/nouveau/nouveau_ttm.c b/drivers/gpu/drm/nouveau/nouveau_ttm.c
new file mode 100644
index 0000000..187eb84
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_ttm.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2007-2008 Tungsten Graphics, Inc., Cedar Park, TX., USA,
+ * All Rights Reserved.
+ * Copyright (c) 2009 VMware, Inc., Palo Alto, CA., USA,
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sub license,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "drmP.h"
+
+#include "nouveau_drv.h"
+
+static struct vm_operations_struct nouveau_ttm_vm_ops;
+static const struct vm_operations_struct *ttm_vm_ops;
+
+static int
+nouveau_ttm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+	struct ttm_buffer_object *bo = vma->vm_private_data;
+	int ret;
+
+	if (unlikely(bo == NULL))
+		return VM_FAULT_NOPAGE;
+
+	ret = ttm_vm_ops->fault(vma, vmf);
+	return ret;
+}
+
+int
+nouveau_ttm_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	struct drm_file *file_priv = filp->private_data;
+	struct drm_nouveau_private *dev_priv =
+		file_priv->minor->dev->dev_private;
+	int ret;
+
+	if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET))
+		return drm_mmap(filp, vma);
+
+	ret = ttm_bo_mmap(filp, vma, &dev_priv->ttm.bdev);
+	if (unlikely(ret != 0))
+		return ret;
+
+	if (unlikely(ttm_vm_ops == NULL)) {
+		ttm_vm_ops = vma->vm_ops;
+		nouveau_ttm_vm_ops = *ttm_vm_ops;
+		nouveau_ttm_vm_ops.fault = &nouveau_ttm_fault;
+	}
+
+	vma->vm_ops = &nouveau_ttm_vm_ops;
+	return 0;
+}
+
+static int
+nouveau_ttm_mem_global_init(struct ttm_global_reference *ref)
+{
+	return ttm_mem_global_init(ref->object);
+}
+
+static void
+nouveau_ttm_mem_global_release(struct ttm_global_reference *ref)
+{
+	ttm_mem_global_release(ref->object);
+}
+
+int
+nouveau_ttm_global_init(struct drm_nouveau_private *dev_priv)
+{
+	struct ttm_global_reference *global_ref;
+	int ret;
+
+	global_ref = &dev_priv->ttm.mem_global_ref;
+	global_ref->global_type = TTM_GLOBAL_TTM_MEM;
+	global_ref->size = sizeof(struct ttm_mem_global);
+	global_ref->init = &nouveau_ttm_mem_global_init;
+	global_ref->release = &nouveau_ttm_mem_global_release;
+
+	ret = ttm_global_item_ref(global_ref);
+	if (unlikely(ret != 0)) {
+		DRM_ERROR("Failed setting up TTM memory accounting\n");
+		dev_priv->ttm.mem_global_ref.release = NULL;
+		return ret;
+	}
+
+	dev_priv->ttm.bo_global_ref.mem_glob = global_ref->object;
+	global_ref = &dev_priv->ttm.bo_global_ref.ref;
+	global_ref->global_type = TTM_GLOBAL_TTM_BO;
+	global_ref->size = sizeof(struct ttm_bo_global);
+	global_ref->init = &ttm_bo_global_init;
+	global_ref->release = &ttm_bo_global_release;
+
+	ret = ttm_global_item_ref(global_ref);
+	if (unlikely(ret != 0)) {
+		DRM_ERROR("Failed setting up TTM BO subsystem\n");
+		ttm_global_item_unref(&dev_priv->ttm.mem_global_ref);
+		dev_priv->ttm.mem_global_ref.release = NULL;
+		return ret;
+	}
+
+	return 0;
+}
+
+void
+nouveau_ttm_global_release(struct drm_nouveau_private *dev_priv)
+{
+	if (dev_priv->ttm.mem_global_ref.release == NULL)
+		return;
+
+	ttm_global_item_unref(&dev_priv->ttm.bo_global_ref.ref);
+	ttm_global_item_unref(&dev_priv->ttm.mem_global_ref);
+	dev_priv->ttm.mem_global_ref.release = NULL;
+}
+
diff --git a/drivers/gpu/drm/nouveau/nv04_crtc.c b/drivers/gpu/drm/nouveau/nv04_crtc.c
new file mode 100644
index 0000000..b913636
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv04_crtc.c
@@ -0,0 +1,1002 @@
+/*
+ * Copyright 1993-2003 NVIDIA, Corporation
+ * Copyright 2006 Dave Airlie
+ * Copyright 2007 Maarten Maathuis
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "drmP.h"
+#include "drm_crtc_helper.h"
+
+#include "nouveau_drv.h"
+#include "nouveau_encoder.h"
+#include "nouveau_connector.h"
+#include "nouveau_crtc.h"
+#include "nouveau_fb.h"
+#include "nouveau_hw.h"
+#include "nvreg.h"
+
+static int
+nv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+			struct drm_framebuffer *old_fb);
+
+static void
+crtc_wr_cio_state(struct drm_crtc *crtc, struct nv04_crtc_reg *crtcstate, int index)
+{
+	NVWriteVgaCrtc(crtc->dev, nouveau_crtc(crtc)->index, index,
+		       crtcstate->CRTC[index]);
+}
+
+static void nv_crtc_set_digital_vibrance(struct drm_crtc *crtc, int level)
+{
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
+	struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index];
+
+	regp->CRTC[NV_CIO_CRE_CSB] = nv_crtc->saturation = level;
+	if (nv_crtc->saturation && nv_gf4_disp_arch(crtc->dev)) {
+		regp->CRTC[NV_CIO_CRE_CSB] = 0x80;
+		regp->CRTC[NV_CIO_CRE_5B] = nv_crtc->saturation << 2;
+		crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_5B);
+	}
+	crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_CSB);
+}
+
+static void nv_crtc_set_image_sharpening(struct drm_crtc *crtc, int level)
+{
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
+	struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index];
+
+	nv_crtc->sharpness = level;
+	if (level < 0)	/* blur is in hw range 0x3f -> 0x20 */
+		level += 0x40;
+	regp->ramdac_634 = level;
+	NVWriteRAMDAC(crtc->dev, nv_crtc->index, NV_PRAMDAC_634, regp->ramdac_634);
+}
+
+#define PLLSEL_VPLL1_MASK				\
+	(NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_VPLL	\
+	 | NV_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO_DB2)
+#define PLLSEL_VPLL2_MASK				\
+	(NV_PRAMDAC_PLL_COEFF_SELECT_PLL_SOURCE_VPLL2		\
+	 | NV_PRAMDAC_PLL_COEFF_SELECT_VCLK2_RATIO_DB2)
+#define PLLSEL_TV_MASK					\
+	(NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1		\
+	 | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1		\
+	 | NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2	\
+	 | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2)
+
+/* NV4x 0x40.. pll notes:
+ * gpu pll: 0x4000 + 0x4004
+ * ?gpu? pll: 0x4008 + 0x400c
+ * vpll1: 0x4010 + 0x4014
+ * vpll2: 0x4018 + 0x401c
+ * mpll: 0x4020 + 0x4024
+ * mpll: 0x4038 + 0x403c
+ *
+ * the first register of each pair has some unknown details:
+ * bits 0-7: redirected values from elsewhere? (similar to PLL_SETUP_CONTROL?)
+ * bits 20-23: (mpll) something to do with post divider?
+ * bits 28-31: related to single stage mode? (bit 8/12)
+ */
+
+static void nv_crtc_calc_state_ext(struct drm_crtc *crtc, struct drm_display_mode * mode, int dot_clock)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+	struct nv04_mode_state *state = &dev_priv->mode_reg;
+	struct nv04_crtc_reg *regp = &state->crtc_reg[nv_crtc->index];
+	struct nouveau_pll_vals *pv = &regp->pllvals;
+	struct pll_lims pll_lim;
+
+	if (get_pll_limits(dev, nv_crtc->index ? VPLL2 : VPLL1, &pll_lim))
+		return;
+
+	/* NM2 == 0 is used to determine single stage mode on two stage plls */
+	pv->NM2 = 0;
+
+	/* for newer nv4x the blob uses only the first stage of the vpll below a
+	 * certain clock.  for a certain nv4b this is 150MHz.  since the max
+	 * output frequency of the first stage for this card is 300MHz, it is
+	 * assumed the threshold is given by vco1 maxfreq/2
+	 */
+	/* for early nv4x, specifically nv40 and *some* nv43 (devids 0 and 6,
+	 * not 8, others unknown), the blob always uses both plls.  no problem
+	 * has yet been observed in allowing the use a single stage pll on all
+	 * nv43 however.  the behaviour of single stage use is untested on nv40
+	 */
+	if (dev_priv->chipset > 0x40 && dot_clock <= (pll_lim.vco1.maxfreq / 2))
+		memset(&pll_lim.vco2, 0, sizeof(pll_lim.vco2));
+
+	if (!nouveau_calc_pll_mnp(dev, &pll_lim, dot_clock, pv))
+		return;
+
+	state->pllsel &= PLLSEL_VPLL1_MASK | PLLSEL_VPLL2_MASK | PLLSEL_TV_MASK;
+
+	/* The blob uses this always, so let's do the same */
+	if (dev_priv->card_type == NV_40)
+		state->pllsel |= NV_PRAMDAC_PLL_COEFF_SELECT_USE_VPLL2_TRUE;
+	/* again nv40 and some nv43 act more like nv3x as described above */
+	if (dev_priv->chipset < 0x41)
+		state->pllsel |= NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_MPLL |
+				 NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_NVPLL;
+	state->pllsel |= nv_crtc->index ? PLLSEL_VPLL2_MASK : PLLSEL_VPLL1_MASK;
+
+	if (pv->NM2)
+		NV_TRACE(dev, "vpll: n1 %d n2 %d m1 %d m2 %d log2p %d\n",
+			 pv->N1, pv->N2, pv->M1, pv->M2, pv->log2P);
+	else
+		NV_TRACE(dev, "vpll: n %d m %d log2p %d\n",
+			 pv->N1, pv->M1, pv->log2P);
+
+	nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.offset);
+}
+
+static void
+nv_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+	struct drm_device *dev = crtc->dev;
+	unsigned char seq1 = 0, crtc17 = 0;
+	unsigned char crtc1A;
+
+	NV_TRACE(dev, "Setting dpms mode %d on CRTC %d\n", mode,
+							nv_crtc->index);
+
+	if (nv_crtc->last_dpms == mode) /* Don't do unnecesary mode changes. */
+		return;
+
+	nv_crtc->last_dpms = mode;
+
+	if (nv_two_heads(dev))
+		NVSetOwner(dev, nv_crtc->index);
+
+	/* nv4ref indicates these two RPC1 bits inhibit h/v sync */
+	crtc1A = NVReadVgaCrtc(dev, nv_crtc->index,
+					NV_CIO_CRE_RPC1_INDEX) & ~0xC0;
+	switch (mode) {
+	case DRM_MODE_DPMS_STANDBY:
+		/* Screen: Off; HSync: Off, VSync: On -- Not Supported */
+		seq1 = 0x20;
+		crtc17 = 0x80;
+		crtc1A |= 0x80;
+		break;
+	case DRM_MODE_DPMS_SUSPEND:
+		/* Screen: Off; HSync: On, VSync: Off -- Not Supported */
+		seq1 = 0x20;
+		crtc17 = 0x80;
+		crtc1A |= 0x40;
+		break;
+	case DRM_MODE_DPMS_OFF:
+		/* Screen: Off; HSync: Off, VSync: Off */
+		seq1 = 0x20;
+		crtc17 = 0x00;
+		crtc1A |= 0xC0;
+		break;
+	case DRM_MODE_DPMS_ON:
+	default:
+		/* Screen: On; HSync: On, VSync: On */
+		seq1 = 0x00;
+		crtc17 = 0x80;
+		break;
+	}
+
+	NVVgaSeqReset(dev, nv_crtc->index, true);
+	/* Each head has it's own sequencer, so we can turn it off when we want */
+	seq1 |= (NVReadVgaSeq(dev, nv_crtc->index, NV_VIO_SR_CLOCK_INDEX) & ~0x20);
+	NVWriteVgaSeq(dev, nv_crtc->index, NV_VIO_SR_CLOCK_INDEX, seq1);
+	crtc17 |= (NVReadVgaCrtc(dev, nv_crtc->index, NV_CIO_CR_MODE_INDEX) & ~0x80);
+	mdelay(10);
+	NVWriteVgaCrtc(dev, nv_crtc->index, NV_CIO_CR_MODE_INDEX, crtc17);
+	NVVgaSeqReset(dev, nv_crtc->index, false);
+
+	NVWriteVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RPC1_INDEX, crtc1A);
+}
+
+static bool
+nv_crtc_mode_fixup(struct drm_crtc *crtc, struct drm_display_mode *mode,
+		   struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static void
+nv_crtc_mode_set_vga(struct drm_crtc *crtc, struct drm_display_mode *mode)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+	struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index];
+	struct drm_framebuffer *fb = crtc->fb;
+
+	/* Calculate our timings */
+	int horizDisplay	= (mode->crtc_hdisplay >> 3) 	- 1;
+	int horizStart		= (mode->crtc_hsync_start >> 3) 	- 1;
+	int horizEnd		= (mode->crtc_hsync_end >> 3) 	- 1;
+	int horizTotal		= (mode->crtc_htotal >> 3)		- 5;
+	int horizBlankStart	= (mode->crtc_hdisplay >> 3)		- 1;
+	int horizBlankEnd	= (mode->crtc_htotal >> 3)		- 1;
+	int vertDisplay		= mode->crtc_vdisplay			- 1;
+	int vertStart		= mode->crtc_vsync_start 		- 1;
+	int vertEnd		= mode->crtc_vsync_end			- 1;
+	int vertTotal		= mode->crtc_vtotal 			- 2;
+	int vertBlankStart	= mode->crtc_vdisplay 			- 1;
+	int vertBlankEnd	= mode->crtc_vtotal			- 1;
+
+	struct drm_encoder *encoder;
+	bool fp_output = false;
+
+	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+		struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+
+		if (encoder->crtc == crtc &&
+		    (nv_encoder->dcb->type == OUTPUT_LVDS ||
+		     nv_encoder->dcb->type == OUTPUT_TMDS))
+			fp_output = true;
+	}
+
+	if (fp_output) {
+		vertStart = vertTotal - 3;
+		vertEnd = vertTotal - 2;
+		vertBlankStart = vertStart;
+		horizStart = horizTotal - 5;
+		horizEnd = horizTotal - 2;
+		horizBlankEnd = horizTotal + 4;
+#if 0
+		if (dev->overlayAdaptor && dev_priv->card_type >= NV_10)
+			/* This reportedly works around some video overlay bandwidth problems */
+			horizTotal += 2;
+#endif
+	}
+
+	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+		vertTotal |= 1;
+
+#if 0
+	ErrorF("horizDisplay: 0x%X \n", horizDisplay);
+	ErrorF("horizStart: 0x%X \n", horizStart);
+	ErrorF("horizEnd: 0x%X \n", horizEnd);
+	ErrorF("horizTotal: 0x%X \n", horizTotal);
+	ErrorF("horizBlankStart: 0x%X \n", horizBlankStart);
+	ErrorF("horizBlankEnd: 0x%X \n", horizBlankEnd);
+	ErrorF("vertDisplay: 0x%X \n", vertDisplay);
+	ErrorF("vertStart: 0x%X \n", vertStart);
+	ErrorF("vertEnd: 0x%X \n", vertEnd);
+	ErrorF("vertTotal: 0x%X \n", vertTotal);
+	ErrorF("vertBlankStart: 0x%X \n", vertBlankStart);
+	ErrorF("vertBlankEnd: 0x%X \n", vertBlankEnd);
+#endif
+
+	/*
+	* compute correct Hsync & Vsync polarity
+	*/
+	if ((mode->flags & (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NHSYNC))
+		&& (mode->flags & (DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC))) {
+
+		regp->MiscOutReg = 0x23;
+		if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+			regp->MiscOutReg |= 0x40;
+		if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+			regp->MiscOutReg |= 0x80;
+	} else {
+		int vdisplay = mode->vdisplay;
+		if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
+			vdisplay *= 2;
+		if (mode->vscan > 1)
+			vdisplay *= mode->vscan;
+		if (vdisplay < 400)
+			regp->MiscOutReg = 0xA3;	/* +hsync -vsync */
+		else if (vdisplay < 480)
+			regp->MiscOutReg = 0x63;	/* -hsync +vsync */
+		else if (vdisplay < 768)
+			regp->MiscOutReg = 0xE3;	/* -hsync -vsync */
+		else
+			regp->MiscOutReg = 0x23;	/* +hsync +vsync */
+	}
+
+	regp->MiscOutReg |= (mode->clock_index & 0x03) << 2;
+
+	/*
+	 * Time Sequencer
+	 */
+	regp->Sequencer[NV_VIO_SR_RESET_INDEX] = 0x00;
+	/* 0x20 disables the sequencer */
+	if (mode->flags & DRM_MODE_FLAG_CLKDIV2)
+		regp->Sequencer[NV_VIO_SR_CLOCK_INDEX] = 0x29;
+	else
+		regp->Sequencer[NV_VIO_SR_CLOCK_INDEX] = 0x21;
+	regp->Sequencer[NV_VIO_SR_PLANE_MASK_INDEX] = 0x0F;
+	regp->Sequencer[NV_VIO_SR_CHAR_MAP_INDEX] = 0x00;
+	regp->Sequencer[NV_VIO_SR_MEM_MODE_INDEX] = 0x0E;
+
+	/*
+	 * CRTC
+	 */
+	regp->CRTC[NV_CIO_CR_HDT_INDEX] = horizTotal;
+	regp->CRTC[NV_CIO_CR_HDE_INDEX] = horizDisplay;
+	regp->CRTC[NV_CIO_CR_HBS_INDEX] = horizBlankStart;
+	regp->CRTC[NV_CIO_CR_HBE_INDEX] = (1 << 7) |
+					  XLATE(horizBlankEnd, 0, NV_CIO_CR_HBE_4_0);
+	regp->CRTC[NV_CIO_CR_HRS_INDEX] = horizStart;
+	regp->CRTC[NV_CIO_CR_HRE_INDEX] = XLATE(horizBlankEnd, 5, NV_CIO_CR_HRE_HBE_5) |
+					  XLATE(horizEnd, 0, NV_CIO_CR_HRE_4_0);
+	regp->CRTC[NV_CIO_CR_VDT_INDEX] = vertTotal;
+	regp->CRTC[NV_CIO_CR_OVL_INDEX] = XLATE(vertStart, 9, NV_CIO_CR_OVL_VRS_9) |
+					  XLATE(vertDisplay, 9, NV_CIO_CR_OVL_VDE_9) |
+					  XLATE(vertTotal, 9, NV_CIO_CR_OVL_VDT_9) |
+					  (1 << 4) |
+					  XLATE(vertBlankStart, 8, NV_CIO_CR_OVL_VBS_8) |
+					  XLATE(vertStart, 8, NV_CIO_CR_OVL_VRS_8) |
+					  XLATE(vertDisplay, 8, NV_CIO_CR_OVL_VDE_8) |
+					  XLATE(vertTotal, 8, NV_CIO_CR_OVL_VDT_8);
+	regp->CRTC[NV_CIO_CR_RSAL_INDEX] = 0x00;
+	regp->CRTC[NV_CIO_CR_CELL_HT_INDEX] = ((mode->flags & DRM_MODE_FLAG_DBLSCAN) ? MASK(NV_CIO_CR_CELL_HT_SCANDBL) : 0) |
+					      1 << 6 |
+					      XLATE(vertBlankStart, 9, NV_CIO_CR_CELL_HT_VBS_9);
+	regp->CRTC[NV_CIO_CR_CURS_ST_INDEX] = 0x00;
+	regp->CRTC[NV_CIO_CR_CURS_END_INDEX] = 0x00;
+	regp->CRTC[NV_CIO_CR_SA_HI_INDEX] = 0x00;
+	regp->CRTC[NV_CIO_CR_SA_LO_INDEX] = 0x00;
+	regp->CRTC[NV_CIO_CR_TCOFF_HI_INDEX] = 0x00;
+	regp->CRTC[NV_CIO_CR_TCOFF_LO_INDEX] = 0x00;
+	regp->CRTC[NV_CIO_CR_VRS_INDEX] = vertStart;
+	regp->CRTC[NV_CIO_CR_VRE_INDEX] = 1 << 5 | XLATE(vertEnd, 0, NV_CIO_CR_VRE_3_0);
+	regp->CRTC[NV_CIO_CR_VDE_INDEX] = vertDisplay;
+	/* framebuffer can be larger than crtc scanout area. */
+	regp->CRTC[NV_CIO_CR_OFFSET_INDEX] = fb->pitch / 8;
+	regp->CRTC[NV_CIO_CR_ULINE_INDEX] = 0x00;
+	regp->CRTC[NV_CIO_CR_VBS_INDEX] = vertBlankStart;
+	regp->CRTC[NV_CIO_CR_VBE_INDEX] = vertBlankEnd;
+	regp->CRTC[NV_CIO_CR_MODE_INDEX] = 0x43;
+	regp->CRTC[NV_CIO_CR_LCOMP_INDEX] = 0xff;
+
+	/*
+	 * Some extended CRTC registers (they are not saved with the rest of the vga regs).
+	 */
+
+	/* framebuffer can be larger than crtc scanout area. */
+	regp->CRTC[NV_CIO_CRE_RPC0_INDEX] = XLATE(fb->pitch / 8, 8, NV_CIO_CRE_RPC0_OFFSET_10_8);
+	regp->CRTC[NV_CIO_CRE_RPC1_INDEX] = mode->crtc_hdisplay < 1280 ?
+					    MASK(NV_CIO_CRE_RPC1_LARGE) : 0x00;
+	regp->CRTC[NV_CIO_CRE_LSR_INDEX] = XLATE(horizBlankEnd, 6, NV_CIO_CRE_LSR_HBE_6) |
+					   XLATE(vertBlankStart, 10, NV_CIO_CRE_LSR_VBS_10) |
+					   XLATE(vertStart, 10, NV_CIO_CRE_LSR_VRS_10) |
+					   XLATE(vertDisplay, 10, NV_CIO_CRE_LSR_VDE_10) |
+					   XLATE(vertTotal, 10, NV_CIO_CRE_LSR_VDT_10);
+	regp->CRTC[NV_CIO_CRE_HEB__INDEX] = XLATE(horizStart, 8, NV_CIO_CRE_HEB_HRS_8) |
+					    XLATE(horizBlankStart, 8, NV_CIO_CRE_HEB_HBS_8) |
+					    XLATE(horizDisplay, 8, NV_CIO_CRE_HEB_HDE_8) |
+					    XLATE(horizTotal, 8, NV_CIO_CRE_HEB_HDT_8);
+	regp->CRTC[NV_CIO_CRE_EBR_INDEX] = XLATE(vertBlankStart, 11, NV_CIO_CRE_EBR_VBS_11) |
+					   XLATE(vertStart, 11, NV_CIO_CRE_EBR_VRS_11) |
+					   XLATE(vertDisplay, 11, NV_CIO_CRE_EBR_VDE_11) |
+					   XLATE(vertTotal, 11, NV_CIO_CRE_EBR_VDT_11);
+
+	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
+		horizTotal = (horizTotal >> 1) & ~1;
+		regp->CRTC[NV_CIO_CRE_ILACE__INDEX] = horizTotal;
+		regp->CRTC[NV_CIO_CRE_HEB__INDEX] |= XLATE(horizTotal, 8, NV_CIO_CRE_HEB_ILC_8);
+	} else
+		regp->CRTC[NV_CIO_CRE_ILACE__INDEX] = 0xff;  /* interlace off */
+
+	/*
+	* Graphics Display Controller
+	*/
+	regp->Graphics[NV_VIO_GX_SR_INDEX] = 0x00;
+	regp->Graphics[NV_VIO_GX_SREN_INDEX] = 0x00;
+	regp->Graphics[NV_VIO_GX_CCOMP_INDEX] = 0x00;
+	regp->Graphics[NV_VIO_GX_ROP_INDEX] = 0x00;
+	regp->Graphics[NV_VIO_GX_READ_MAP_INDEX] = 0x00;
+	regp->Graphics[NV_VIO_GX_MODE_INDEX] = 0x40; /* 256 color mode */
+	regp->Graphics[NV_VIO_GX_MISC_INDEX] = 0x05; /* map 64k mem + graphic mode */
+	regp->Graphics[NV_VIO_GX_DONT_CARE_INDEX] = 0x0F;
+	regp->Graphics[NV_VIO_GX_BIT_MASK_INDEX] = 0xFF;
+
+	regp->Attribute[0]  = 0x00; /* standard colormap translation */
+	regp->Attribute[1]  = 0x01;
+	regp->Attribute[2]  = 0x02;
+	regp->Attribute[3]  = 0x03;
+	regp->Attribute[4]  = 0x04;
+	regp->Attribute[5]  = 0x05;
+	regp->Attribute[6]  = 0x06;
+	regp->Attribute[7]  = 0x07;
+	regp->Attribute[8]  = 0x08;
+	regp->Attribute[9]  = 0x09;
+	regp->Attribute[10] = 0x0A;
+	regp->Attribute[11] = 0x0B;
+	regp->Attribute[12] = 0x0C;
+	regp->Attribute[13] = 0x0D;
+	regp->Attribute[14] = 0x0E;
+	regp->Attribute[15] = 0x0F;
+	regp->Attribute[NV_CIO_AR_MODE_INDEX] = 0x01; /* Enable graphic mode */
+	/* Non-vga */
+	regp->Attribute[NV_CIO_AR_OSCAN_INDEX] = 0x00;
+	regp->Attribute[NV_CIO_AR_PLANE_INDEX] = 0x0F; /* enable all color planes */
+	regp->Attribute[NV_CIO_AR_HPP_INDEX] = 0x00;
+	regp->Attribute[NV_CIO_AR_CSEL_INDEX] = 0x00;
+}
+
+/**
+ * Sets up registers for the given mode/adjusted_mode pair.
+ *
+ * The clocks, CRTCs and outputs attached to this CRTC must be off.
+ *
+ * This shouldn't enable any clocks, CRTCs, or outputs, but they should
+ * be easily turned on/off after this.
+ */
+static void
+nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct drm_display_mode * mode)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+	struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index];
+	struct nv04_crtc_reg *savep = &dev_priv->saved_reg.crtc_reg[nv_crtc->index];
+	struct drm_encoder *encoder;
+	bool lvds_output = false, tmds_output = false, tv_output = false,
+		off_chip_digital = false;
+
+	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+		struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+		bool digital = false;
+
+		if (encoder->crtc != crtc)
+			continue;
+
+		if (nv_encoder->dcb->type == OUTPUT_LVDS)
+			digital = lvds_output = true;
+		if (nv_encoder->dcb->type == OUTPUT_TV)
+			tv_output = true;
+		if (nv_encoder->dcb->type == OUTPUT_TMDS)
+			digital = tmds_output = true;
+		if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP && digital)
+			off_chip_digital = true;
+	}
+
+	/* Registers not directly related to the (s)vga mode */
+
+	/* What is the meaning of this register? */
+	/* A few popular values are 0x18, 0x1c, 0x38, 0x3c */
+	regp->CRTC[NV_CIO_CRE_ENH_INDEX] = savep->CRTC[NV_CIO_CRE_ENH_INDEX] & ~(1<<5);
+
+	regp->crtc_eng_ctrl = 0;
+	/* Except for rare conditions I2C is enabled on the primary crtc */
+	if (nv_crtc->index == 0)
+		regp->crtc_eng_ctrl |= NV_CRTC_FSEL_I2C;
+#if 0
+	/* Set overlay to desired crtc. */
+	if (dev->overlayAdaptor) {
+		NVPortPrivPtr pPriv = GET_OVERLAY_PRIVATE(dev);
+		if (pPriv->overlayCRTC == nv_crtc->index)
+			regp->crtc_eng_ctrl |= NV_CRTC_FSEL_OVERLAY;
+	}
+#endif
+
+	/* ADDRESS_SPACE_PNVM is the same as setting HCUR_ASI */
+	regp->cursor_cfg = NV_PCRTC_CURSOR_CONFIG_CUR_LINES_64 |
+			     NV_PCRTC_CURSOR_CONFIG_CUR_PIXELS_64 |
+			     NV_PCRTC_CURSOR_CONFIG_ADDRESS_SPACE_PNVM;
+	if (dev_priv->chipset >= 0x11)
+		regp->cursor_cfg |= NV_PCRTC_CURSOR_CONFIG_CUR_BPP_32;
+	if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
+		regp->cursor_cfg |= NV_PCRTC_CURSOR_CONFIG_DOUBLE_SCAN_ENABLE;
+
+	/* Unblock some timings */
+	regp->CRTC[NV_CIO_CRE_53] = 0;
+	regp->CRTC[NV_CIO_CRE_54] = 0;
+
+	/* 0x00 is disabled, 0x11 is lvds, 0x22 crt and 0x88 tmds */
+	if (lvds_output)
+		regp->CRTC[NV_CIO_CRE_SCRATCH3__INDEX] = 0x11;
+	else if (tmds_output)
+		regp->CRTC[NV_CIO_CRE_SCRATCH3__INDEX] = 0x88;
+	else
+		regp->CRTC[NV_CIO_CRE_SCRATCH3__INDEX] = 0x22;
+
+	/* These values seem to vary */
+	/* This register seems to be used by the bios to make certain decisions on some G70 cards? */
+	regp->CRTC[NV_CIO_CRE_SCRATCH4__INDEX] = savep->CRTC[NV_CIO_CRE_SCRATCH4__INDEX];
+
+	nv_crtc_set_digital_vibrance(crtc, nv_crtc->saturation);
+
+	/* probably a scratch reg, but kept for cargo-cult purposes:
+	 * bit0: crtc0?, head A
+	 * bit6: lvds, head A
+	 * bit7: (only in X), head A
+	 */
+	if (nv_crtc->index == 0)
+		regp->CRTC[NV_CIO_CRE_4B] = savep->CRTC[NV_CIO_CRE_4B] | 0x80;
+
+	/* The blob seems to take the current value from crtc 0, add 4 to that
+	 * and reuse the old value for crtc 1 */
+	regp->CRTC[NV_CIO_CRE_TVOUT_LATENCY] = dev_priv->saved_reg.crtc_reg[0].CRTC[NV_CIO_CRE_TVOUT_LATENCY];
+	if (!nv_crtc->index)
+		regp->CRTC[NV_CIO_CRE_TVOUT_LATENCY] += 4;
+
+	/* the blob sometimes sets |= 0x10 (which is the same as setting |=
+	 * 1 << 30 on 0x60.830), for no apparent reason */
+	regp->CRTC[NV_CIO_CRE_59] = off_chip_digital;
+
+	regp->crtc_830 = mode->crtc_vdisplay - 3;
+	regp->crtc_834 = mode->crtc_vdisplay - 1;
+
+	if (dev_priv->card_type == NV_40)
+		/* This is what the blob does */
+		regp->crtc_850 = NVReadCRTC(dev, 0, NV_PCRTC_850);
+
+	if (dev_priv->card_type >= NV_30)
+		regp->gpio_ext = NVReadCRTC(dev, 0, NV_PCRTC_GPIO_EXT);
+
+	regp->crtc_cfg = NV_PCRTC_CONFIG_START_ADDRESS_HSYNC;
+
+	/* Some misc regs */
+	if (dev_priv->card_type == NV_40) {
+		regp->CRTC[NV_CIO_CRE_85] = 0xFF;
+		regp->CRTC[NV_CIO_CRE_86] = 0x1;
+	}
+
+	regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] = (crtc->fb->depth + 1) / 8;
+	/* Enable slaved mode (called MODE_TV in nv4ref.h) */
+	if (lvds_output || tmds_output || tv_output)
+		regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] |= (1 << 7);
+
+	/* Generic PRAMDAC regs */
+
+	if (dev_priv->card_type >= NV_10)
+		/* Only bit that bios and blob set. */
+		regp->nv10_cursync = (1 << 25);
+
+	regp->ramdac_gen_ctrl = NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS |
+				NV_PRAMDAC_GENERAL_CONTROL_VGA_STATE_SEL |
+				NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON;
+	if (crtc->fb->depth == 16)
+		regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL;
+	if (dev_priv->chipset >= 0x11)
+		regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_PIPE_LONG;
+
+	regp->ramdac_630 = 0; /* turn off green mode (tv test pattern?) */
+	regp->tv_setup = 0;
+
+	nv_crtc_set_image_sharpening(crtc, nv_crtc->sharpness);
+
+	/* Some values the blob sets */
+	regp->ramdac_8c0 = 0x100;
+	regp->ramdac_a20 = 0x0;
+	regp->ramdac_a24 = 0xfffff;
+	regp->ramdac_a34 = 0x1;
+}
+
+/**
+ * Sets up registers for the given mode/adjusted_mode pair.
+ *
+ * The clocks, CRTCs and outputs attached to this CRTC must be off.
+ *
+ * This shouldn't enable any clocks, CRTCs, or outputs, but they should
+ * be easily turned on/off after this.
+ */
+static int
+nv_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
+		 struct drm_display_mode *adjusted_mode,
+		 int x, int y, struct drm_framebuffer *old_fb)
+{
+	struct drm_device *dev = crtc->dev;
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	NV_DEBUG(dev, "CTRC mode on CRTC %d:\n", nv_crtc->index);
+	drm_mode_debug_printmodeline(adjusted_mode);
+
+	/* unlock must come after turning off FP_TG_CONTROL in output_prepare */
+	nv_lock_vga_crtc_shadow(dev, nv_crtc->index, -1);
+
+	nv_crtc_mode_set_vga(crtc, adjusted_mode);
+	/* calculated in nv04_dfp_prepare, nv40 needs it written before calculating PLLs */
+	if (dev_priv->card_type == NV_40)
+		NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, dev_priv->mode_reg.sel_clk);
+	nv_crtc_mode_set_regs(crtc, adjusted_mode);
+	nv_crtc_calc_state_ext(crtc, mode, adjusted_mode->clock);
+	return 0;
+}
+
+static void nv_crtc_save(struct drm_crtc *crtc)
+{
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
+	struct nv04_mode_state *state = &dev_priv->mode_reg;
+	struct nv04_crtc_reg *crtc_state = &state->crtc_reg[nv_crtc->index];
+	struct nv04_mode_state *saved = &dev_priv->saved_reg;
+	struct nv04_crtc_reg *crtc_saved = &saved->crtc_reg[nv_crtc->index];
+
+	if (nv_two_heads(crtc->dev))
+		NVSetOwner(crtc->dev, nv_crtc->index);
+
+	nouveau_hw_save_state(crtc->dev, nv_crtc->index, saved);
+
+	/* init some state to saved value */
+	state->sel_clk = saved->sel_clk & ~(0x5 << 16);
+	crtc_state->CRTC[NV_CIO_CRE_LCD__INDEX] = crtc_saved->CRTC[NV_CIO_CRE_LCD__INDEX];
+	state->pllsel = saved->pllsel & ~(PLLSEL_VPLL1_MASK | PLLSEL_VPLL2_MASK | PLLSEL_TV_MASK);
+	crtc_state->gpio_ext = crtc_saved->gpio_ext;
+}
+
+static void nv_crtc_restore(struct drm_crtc *crtc)
+{
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
+	int head = nv_crtc->index;
+	uint8_t saved_cr21 = dev_priv->saved_reg.crtc_reg[head].CRTC[NV_CIO_CRE_21];
+
+	if (nv_two_heads(crtc->dev))
+		NVSetOwner(crtc->dev, head);
+
+	nouveau_hw_load_state(crtc->dev, head, &dev_priv->saved_reg);
+	nv_lock_vga_crtc_shadow(crtc->dev, head, saved_cr21);
+
+	nv_crtc->last_dpms = NV_DPMS_CLEARED;
+}
+
+static void nv_crtc_prepare(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+	struct drm_crtc_helper_funcs *funcs = crtc->helper_private;
+
+	if (nv_two_heads(dev))
+		NVSetOwner(dev, nv_crtc->index);
+
+	funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
+
+	NVBlankScreen(dev, nv_crtc->index, true);
+
+	/* Some more preperation. */
+	NVWriteCRTC(dev, nv_crtc->index, NV_PCRTC_CONFIG, NV_PCRTC_CONFIG_START_ADDRESS_NON_VGA);
+	if (dev_priv->card_type == NV_40) {
+		uint32_t reg900 = NVReadRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_900);
+		NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_900, reg900 & ~0x10000);
+	}
+}
+
+static void nv_crtc_commit(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_crtc_helper_funcs *funcs = crtc->helper_private;
+	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+
+	nouveau_hw_load_state(dev, nv_crtc->index, &dev_priv->mode_reg);
+	nv04_crtc_mode_set_base(crtc, crtc->x, crtc->y, NULL);
+
+#ifdef __BIG_ENDIAN
+	/* turn on LFB swapping */
+	{
+		uint8_t tmp = NVReadVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RCR);
+		tmp |= MASK(NV_CIO_CRE_RCR_ENDIAN_BIG);
+		NVWriteVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RCR, tmp);
+	}
+#endif
+
+	funcs->dpms(crtc, DRM_MODE_DPMS_ON);
+}
+
+static void nv_crtc_destroy(struct drm_crtc *crtc)
+{
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+
+	NV_DEBUG(crtc->dev, "\n");
+
+	if (!nv_crtc)
+		return;
+
+	drm_crtc_cleanup(crtc);
+
+	nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo);
+	kfree(nv_crtc);
+}
+
+static void
+nv_crtc_gamma_load(struct drm_crtc *crtc)
+{
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+	struct drm_device *dev = nv_crtc->base.dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct rgb { uint8_t r, g, b; } __attribute__((packed)) *rgbs;
+	int i;
+
+	rgbs = (struct rgb *)dev_priv->mode_reg.crtc_reg[nv_crtc->index].DAC;
+	for (i = 0; i < 256; i++) {
+		rgbs[i].r = nv_crtc->lut.r[i] >> 8;
+		rgbs[i].g = nv_crtc->lut.g[i] >> 8;
+		rgbs[i].b = nv_crtc->lut.b[i] >> 8;
+	}
+
+	nouveau_hw_load_state_palette(dev, nv_crtc->index, &dev_priv->mode_reg);
+}
+
+static void
+nv_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, uint32_t size)
+{
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+	int i;
+
+	if (size != 256)
+		return;
+
+	for (i = 0; i < 256; i++) {
+		nv_crtc->lut.r[i] = r[i];
+		nv_crtc->lut.g[i] = g[i];
+		nv_crtc->lut.b[i] = b[i];
+	}
+
+	/* We need to know the depth before we upload, but it's possible to
+	 * get called before a framebuffer is bound.  If this is the case,
+	 * mark the lut values as dirty by setting depth==0, and it'll be
+	 * uploaded on the first mode_set_base()
+	 */
+	if (!nv_crtc->base.fb) {
+		nv_crtc->lut.depth = 0;
+		return;
+	}
+
+	nv_crtc_gamma_load(crtc);
+}
+
+static int
+nv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+			struct drm_framebuffer *old_fb)
+{
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+	struct drm_device *dev = crtc->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index];
+	struct drm_framebuffer *drm_fb = nv_crtc->base.fb;
+	struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb);
+	int arb_burst, arb_lwm;
+	int ret;
+
+	ret = nouveau_bo_pin(fb->nvbo, TTM_PL_FLAG_VRAM);
+	if (ret)
+		return ret;
+
+	if (old_fb) {
+		struct nouveau_framebuffer *ofb = nouveau_framebuffer(old_fb);
+		nouveau_bo_unpin(ofb->nvbo);
+	}
+
+	nv_crtc->fb.offset = fb->nvbo->bo.offset;
+
+	if (nv_crtc->lut.depth != drm_fb->depth) {
+		nv_crtc->lut.depth = drm_fb->depth;
+		nv_crtc_gamma_load(crtc);
+	}
+
+	/* Update the framebuffer format. */
+	regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] &= ~3;
+	regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] |= (crtc->fb->depth + 1) / 8;
+	regp->ramdac_gen_ctrl &= ~NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL;
+	if (crtc->fb->depth == 16)
+		regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL;
+	crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_PIXEL_INDEX);
+	NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_GENERAL_CONTROL,
+		      regp->ramdac_gen_ctrl);
+
+	regp->CRTC[NV_CIO_CR_OFFSET_INDEX] = drm_fb->pitch >> 3;
+	regp->CRTC[NV_CIO_CRE_RPC0_INDEX] =
+		XLATE(drm_fb->pitch >> 3, 8, NV_CIO_CRE_RPC0_OFFSET_10_8);
+	crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_RPC0_INDEX);
+	crtc_wr_cio_state(crtc, regp, NV_CIO_CR_OFFSET_INDEX);
+
+	/* Update the framebuffer location. */
+	regp->fb_start = nv_crtc->fb.offset & ~3;
+	regp->fb_start += (y * drm_fb->pitch) + (x * drm_fb->bits_per_pixel / 8);
+	NVWriteCRTC(dev, nv_crtc->index, NV_PCRTC_START, regp->fb_start);
+
+	/* Update the arbitration parameters. */
+	nouveau_calc_arb(dev, crtc->mode.clock, drm_fb->bits_per_pixel,
+			 &arb_burst, &arb_lwm);
+
+	regp->CRTC[NV_CIO_CRE_FF_INDEX] = arb_burst;
+	regp->CRTC[NV_CIO_CRE_FFLWM__INDEX] = arb_lwm & 0xff;
+	crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_FF_INDEX);
+	crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_FFLWM__INDEX);
+
+	if (dev_priv->card_type >= NV_30) {
+		regp->CRTC[NV_CIO_CRE_47] = arb_lwm >> 8;
+		crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_47);
+	}
+
+	return 0;
+}
+
+static void nv04_cursor_upload(struct drm_device *dev, struct nouveau_bo *src,
+			       struct nouveau_bo *dst)
+{
+	int width = nv_cursor_width(dev);
+	uint32_t pixel;
+	int i, j;
+
+	for (i = 0; i < width; i++) {
+		for (j = 0; j < width; j++) {
+			pixel = nouveau_bo_rd32(src, i*64 + j);
+
+			nouveau_bo_wr16(dst, i*width + j, (pixel & 0x80000000) >> 16
+				     | (pixel & 0xf80000) >> 9
+				     | (pixel & 0xf800) >> 6
+				     | (pixel & 0xf8) >> 3);
+		}
+	}
+}
+
+static void nv11_cursor_upload(struct drm_device *dev, struct nouveau_bo *src,
+			       struct nouveau_bo *dst)
+{
+	uint32_t pixel;
+	int alpha, i;
+
+	/* nv11+ supports premultiplied (PM), or non-premultiplied (NPM) alpha
+	 * cursors (though NPM in combination with fp dithering may not work on
+	 * nv11, from "nv" driver history)
+	 * NPM mode needs NV_PCRTC_CURSOR_CONFIG_ALPHA_BLEND set and is what the
+	 * blob uses, however we get given PM cursors so we use PM mode
+	 */
+	for (i = 0; i < 64 * 64; i++) {
+		pixel = nouveau_bo_rd32(src, i);
+
+		/* hw gets unhappy if alpha <= rgb values.  for a PM image "less
+		 * than" shouldn't happen; fix "equal to" case by adding one to
+		 * alpha channel (slightly inaccurate, but so is attempting to
+		 * get back to NPM images, due to limits of integer precision)
+		 */
+		alpha = pixel >> 24;
+		if (alpha > 0 && alpha < 255)
+			pixel = (pixel & 0x00ffffff) | ((alpha + 1) << 24);
+
+#ifdef __BIG_ENDIAN
+		{
+			struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+			if (dev_priv->chipset == 0x11) {
+				pixel = ((pixel & 0x000000ff) << 24) |
+					((pixel & 0x0000ff00) << 8) |
+					((pixel & 0x00ff0000) >> 8) |
+					((pixel & 0xff000000) >> 24);
+			}
+		}
+#endif
+
+		nouveau_bo_wr32(dst, i, pixel);
+	}
+}
+
+static int
+nv04_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
+		     uint32_t buffer_handle, uint32_t width, uint32_t height)
+{
+	struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
+	struct drm_device *dev = dev_priv->dev;
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+	struct nouveau_bo *cursor = NULL;
+	struct drm_gem_object *gem;
+	int ret = 0;
+
+	if (width != 64 || height != 64)
+		return -EINVAL;
+
+	if (!buffer_handle) {
+		nv_crtc->cursor.hide(nv_crtc, true);
+		return 0;
+	}
+
+	gem = drm_gem_object_lookup(dev, file_priv, buffer_handle);
+	if (!gem)
+		return -EINVAL;
+	cursor = nouveau_gem_object(gem);
+
+	ret = nouveau_bo_map(cursor);
+	if (ret)
+		goto out;
+
+	if (dev_priv->chipset >= 0x11)
+		nv11_cursor_upload(dev, cursor, nv_crtc->cursor.nvbo);
+	else
+		nv04_cursor_upload(dev, cursor, nv_crtc->cursor.nvbo);
+
+	nouveau_bo_unmap(cursor);
+	nv_crtc->cursor.offset = nv_crtc->cursor.nvbo->bo.offset;
+	nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.offset);
+	nv_crtc->cursor.show(nv_crtc, true);
+out:
+	mutex_lock(&dev->struct_mutex);
+	drm_gem_object_unreference(gem);
+	mutex_unlock(&dev->struct_mutex);
+	return ret;
+}
+
+static int
+nv04_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
+{
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+
+	nv_crtc->cursor.set_pos(nv_crtc, x, y);
+	return 0;
+}
+
+static const struct drm_crtc_funcs nv04_crtc_funcs = {
+	.save = nv_crtc_save,
+	.restore = nv_crtc_restore,
+	.cursor_set = nv04_crtc_cursor_set,
+	.cursor_move = nv04_crtc_cursor_move,
+	.gamma_set = nv_crtc_gamma_set,
+	.set_config = drm_crtc_helper_set_config,
+	.destroy = nv_crtc_destroy,
+};
+
+static const struct drm_crtc_helper_funcs nv04_crtc_helper_funcs = {
+	.dpms = nv_crtc_dpms,
+	.prepare = nv_crtc_prepare,
+	.commit = nv_crtc_commit,
+	.mode_fixup = nv_crtc_mode_fixup,
+	.mode_set = nv_crtc_mode_set,
+	.mode_set_base = nv04_crtc_mode_set_base,
+	.load_lut = nv_crtc_gamma_load,
+};
+
+int
+nv04_crtc_create(struct drm_device *dev, int crtc_num)
+{
+	struct nouveau_crtc *nv_crtc;
+	int ret, i;
+
+	nv_crtc = kzalloc(sizeof(*nv_crtc), GFP_KERNEL);
+	if (!nv_crtc)
+		return -ENOMEM;
+
+	for (i = 0; i < 256; i++) {
+		nv_crtc->lut.r[i] = i << 8;
+		nv_crtc->lut.g[i] = i << 8;
+		nv_crtc->lut.b[i] = i << 8;
+	}
+	nv_crtc->lut.depth = 0;
+
+	nv_crtc->index = crtc_num;
+	nv_crtc->last_dpms = NV_DPMS_CLEARED;
+
+	drm_crtc_init(dev, &nv_crtc->base, &nv04_crtc_funcs);
+	drm_crtc_helper_add(&nv_crtc->base, &nv04_crtc_helper_funcs);
+	drm_mode_crtc_set_gamma_size(&nv_crtc->base, 256);
+
+	ret = nouveau_bo_new(dev, NULL, 64*64*4, 0x100, TTM_PL_FLAG_VRAM,
+			     0, 0x0000, false, true, &nv_crtc->cursor.nvbo);
+	if (!ret) {
+		ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM);
+		if (!ret)
+			ret = nouveau_bo_map(nv_crtc->cursor.nvbo);
+		if (ret)
+			nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo);
+	}
+
+	nv04_cursor_init(nv_crtc);
+
+	return 0;
+}
+
diff --git a/drivers/gpu/drm/nouveau/nv04_cursor.c b/drivers/gpu/drm/nouveau/nv04_cursor.c
new file mode 100644
index 0000000..89a91b9
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv04_cursor.c
@@ -0,0 +1,70 @@
+#include "drmP.h"
+#include "drm_mode.h"
+#include "nouveau_reg.h"
+#include "nouveau_drv.h"
+#include "nouveau_crtc.h"
+#include "nouveau_hw.h"
+
+static void
+nv04_cursor_show(struct nouveau_crtc *nv_crtc, bool update)
+{
+	nv_show_cursor(nv_crtc->base.dev, nv_crtc->index, true);
+}
+
+static void
+nv04_cursor_hide(struct nouveau_crtc *nv_crtc, bool update)
+{
+	nv_show_cursor(nv_crtc->base.dev, nv_crtc->index, false);
+}
+
+static void
+nv04_cursor_set_pos(struct nouveau_crtc *nv_crtc, int x, int y)
+{
+	NVWriteRAMDAC(nv_crtc->base.dev, nv_crtc->index,
+		      NV_PRAMDAC_CU_START_POS,
+		      XLATE(y, 0, NV_PRAMDAC_CU_START_POS_Y) |
+		      XLATE(x, 0, NV_PRAMDAC_CU_START_POS_X));
+}
+
+static void
+crtc_wr_cio_state(struct drm_crtc *crtc, struct nv04_crtc_reg *crtcstate, int index)
+{
+	NVWriteVgaCrtc(crtc->dev, nouveau_crtc(crtc)->index, index,
+		       crtcstate->CRTC[index]);
+}
+
+static void
+nv04_cursor_set_offset(struct nouveau_crtc *nv_crtc, uint32_t offset)
+{
+	struct drm_device *dev = nv_crtc->base.dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index];
+	struct drm_crtc *crtc = &nv_crtc->base;
+
+	regp->CRTC[NV_CIO_CRE_HCUR_ADDR0_INDEX] =
+		MASK(NV_CIO_CRE_HCUR_ASI) |
+		XLATE(offset, 17, NV_CIO_CRE_HCUR_ADDR0_ADR);
+	regp->CRTC[NV_CIO_CRE_HCUR_ADDR1_INDEX] =
+		XLATE(offset, 11, NV_CIO_CRE_HCUR_ADDR1_ADR);
+	if (crtc->mode.flags & DRM_MODE_FLAG_DBLSCAN)
+		regp->CRTC[NV_CIO_CRE_HCUR_ADDR1_INDEX] |=
+			MASK(NV_CIO_CRE_HCUR_ADDR1_CUR_DBL);
+	regp->CRTC[NV_CIO_CRE_HCUR_ADDR2_INDEX] = offset >> 24;
+
+	crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX);
+	crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX);
+	crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_HCUR_ADDR2_INDEX);
+	if (dev_priv->card_type == NV_40)
+		nv_fix_nv40_hw_cursor(dev, nv_crtc->index);
+}
+
+int
+nv04_cursor_init(struct nouveau_crtc *crtc)
+{
+	crtc->cursor.set_offset = nv04_cursor_set_offset;
+	crtc->cursor.set_pos = nv04_cursor_set_pos;
+	crtc->cursor.hide = nv04_cursor_hide;
+	crtc->cursor.show = nv04_cursor_show;
+	return 0;
+}
+
diff --git a/drivers/gpu/drm/nouveau/nv04_dac.c b/drivers/gpu/drm/nouveau/nv04_dac.c
new file mode 100644
index 0000000..a5fa517
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv04_dac.c
@@ -0,0 +1,528 @@
+/*
+ * Copyright 2003 NVIDIA, Corporation
+ * Copyright 2006 Dave Airlie
+ * Copyright 2007 Maarten Maathuis
+ * Copyright 2007-2009 Stuart Bennett
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "drmP.h"
+#include "drm_crtc_helper.h"
+
+#include "nouveau_drv.h"
+#include "nouveau_encoder.h"
+#include "nouveau_connector.h"
+#include "nouveau_crtc.h"
+#include "nouveau_hw.h"
+#include "nvreg.h"
+
+int nv04_dac_output_offset(struct drm_encoder *encoder)
+{
+	struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
+	int offset = 0;
+
+	if (dcb->or & (8 | OUTPUT_C))
+		offset += 0x68;
+	if (dcb->or & (8 | OUTPUT_B))
+		offset += 0x2000;
+
+	return offset;
+}
+
+/*
+ * arbitrary limit to number of sense oscillations tolerated in one sample
+ * period (observed to be at least 13 in "nvidia")
+ */
+#define MAX_HBLANK_OSC 20
+
+/*
+ * arbitrary limit to number of conflicting sample pairs to tolerate at a
+ * voltage step (observed to be at least 5 in "nvidia")
+ */
+#define MAX_SAMPLE_PAIRS 10
+
+static int sample_load_twice(struct drm_device *dev, bool sense[2])
+{
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		bool sense_a, sense_b, sense_b_prime;
+		int j = 0;
+
+		/*
+		 * wait for bit 0 clear -- out of hblank -- (say reg value 0x4),
+		 * then wait for transition 0x4->0x5->0x4: enter hblank, leave
+		 * hblank again
+		 * use a 10ms timeout (guards against crtc being inactive, in
+		 * which case blank state would never change)
+		 */
+		if (!nouveau_wait_until(dev, 10000000, NV_PRMCIO_INP0__COLOR,
+					0x00000001, 0x00000000))
+			return -EBUSY;
+		if (!nouveau_wait_until(dev, 10000000, NV_PRMCIO_INP0__COLOR,
+					0x00000001, 0x00000001))
+			return -EBUSY;
+		if (!nouveau_wait_until(dev, 10000000, NV_PRMCIO_INP0__COLOR,
+					0x00000001, 0x00000000))
+			return -EBUSY;
+
+		udelay(100);
+		/* when level triggers, sense is _LO_ */
+		sense_a = nv_rd08(dev, NV_PRMCIO_INP0) & 0x10;
+
+		/* take another reading until it agrees with sense_a... */
+		do {
+			udelay(100);
+			sense_b = nv_rd08(dev, NV_PRMCIO_INP0) & 0x10;
+			if (sense_a != sense_b) {
+				sense_b_prime =
+					nv_rd08(dev, NV_PRMCIO_INP0) & 0x10;
+				if (sense_b == sense_b_prime) {
+					/* ... unless two consecutive subsequent
+					 * samples agree; sense_a is replaced */
+					sense_a = sense_b;
+					/* force mis-match so we loop */
+					sense_b = !sense_a;
+				}
+			}
+		} while ((sense_a != sense_b) && ++j < MAX_HBLANK_OSC);
+
+		if (j == MAX_HBLANK_OSC)
+			/* with so much oscillation, default to sense:LO */
+			sense[i] = false;
+		else
+			sense[i] = sense_a;
+	}
+
+	return 0;
+}
+
+static enum drm_connector_status nv04_dac_detect(struct drm_encoder *encoder,
+						 struct drm_connector *connector)
+{
+	struct drm_device *dev = encoder->dev;
+	uint8_t saved_seq1, saved_pi, saved_rpc1;
+	uint8_t saved_palette0[3], saved_palette_mask;
+	uint32_t saved_rtest_ctrl, saved_rgen_ctrl;
+	int i;
+	uint8_t blue;
+	bool sense = true;
+
+	/*
+	 * for this detection to work, there needs to be a mode set up on the
+	 * CRTC.  this is presumed to be the case
+	 */
+
+	if (nv_two_heads(dev))
+		/* only implemented for head A for now */
+		NVSetOwner(dev, 0);
+
+	saved_seq1 = NVReadVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX);
+	NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1 & ~0x20);
+
+	saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL);
+	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL,
+		      saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF);
+
+	msleep(10);
+
+	saved_pi = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX);
+	NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX,
+		       saved_pi & ~(0x80 | MASK(NV_CIO_CRE_PIXEL_FORMAT)));
+	saved_rpc1 = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX);
+	NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1 & ~0xc0);
+
+	nv_wr08(dev, NV_PRMDIO_READ_MODE_ADDRESS, 0x0);
+	for (i = 0; i < 3; i++)
+		saved_palette0[i] = nv_rd08(dev, NV_PRMDIO_PALETTE_DATA);
+	saved_palette_mask = nv_rd08(dev, NV_PRMDIO_PIXEL_MASK);
+	nv_wr08(dev, NV_PRMDIO_PIXEL_MASK, 0);
+
+	saved_rgen_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL);
+	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL,
+		      (saved_rgen_ctrl & ~(NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS |
+					   NV_PRAMDAC_GENERAL_CONTROL_TERMINATION_75OHM)) |
+		      NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON);
+
+	blue = 8;	/* start of test range */
+
+	do {
+		bool sense_pair[2];
+
+		nv_wr08(dev, NV_PRMDIO_WRITE_MODE_ADDRESS, 0);
+		nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, 0);
+		nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, 0);
+		/* testing blue won't find monochrome monitors.  I don't care */
+		nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, blue);
+
+		i = 0;
+		/* take sample pairs until both samples in the pair agree */
+		do {
+			if (sample_load_twice(dev, sense_pair))
+				goto out;
+		} while ((sense_pair[0] != sense_pair[1]) &&
+							++i < MAX_SAMPLE_PAIRS);
+
+		if (i == MAX_SAMPLE_PAIRS)
+			/* too much oscillation defaults to LO */
+			sense = false;
+		else
+			sense = sense_pair[0];
+
+	/*
+	 * if sense goes LO before blue ramps to 0x18, monitor is not connected.
+	 * ergo, if blue gets to 0x18, monitor must be connected
+	 */
+	} while (++blue < 0x18 && sense);
+
+out:
+	nv_wr08(dev, NV_PRMDIO_PIXEL_MASK, saved_palette_mask);
+	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL, saved_rgen_ctrl);
+	nv_wr08(dev, NV_PRMDIO_WRITE_MODE_ADDRESS, 0);
+	for (i = 0; i < 3; i++)
+		nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, saved_palette0[i]);
+	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL, saved_rtest_ctrl);
+	NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX, saved_pi);
+	NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1);
+	NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1);
+
+	if (blue == 0x18) {
+		NV_TRACE(dev, "Load detected on head A\n");
+		return connector_status_connected;
+	}
+
+	return connector_status_disconnected;
+}
+
+enum drm_connector_status nv17_dac_detect(struct drm_encoder *encoder,
+					  struct drm_connector *connector)
+{
+	struct drm_device *dev = encoder->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
+	uint32_t testval, regoffset = nv04_dac_output_offset(encoder);
+	uint32_t saved_powerctrl_2 = 0, saved_powerctrl_4 = 0, saved_routput,
+		saved_rtest_ctrl, saved_gpio0, saved_gpio1, temp, routput;
+	int head, present = 0;
+
+#define RGB_TEST_DATA(r, g, b) (r << 0 | g << 10 | b << 20)
+	if (dcb->type == OUTPUT_TV) {
+		testval = RGB_TEST_DATA(0xa0, 0xa0, 0xa0);
+
+		if (dev_priv->vbios->tvdactestval)
+			testval = dev_priv->vbios->tvdactestval;
+	} else {
+		testval = RGB_TEST_DATA(0x140, 0x140, 0x140); /* 0x94050140 */
+
+		if (dev_priv->vbios->dactestval)
+			testval = dev_priv->vbios->dactestval;
+	}
+
+	saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset);
+	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset,
+		      saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF);
+
+	saved_powerctrl_2 = nvReadMC(dev, NV_PBUS_POWERCTRL_2);
+
+	nvWriteMC(dev, NV_PBUS_POWERCTRL_2, saved_powerctrl_2 & 0xd7ffffff);
+	if (regoffset == 0x68) {
+		saved_powerctrl_4 = nvReadMC(dev, NV_PBUS_POWERCTRL_4);
+		nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4 & 0xffffffcf);
+	}
+
+	saved_gpio1 = nv17_gpio_get(dev, DCB_GPIO_TVDAC1);
+	saved_gpio0 = nv17_gpio_get(dev, DCB_GPIO_TVDAC0);
+
+	nv17_gpio_set(dev, DCB_GPIO_TVDAC1, dcb->type == OUTPUT_TV);
+	nv17_gpio_set(dev, DCB_GPIO_TVDAC0, dcb->type == OUTPUT_TV);
+
+	msleep(4);
+
+	saved_routput = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset);
+	head = (saved_routput & 0x100) >> 8;
+#if 0
+	/* if there's a spare crtc, using it will minimise flicker for the case
+	 * where the in-use crtc is in use by an off-chip tmds encoder */
+	if (xf86_config->crtc[head]->enabled && !xf86_config->crtc[head ^ 1]->enabled)
+		head ^= 1;
+#endif
+	/* nv driver and nv31 use 0xfffffeee, nv34 and 6600 use 0xfffffece */
+	routput = (saved_routput & 0xfffffece) | head << 8;
+
+	if (dev_priv->card_type >= NV_40) {
+		if (dcb->type == OUTPUT_TV)
+			routput |= 0x1a << 16;
+		else
+			routput &= ~(0x1a << 16);
+	}
+
+	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, routput);
+	msleep(1);
+
+	temp = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset);
+	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, temp | 1);
+
+	NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA,
+		      NV_PRAMDAC_TESTPOINT_DATA_NOTBLANK | testval);
+	temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL);
+	NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL,
+		      temp | NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED);
+	msleep(5);
+
+	temp = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset);
+
+	if (dcb->type == OUTPUT_TV)
+		present = (nv17_tv_detect(encoder, connector, temp)
+			   == connector_status_connected);
+	else
+		present = temp & NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI;
+
+	temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL);
+	NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL,
+		      temp & ~NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED);
+	NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA, 0);
+
+	/* bios does something more complex for restoring, but I think this is good enough */
+	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, saved_routput);
+	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, saved_rtest_ctrl);
+	if (regoffset == 0x68)
+		nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4);
+	nvWriteMC(dev, NV_PBUS_POWERCTRL_2, saved_powerctrl_2);
+
+	nv17_gpio_set(dev, DCB_GPIO_TVDAC1, saved_gpio1);
+	nv17_gpio_set(dev, DCB_GPIO_TVDAC0, saved_gpio0);
+
+	if (present) {
+		NV_INFO(dev, "Load detected on output %c\n", '@' + ffs(dcb->or));
+		return connector_status_connected;
+	}
+
+	return connector_status_disconnected;
+}
+
+
+static bool nv04_dac_mode_fixup(struct drm_encoder *encoder,
+				struct drm_display_mode *mode,
+				struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static void nv04_dac_prepare(struct drm_encoder *encoder)
+{
+	struct drm_encoder_helper_funcs *helper = encoder->helper_private;
+	struct drm_device *dev = encoder->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	int head = nouveau_crtc(encoder->crtc)->index;
+	struct nv04_crtc_reg *crtcstate = dev_priv->mode_reg.crtc_reg;
+
+	helper->dpms(encoder, DRM_MODE_DPMS_OFF);
+
+	nv04_dfp_disable(dev, head);
+
+	/* Some NV4x have unknown values (0x3f, 0x50, 0x54, 0x6b, 0x79, 0x7f)
+	 * at LCD__INDEX which we don't alter
+	 */
+	if (!(crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX] & 0x44))
+		crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX] = 0;
+}
+
+
+static void nv04_dac_mode_set(struct drm_encoder *encoder,
+			      struct drm_display_mode *mode,
+			      struct drm_display_mode *adjusted_mode)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct drm_device *dev = encoder->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	int head = nouveau_crtc(encoder->crtc)->index;
+
+	NV_TRACE(dev, "%s called for encoder %d\n", __func__,
+		      nv_encoder->dcb->index);
+
+	if (nv_gf4_disp_arch(dev)) {
+		struct drm_encoder *rebind;
+		uint32_t dac_offset = nv04_dac_output_offset(encoder);
+		uint32_t otherdac;
+
+		/* bit 16-19 are bits that are set on some G70 cards,
+		 * but don't seem to have much effect */
+		NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset,
+			      head << 8 | NV_PRAMDAC_DACCLK_SEL_DACCLK);
+		/* force any other vga encoders to bind to the other crtc */
+		list_for_each_entry(rebind, &dev->mode_config.encoder_list, head) {
+			if (rebind == encoder
+			    || nouveau_encoder(rebind)->dcb->type != OUTPUT_ANALOG)
+				continue;
+
+			dac_offset = nv04_dac_output_offset(rebind);
+			otherdac = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset);
+			NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset,
+				      (otherdac & ~0x0100) | (head ^ 1) << 8);
+		}
+	}
+
+	/* This could use refinement for flatpanels, but it should work this way */
+	if (dev_priv->chipset < 0x44)
+		NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0xf0000000);
+	else
+		NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000);
+}
+
+static void nv04_dac_commit(struct drm_encoder *encoder)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct drm_device *dev = encoder->dev;
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
+	struct drm_encoder_helper_funcs *helper = encoder->helper_private;
+
+	helper->dpms(encoder, DRM_MODE_DPMS_ON);
+
+	NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
+		drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base),
+		nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
+}
+
+void nv04_dac_update_dacclk(struct drm_encoder *encoder, bool enable)
+{
+	struct drm_device *dev = encoder->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
+
+	if (nv_gf4_disp_arch(dev)) {
+		uint32_t *dac_users = &dev_priv->dac_users[ffs(dcb->or) - 1];
+		int dacclk_off = NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder);
+		uint32_t dacclk = NVReadRAMDAC(dev, 0, dacclk_off);
+
+		if (enable) {
+			*dac_users |= 1 << dcb->index;
+			NVWriteRAMDAC(dev, 0, dacclk_off, dacclk | NV_PRAMDAC_DACCLK_SEL_DACCLK);
+
+		} else {
+			*dac_users &= ~(1 << dcb->index);
+			if (!*dac_users)
+				NVWriteRAMDAC(dev, 0, dacclk_off,
+					dacclk & ~NV_PRAMDAC_DACCLK_SEL_DACCLK);
+		}
+	}
+}
+
+static void nv04_dac_dpms(struct drm_encoder *encoder, int mode)
+{
+	struct drm_device *dev = encoder->dev;
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+
+	if (nv_encoder->last_dpms == mode)
+		return;
+	nv_encoder->last_dpms = mode;
+
+	NV_INFO(dev, "Setting dpms mode %d on vga encoder (output %d)\n",
+		     mode, nv_encoder->dcb->index);
+
+	nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON);
+}
+
+static void nv04_dac_save(struct drm_encoder *encoder)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct drm_device *dev = encoder->dev;
+
+	if (nv_gf4_disp_arch(dev))
+		nv_encoder->restore.output = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK +
+							  nv04_dac_output_offset(encoder));
+}
+
+static void nv04_dac_restore(struct drm_encoder *encoder)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct drm_device *dev = encoder->dev;
+
+	if (nv_gf4_disp_arch(dev))
+		NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder),
+			      nv_encoder->restore.output);
+
+	nv_encoder->last_dpms = NV_DPMS_CLEARED;
+}
+
+static void nv04_dac_destroy(struct drm_encoder *encoder)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+
+	NV_DEBUG(encoder->dev, "\n");
+
+	drm_encoder_cleanup(encoder);
+	kfree(nv_encoder);
+}
+
+static const struct drm_encoder_helper_funcs nv04_dac_helper_funcs = {
+	.dpms = nv04_dac_dpms,
+	.save = nv04_dac_save,
+	.restore = nv04_dac_restore,
+	.mode_fixup = nv04_dac_mode_fixup,
+	.prepare = nv04_dac_prepare,
+	.commit = nv04_dac_commit,
+	.mode_set = nv04_dac_mode_set,
+	.detect = nv04_dac_detect
+};
+
+static const struct drm_encoder_helper_funcs nv17_dac_helper_funcs = {
+	.dpms = nv04_dac_dpms,
+	.save = nv04_dac_save,
+	.restore = nv04_dac_restore,
+	.mode_fixup = nv04_dac_mode_fixup,
+	.prepare = nv04_dac_prepare,
+	.commit = nv04_dac_commit,
+	.mode_set = nv04_dac_mode_set,
+	.detect = nv17_dac_detect
+};
+
+static const struct drm_encoder_funcs nv04_dac_funcs = {
+	.destroy = nv04_dac_destroy,
+};
+
+int nv04_dac_create(struct drm_device *dev, struct dcb_entry *entry)
+{
+	const struct drm_encoder_helper_funcs *helper;
+	struct drm_encoder *encoder;
+	struct nouveau_encoder *nv_encoder = NULL;
+
+	nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
+	if (!nv_encoder)
+		return -ENOMEM;
+
+	encoder = to_drm_encoder(nv_encoder);
+
+	nv_encoder->dcb = entry;
+	nv_encoder->or = ffs(entry->or) - 1;
+
+	if (nv_gf4_disp_arch(dev))
+		helper = &nv17_dac_helper_funcs;
+	else
+		helper = &nv04_dac_helper_funcs;
+
+	drm_encoder_init(dev, encoder, &nv04_dac_funcs, DRM_MODE_ENCODER_DAC);
+	drm_encoder_helper_add(encoder, helper);
+
+	encoder->possible_crtcs = entry->heads;
+	encoder->possible_clones = 0;
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nv04_dfp.c b/drivers/gpu/drm/nouveau/nv04_dfp.c
new file mode 100644
index 0000000..e5b3333
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv04_dfp.c
@@ -0,0 +1,621 @@
+/*
+ * Copyright 2003 NVIDIA, Corporation
+ * Copyright 2006 Dave Airlie
+ * Copyright 2007 Maarten Maathuis
+ * Copyright 2007-2009 Stuart Bennett
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "drmP.h"
+#include "drm_crtc_helper.h"
+
+#include "nouveau_drv.h"
+#include "nouveau_encoder.h"
+#include "nouveau_connector.h"
+#include "nouveau_crtc.h"
+#include "nouveau_hw.h"
+#include "nvreg.h"
+
+#define FP_TG_CONTROL_ON  (NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS |	\
+			   NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS |		\
+			   NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS)
+#define FP_TG_CONTROL_OFF (NV_PRAMDAC_FP_TG_CONTROL_DISPEN_DISABLE |	\
+			   NV_PRAMDAC_FP_TG_CONTROL_HSYNC_DISABLE |	\
+			   NV_PRAMDAC_FP_TG_CONTROL_VSYNC_DISABLE)
+
+static inline bool is_fpc_off(uint32_t fpc)
+{
+	return ((fpc & (FP_TG_CONTROL_ON | FP_TG_CONTROL_OFF)) ==
+			FP_TG_CONTROL_OFF);
+}
+
+int nv04_dfp_get_bound_head(struct drm_device *dev, struct dcb_entry *dcbent)
+{
+	/* special case of nv_read_tmds to find crtc associated with an output.
+	 * this does not give a correct answer for off-chip dvi, but there's no
+	 * use for such an answer anyway
+	 */
+	int ramdac = (dcbent->or & OUTPUT_C) >> 2;
+
+	NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_CONTROL,
+	NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE | 0x4);
+	return ((NVReadRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_DATA) & 0x8) >> 3) ^ ramdac;
+}
+
+void nv04_dfp_bind_head(struct drm_device *dev, struct dcb_entry *dcbent,
+			int head, bool dl)
+{
+	/* The BIOS scripts don't do this for us, sadly
+	 * Luckily we do know the values ;-)
+	 *
+	 * head < 0 indicates we wish to force a setting with the overrideval
+	 * (for VT restore etc.)
+	 */
+
+	int ramdac = (dcbent->or & OUTPUT_C) >> 2;
+	uint8_t tmds04 = 0x80;
+
+	if (head != ramdac)
+		tmds04 = 0x88;
+
+	if (dcbent->type == OUTPUT_LVDS)
+		tmds04 |= 0x01;
+
+	nv_write_tmds(dev, dcbent->or, 0, 0x04, tmds04);
+
+	if (dl)	/* dual link */
+		nv_write_tmds(dev, dcbent->or, 1, 0x04, tmds04 ^ 0x08);
+}
+
+void nv04_dfp_disable(struct drm_device *dev, int head)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nv04_crtc_reg *crtcstate = dev_priv->mode_reg.crtc_reg;
+
+	if (NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL) &
+	    FP_TG_CONTROL_ON) {
+		/* digital remnants must be cleaned before new crtc
+		 * values programmed.  delay is time for the vga stuff
+		 * to realise it's in control again
+		 */
+		NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL,
+			      FP_TG_CONTROL_OFF);
+		msleep(50);
+	}
+	/* don't inadvertently turn it on when state written later */
+	crtcstate[head].fp_control = FP_TG_CONTROL_OFF;
+}
+
+void nv04_dfp_update_fp_control(struct drm_encoder *encoder, int mode)
+{
+	struct drm_device *dev = encoder->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct drm_crtc *crtc;
+	struct nouveau_crtc *nv_crtc;
+	uint32_t *fpc;
+
+	if (mode == DRM_MODE_DPMS_ON) {
+		nv_crtc = nouveau_crtc(encoder->crtc);
+		fpc = &dev_priv->mode_reg.crtc_reg[nv_crtc->index].fp_control;
+
+		if (is_fpc_off(*fpc)) {
+			/* using saved value is ok, as (is_digital && dpms_on &&
+			 * fp_control==OFF) is (at present) *only* true when
+			 * fpc's most recent change was by below "off" code
+			 */
+			*fpc = nv_crtc->dpms_saved_fp_control;
+		}
+
+		nv_crtc->fp_users |= 1 << nouveau_encoder(encoder)->dcb->index;
+		NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_FP_TG_CONTROL, *fpc);
+	} else {
+		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+			nv_crtc = nouveau_crtc(crtc);
+			fpc = &dev_priv->mode_reg.crtc_reg[nv_crtc->index].fp_control;
+
+			nv_crtc->fp_users &= ~(1 << nouveau_encoder(encoder)->dcb->index);
+			if (!is_fpc_off(*fpc) && !nv_crtc->fp_users) {
+				nv_crtc->dpms_saved_fp_control = *fpc;
+				/* cut the FP output */
+				*fpc &= ~FP_TG_CONTROL_ON;
+				*fpc |= FP_TG_CONTROL_OFF;
+				NVWriteRAMDAC(dev, nv_crtc->index,
+					      NV_PRAMDAC_FP_TG_CONTROL, *fpc);
+			}
+		}
+	}
+}
+
+static bool nv04_dfp_mode_fixup(struct drm_encoder *encoder,
+				struct drm_display_mode *mode,
+				struct drm_display_mode *adjusted_mode)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct nouveau_connector *nv_connector = nouveau_encoder_connector_get(nv_encoder);
+
+	/* For internal panels and gpu scaling on DVI we need the native mode */
+	if (nv_connector->scaling_mode != DRM_MODE_SCALE_NONE) {
+		if (!nv_connector->native_mode)
+			return false;
+		nv_encoder->mode = *nv_connector->native_mode;
+		adjusted_mode->clock = nv_connector->native_mode->clock;
+	} else {
+		nv_encoder->mode = *adjusted_mode;
+	}
+
+	return true;
+}
+
+static void nv04_dfp_prepare_sel_clk(struct drm_device *dev,
+				     struct nouveau_encoder *nv_encoder, int head)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nv04_mode_state *state = &dev_priv->mode_reg;
+	uint32_t bits1618 = nv_encoder->dcb->or & OUTPUT_A ? 0x10000 : 0x40000;
+
+	if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP)
+		return;
+
+	/* SEL_CLK is only used on the primary ramdac
+	 * It toggles spread spectrum PLL output and sets the bindings of PLLs
+	 * to heads on digital outputs
+	 */
+	if (head)
+		state->sel_clk |= bits1618;
+	else
+		state->sel_clk &= ~bits1618;
+
+	/* nv30:
+	 *	bit 0		NVClk spread spectrum on/off
+	 *	bit 2		MemClk spread spectrum on/off
+	 * 	bit 4		PixClk1 spread spectrum on/off toggle
+	 * 	bit 6		PixClk2 spread spectrum on/off toggle
+	 *
+	 * nv40 (observations from bios behaviour and mmio traces):
+	 * 	bits 4&6	as for nv30
+	 * 	bits 5&7	head dependent as for bits 4&6, but do not appear with 4&6;
+	 * 			maybe a different spread mode
+	 * 	bits 8&10	seen on dual-link dvi outputs, purpose unknown (set by POST scripts)
+	 * 	The logic behind turning spread spectrum on/off in the first place,
+	 * 	and which bit-pair to use, is unclear on nv40 (for earlier cards, the fp table
+	 * 	entry has the necessary info)
+	 */
+	if (nv_encoder->dcb->type == OUTPUT_LVDS && dev_priv->saved_reg.sel_clk & 0xf0) {
+		int shift = (dev_priv->saved_reg.sel_clk & 0x50) ? 0 : 1;
+
+		state->sel_clk &= ~0xf0;
+		state->sel_clk |= (head ? 0x40 : 0x10) << shift;
+	}
+}
+
+static void nv04_dfp_prepare(struct drm_encoder *encoder)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct drm_encoder_helper_funcs *helper = encoder->helper_private;
+	struct drm_device *dev = encoder->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	int head = nouveau_crtc(encoder->crtc)->index;
+	struct nv04_crtc_reg *crtcstate = dev_priv->mode_reg.crtc_reg;
+	uint8_t *cr_lcd = &crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX];
+	uint8_t *cr_lcd_oth = &crtcstate[head ^ 1].CRTC[NV_CIO_CRE_LCD__INDEX];
+
+	helper->dpms(encoder, DRM_MODE_DPMS_OFF);
+
+	nv04_dfp_prepare_sel_clk(dev, nv_encoder, head);
+
+	/* Some NV4x have unknown values (0x3f, 0x50, 0x54, 0x6b, 0x79, 0x7f)
+	 * at LCD__INDEX which we don't alter
+	 */
+	if (!(*cr_lcd & 0x44)) {
+		*cr_lcd = 0x3;
+
+		if (nv_two_heads(dev)) {
+			if (nv_encoder->dcb->location == DCB_LOC_ON_CHIP)
+				*cr_lcd |= head ? 0x0 : 0x8;
+			else {
+				*cr_lcd |= (nv_encoder->dcb->or << 4) & 0x30;
+				if (nv_encoder->dcb->type == OUTPUT_LVDS)
+					*cr_lcd |= 0x30;
+				if ((*cr_lcd & 0x30) == (*cr_lcd_oth & 0x30)) {
+					/* avoid being connected to both crtcs */
+					*cr_lcd_oth &= ~0x30;
+					NVWriteVgaCrtc(dev, head ^ 1,
+						       NV_CIO_CRE_LCD__INDEX,
+						       *cr_lcd_oth);
+				}
+			}
+		}
+	}
+}
+
+
+static void nv04_dfp_mode_set(struct drm_encoder *encoder,
+			      struct drm_display_mode *mode,
+			      struct drm_display_mode *adjusted_mode)
+{
+	struct drm_device *dev = encoder->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
+	struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index];
+	struct nv04_crtc_reg *savep = &dev_priv->saved_reg.crtc_reg[nv_crtc->index];
+	struct nouveau_connector *nv_connector = nouveau_crtc_connector_get(nv_crtc);
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct drm_display_mode *output_mode = &nv_encoder->mode;
+	uint32_t mode_ratio, panel_ratio;
+
+	NV_DEBUG(dev, "Output mode on CRTC %d:\n", nv_crtc->index);
+	drm_mode_debug_printmodeline(output_mode);
+
+	/* Initialize the FP registers in this CRTC. */
+	regp->fp_horiz_regs[FP_DISPLAY_END] = output_mode->hdisplay - 1;
+	regp->fp_horiz_regs[FP_TOTAL] = output_mode->htotal - 1;
+	if (!nv_gf4_disp_arch(dev) ||
+	    (output_mode->hsync_start - output_mode->hdisplay) >=
+					dev_priv->vbios->digital_min_front_porch)
+		regp->fp_horiz_regs[FP_CRTC] = output_mode->hdisplay;
+	else
+		regp->fp_horiz_regs[FP_CRTC] = output_mode->hsync_start - dev_priv->vbios->digital_min_front_porch - 1;
+	regp->fp_horiz_regs[FP_SYNC_START] = output_mode->hsync_start - 1;
+	regp->fp_horiz_regs[FP_SYNC_END] = output_mode->hsync_end - 1;
+	regp->fp_horiz_regs[FP_VALID_START] = output_mode->hskew;
+	regp->fp_horiz_regs[FP_VALID_END] = output_mode->hdisplay - 1;
+
+	regp->fp_vert_regs[FP_DISPLAY_END] = output_mode->vdisplay - 1;
+	regp->fp_vert_regs[FP_TOTAL] = output_mode->vtotal - 1;
+	regp->fp_vert_regs[FP_CRTC] = output_mode->vtotal - 5 - 1;
+	regp->fp_vert_regs[FP_SYNC_START] = output_mode->vsync_start - 1;
+	regp->fp_vert_regs[FP_SYNC_END] = output_mode->vsync_end - 1;
+	regp->fp_vert_regs[FP_VALID_START] = 0;
+	regp->fp_vert_regs[FP_VALID_END] = output_mode->vdisplay - 1;
+
+	/* bit26: a bit seen on some g7x, no as yet discernable purpose */
+	regp->fp_control = NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS |
+			   (savep->fp_control & (1 << 26 | NV_PRAMDAC_FP_TG_CONTROL_READ_PROG));
+	/* Deal with vsync/hsync polarity */
+	/* LVDS screens do set this, but modes with +ve syncs are very rare */
+	if (output_mode->flags & DRM_MODE_FLAG_PVSYNC)
+		regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS;
+	if (output_mode->flags & DRM_MODE_FLAG_PHSYNC)
+		regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS;
+	/* panel scaling first, as native would get set otherwise */
+	if (nv_connector->scaling_mode == DRM_MODE_SCALE_NONE ||
+	    nv_connector->scaling_mode == DRM_MODE_SCALE_CENTER)	/* panel handles it */
+		regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_CENTER;
+	else if (adjusted_mode->hdisplay == output_mode->hdisplay &&
+		 adjusted_mode->vdisplay == output_mode->vdisplay) /* native mode */
+		regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_NATIVE;
+	else /* gpu needs to scale */
+		regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_SCALE;
+	if (nvReadEXTDEV(dev, NV_PEXTDEV_BOOT_0) & NV_PEXTDEV_BOOT_0_STRAP_FP_IFACE_12BIT)
+		regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12;
+	if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP &&
+	    output_mode->clock > 165000)
+		regp->fp_control |= (2 << 24);
+	if (nv_encoder->dcb->type == OUTPUT_LVDS) {
+		bool duallink, dummy;
+
+		nouveau_bios_parse_lvds_table(dev, nv_connector->native_mode->
+					      clock, &duallink, &dummy);
+		if (duallink)
+			regp->fp_control |= (8 << 28);
+	} else
+	if (output_mode->clock > 165000)
+		regp->fp_control |= (8 << 28);
+
+	regp->fp_debug_0 = NV_PRAMDAC_FP_DEBUG_0_YWEIGHT_ROUND |
+			   NV_PRAMDAC_FP_DEBUG_0_XWEIGHT_ROUND |
+			   NV_PRAMDAC_FP_DEBUG_0_YINTERP_BILINEAR |
+			   NV_PRAMDAC_FP_DEBUG_0_XINTERP_BILINEAR |
+			   NV_RAMDAC_FP_DEBUG_0_TMDS_ENABLED |
+			   NV_PRAMDAC_FP_DEBUG_0_YSCALE_ENABLE |
+			   NV_PRAMDAC_FP_DEBUG_0_XSCALE_ENABLE;
+
+	/* We want automatic scaling */
+	regp->fp_debug_1 = 0;
+	/* This can override HTOTAL and VTOTAL */
+	regp->fp_debug_2 = 0;
+
+	/* Use 20.12 fixed point format to avoid floats */
+	mode_ratio = (1 << 12) * adjusted_mode->hdisplay / adjusted_mode->vdisplay;
+	panel_ratio = (1 << 12) * output_mode->hdisplay / output_mode->vdisplay;
+	/* if ratios are equal, SCALE_ASPECT will automatically (and correctly)
+	 * get treated the same as SCALE_FULLSCREEN */
+	if (nv_connector->scaling_mode == DRM_MODE_SCALE_ASPECT &&
+	    mode_ratio != panel_ratio) {
+		uint32_t diff, scale;
+		bool divide_by_2 = nv_gf4_disp_arch(dev);
+
+		if (mode_ratio < panel_ratio) {
+			/* vertical needs to expand to glass size (automatic)
+			 * horizontal needs to be scaled at vertical scale factor
+			 * to maintain aspect */
+
+			scale = (1 << 12) * adjusted_mode->vdisplay / output_mode->vdisplay;
+			regp->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_XSCALE_TESTMODE_ENABLE |
+					   XLATE(scale, divide_by_2, NV_PRAMDAC_FP_DEBUG_1_XSCALE_VALUE);
+
+			/* restrict area of screen used, horizontally */
+			diff = output_mode->hdisplay -
+			       output_mode->vdisplay * mode_ratio / (1 << 12);
+			regp->fp_horiz_regs[FP_VALID_START] += diff / 2;
+			regp->fp_horiz_regs[FP_VALID_END] -= diff / 2;
+		}
+
+		if (mode_ratio > panel_ratio) {
+			/* horizontal needs to expand to glass size (automatic)
+			 * vertical needs to be scaled at horizontal scale factor
+			 * to maintain aspect */
+
+			scale = (1 << 12) * adjusted_mode->hdisplay / output_mode->hdisplay;
+			regp->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_YSCALE_TESTMODE_ENABLE |
+					   XLATE(scale, divide_by_2, NV_PRAMDAC_FP_DEBUG_1_YSCALE_VALUE);
+
+			/* restrict area of screen used, vertically */
+			diff = output_mode->vdisplay -
+			       (1 << 12) * output_mode->hdisplay / mode_ratio;
+			regp->fp_vert_regs[FP_VALID_START] += diff / 2;
+			regp->fp_vert_regs[FP_VALID_END] -= diff / 2;
+		}
+	}
+
+	/* Output property. */
+	if (nv_connector->use_dithering) {
+		if (dev_priv->chipset == 0x11)
+			regp->dither = savep->dither | 0x00010000;
+		else {
+			int i;
+			regp->dither = savep->dither | 0x00000001;
+			for (i = 0; i < 3; i++) {
+				regp->dither_regs[i] = 0xe4e4e4e4;
+				regp->dither_regs[i + 3] = 0x44444444;
+			}
+		}
+	} else {
+		if (dev_priv->chipset != 0x11) {
+			/* reset them */
+			int i;
+			for (i = 0; i < 3; i++) {
+				regp->dither_regs[i] = savep->dither_regs[i];
+				regp->dither_regs[i + 3] = savep->dither_regs[i + 3];
+			}
+		}
+		regp->dither = savep->dither;
+	}
+
+	regp->fp_margin_color = 0;
+}
+
+static void nv04_dfp_commit(struct drm_encoder *encoder)
+{
+	struct drm_device *dev = encoder->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct drm_encoder_helper_funcs *helper = encoder->helper_private;
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct dcb_entry *dcbe = nv_encoder->dcb;
+	int head = nouveau_crtc(encoder->crtc)->index;
+
+	NV_TRACE(dev, "%s called for encoder %d\n", __func__, nv_encoder->dcb->index);
+
+	if (dcbe->type == OUTPUT_TMDS)
+		run_tmds_table(dev, dcbe, head, nv_encoder->mode.clock);
+	else if (dcbe->type == OUTPUT_LVDS)
+		call_lvds_script(dev, dcbe, head, LVDS_RESET, nv_encoder->mode.clock);
+
+	/* update fp_control state for any changes made by scripts,
+	 * so correct value is written at DPMS on */
+	dev_priv->mode_reg.crtc_reg[head].fp_control =
+		NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL);
+
+	/* This could use refinement for flatpanels, but it should work this way */
+	if (dev_priv->chipset < 0x44)
+		NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0xf0000000);
+	else
+		NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000);
+
+	helper->dpms(encoder, DRM_MODE_DPMS_ON);
+
+	NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
+		drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base),
+		nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
+}
+
+static inline bool is_powersaving_dpms(int mode)
+{
+	return (mode != DRM_MODE_DPMS_ON);
+}
+
+static void nv04_lvds_dpms(struct drm_encoder *encoder, int mode)
+{
+	struct drm_device *dev = encoder->dev;
+	struct drm_crtc *crtc = encoder->crtc;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	bool was_powersaving = is_powersaving_dpms(nv_encoder->last_dpms);
+
+	if (nv_encoder->last_dpms == mode)
+		return;
+	nv_encoder->last_dpms = mode;
+
+	NV_INFO(dev, "Setting dpms mode %d on lvds encoder (output %d)\n",
+		     mode, nv_encoder->dcb->index);
+
+	if (was_powersaving && is_powersaving_dpms(mode))
+		return;
+
+	if (nv_encoder->dcb->lvdsconf.use_power_scripts) {
+		struct nouveau_connector *nv_connector = nouveau_encoder_connector_get(nv_encoder);
+
+		/* when removing an output, crtc may not be set, but PANEL_OFF
+		 * must still be run
+		 */
+		int head = crtc ? nouveau_crtc(crtc)->index :
+			   nv04_dfp_get_bound_head(dev, nv_encoder->dcb);
+
+		if (mode == DRM_MODE_DPMS_ON) {
+			if (!nv_connector->native_mode) {
+				NV_ERROR(dev, "Not turning on LVDS without native mode\n");
+				return;
+			}
+			call_lvds_script(dev, nv_encoder->dcb, head,
+					 LVDS_PANEL_ON, nv_connector->native_mode->clock);
+		} else
+			/* pxclk of 0 is fine for PANEL_OFF, and for a
+			 * disconnected LVDS encoder there is no native_mode
+			 */
+			call_lvds_script(dev, nv_encoder->dcb, head,
+					 LVDS_PANEL_OFF, 0);
+	}
+
+	nv04_dfp_update_fp_control(encoder, mode);
+
+	if (mode == DRM_MODE_DPMS_ON)
+		nv04_dfp_prepare_sel_clk(dev, nv_encoder, nouveau_crtc(crtc)->index);
+	else {
+		dev_priv->mode_reg.sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK);
+		dev_priv->mode_reg.sel_clk &= ~0xf0;
+	}
+	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, dev_priv->mode_reg.sel_clk);
+}
+
+static void nv04_tmds_dpms(struct drm_encoder *encoder, int mode)
+{
+	struct drm_device *dev = encoder->dev;
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+
+	if (nv_encoder->last_dpms == mode)
+		return;
+	nv_encoder->last_dpms = mode;
+
+	NV_INFO(dev, "Setting dpms mode %d on tmds encoder (output %d)\n",
+		     mode, nv_encoder->dcb->index);
+
+	nv04_dfp_update_fp_control(encoder, mode);
+}
+
+static void nv04_dfp_save(struct drm_encoder *encoder)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct drm_device *dev = encoder->dev;
+
+	if (nv_two_heads(dev))
+		nv_encoder->restore.head =
+			nv04_dfp_get_bound_head(dev, nv_encoder->dcb);
+}
+
+static void nv04_dfp_restore(struct drm_encoder *encoder)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct drm_device *dev = encoder->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	int head = nv_encoder->restore.head;
+
+	if (nv_encoder->dcb->type == OUTPUT_LVDS) {
+		struct drm_display_mode *native_mode = nouveau_encoder_connector_get(nv_encoder)->native_mode;
+		if (native_mode)
+			call_lvds_script(dev, nv_encoder->dcb, head, LVDS_PANEL_ON,
+					 native_mode->clock);
+		else
+			NV_ERROR(dev, "Not restoring LVDS without native mode\n");
+
+	} else if (nv_encoder->dcb->type == OUTPUT_TMDS) {
+		int clock = nouveau_hw_pllvals_to_clk
+					(&dev_priv->saved_reg.crtc_reg[head].pllvals);
+
+		run_tmds_table(dev, nv_encoder->dcb, head, clock);
+	}
+
+	nv_encoder->last_dpms = NV_DPMS_CLEARED;
+}
+
+static void nv04_dfp_destroy(struct drm_encoder *encoder)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+
+	NV_DEBUG(encoder->dev, "\n");
+
+	drm_encoder_cleanup(encoder);
+	kfree(nv_encoder);
+}
+
+static const struct drm_encoder_helper_funcs nv04_lvds_helper_funcs = {
+	.dpms = nv04_lvds_dpms,
+	.save = nv04_dfp_save,
+	.restore = nv04_dfp_restore,
+	.mode_fixup = nv04_dfp_mode_fixup,
+	.prepare = nv04_dfp_prepare,
+	.commit = nv04_dfp_commit,
+	.mode_set = nv04_dfp_mode_set,
+	.detect = NULL,
+};
+
+static const struct drm_encoder_helper_funcs nv04_tmds_helper_funcs = {
+	.dpms = nv04_tmds_dpms,
+	.save = nv04_dfp_save,
+	.restore = nv04_dfp_restore,
+	.mode_fixup = nv04_dfp_mode_fixup,
+	.prepare = nv04_dfp_prepare,
+	.commit = nv04_dfp_commit,
+	.mode_set = nv04_dfp_mode_set,
+	.detect = NULL,
+};
+
+static const struct drm_encoder_funcs nv04_dfp_funcs = {
+	.destroy = nv04_dfp_destroy,
+};
+
+int nv04_dfp_create(struct drm_device *dev, struct dcb_entry *entry)
+{
+	const struct drm_encoder_helper_funcs *helper;
+	struct drm_encoder *encoder;
+	struct nouveau_encoder *nv_encoder = NULL;
+	int type;
+
+	switch (entry->type) {
+	case OUTPUT_TMDS:
+		type = DRM_MODE_ENCODER_TMDS;
+		helper = &nv04_tmds_helper_funcs;
+		break;
+	case OUTPUT_LVDS:
+		type = DRM_MODE_ENCODER_LVDS;
+		helper = &nv04_lvds_helper_funcs;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
+	if (!nv_encoder)
+		return -ENOMEM;
+
+	encoder = to_drm_encoder(nv_encoder);
+
+	nv_encoder->dcb = entry;
+	nv_encoder->or = ffs(entry->or) - 1;
+
+	drm_encoder_init(dev, encoder, &nv04_dfp_funcs, type);
+	drm_encoder_helper_add(encoder, helper);
+
+	encoder->possible_crtcs = entry->heads;
+	encoder->possible_clones = 0;
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nv04_display.c b/drivers/gpu/drm/nouveau/nv04_display.c
new file mode 100644
index 0000000..b47c757
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv04_display.c
@@ -0,0 +1,288 @@
+/*
+ * Copyright 2009 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Author: Ben Skeggs
+ */
+
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc_helper.h"
+
+#include "nouveau_drv.h"
+#include "nouveau_fb.h"
+#include "nouveau_hw.h"
+#include "nouveau_encoder.h"
+#include "nouveau_connector.h"
+
+#define MULTIPLE_ENCODERS(e) (e & (e - 1))
+
+static void
+nv04_display_store_initial_head_owner(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	if (dev_priv->chipset != 0x11) {
+		dev_priv->crtc_owner = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_44);
+		goto ownerknown;
+	}
+
+	/* reading CR44 is broken on nv11, so we attempt to infer it */
+	if (nvReadMC(dev, NV_PBUS_DEBUG_1) & (1 << 28))	/* heads tied, restore both */
+		dev_priv->crtc_owner = 0x4;
+	else {
+		uint8_t slaved_on_A, slaved_on_B;
+		bool tvA = false;
+		bool tvB = false;
+
+		NVLockVgaCrtcs(dev, false);
+
+		slaved_on_B = NVReadVgaCrtc(dev, 1, NV_CIO_CRE_PIXEL_INDEX) &
+									0x80;
+		if (slaved_on_B)
+			tvB = !(NVReadVgaCrtc(dev, 1, NV_CIO_CRE_LCD__INDEX) &
+					MASK(NV_CIO_CRE_LCD_LCD_SELECT));
+
+		slaved_on_A = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX) &
+									0x80;
+		if (slaved_on_A)
+			tvA = !(NVReadVgaCrtc(dev, 0, NV_CIO_CRE_LCD__INDEX) &
+					MASK(NV_CIO_CRE_LCD_LCD_SELECT));
+
+		NVLockVgaCrtcs(dev, true);
+
+		if (slaved_on_A && !tvA)
+			dev_priv->crtc_owner = 0x0;
+		else if (slaved_on_B && !tvB)
+			dev_priv->crtc_owner = 0x3;
+		else if (slaved_on_A)
+			dev_priv->crtc_owner = 0x0;
+		else if (slaved_on_B)
+			dev_priv->crtc_owner = 0x3;
+		else
+			dev_priv->crtc_owner = 0x0;
+	}
+
+ownerknown:
+	NV_INFO(dev, "Initial CRTC_OWNER is %d\n", dev_priv->crtc_owner);
+
+	/* we need to ensure the heads are not tied henceforth, or reading any
+	 * 8 bit reg on head B will fail
+	 * setting a single arbitrary head solves that */
+	NVSetOwner(dev, 0);
+}
+
+int
+nv04_display_create(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct parsed_dcb *dcb = dev_priv->vbios->dcb;
+	struct drm_encoder *encoder;
+	struct drm_crtc *crtc;
+	uint16_t connector[16] = { 0 };
+	int i, ret;
+
+	NV_DEBUG(dev, "\n");
+
+	if (nv_two_heads(dev))
+		nv04_display_store_initial_head_owner(dev);
+
+	drm_mode_config_init(dev);
+	drm_mode_create_scaling_mode_property(dev);
+	drm_mode_create_dithering_property(dev);
+
+	dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs;
+
+	dev->mode_config.min_width = 0;
+	dev->mode_config.min_height = 0;
+	switch (dev_priv->card_type) {
+	case NV_04:
+		dev->mode_config.max_width = 2048;
+		dev->mode_config.max_height = 2048;
+		break;
+	default:
+		dev->mode_config.max_width = 4096;
+		dev->mode_config.max_height = 4096;
+		break;
+	}
+
+	dev->mode_config.fb_base = dev_priv->fb_phys;
+
+	nv04_crtc_create(dev, 0);
+	if (nv_two_heads(dev))
+		nv04_crtc_create(dev, 1);
+
+	for (i = 0; i < dcb->entries; i++) {
+		struct dcb_entry *dcbent = &dcb->entry[i];
+
+		switch (dcbent->type) {
+		case OUTPUT_ANALOG:
+			ret = nv04_dac_create(dev, dcbent);
+			break;
+		case OUTPUT_LVDS:
+		case OUTPUT_TMDS:
+			ret = nv04_dfp_create(dev, dcbent);
+			break;
+		case OUTPUT_TV:
+			if (dcbent->location == DCB_LOC_ON_CHIP)
+				ret = nv17_tv_create(dev, dcbent);
+			else
+				ret = nv04_tv_create(dev, dcbent);
+			break;
+		default:
+			NV_WARN(dev, "DCB type %d not known\n", dcbent->type);
+			continue;
+		}
+
+		if (ret)
+			continue;
+
+		connector[dcbent->connector] |= (1 << dcbent->type);
+	}
+
+	for (i = 0; i < dcb->entries; i++) {
+		struct dcb_entry *dcbent = &dcb->entry[i];
+		uint16_t encoders;
+		int type;
+
+		encoders = connector[dcbent->connector];
+		if (!(encoders & (1 << dcbent->type)))
+			continue;
+		connector[dcbent->connector] = 0;
+
+		switch (dcbent->type) {
+		case OUTPUT_ANALOG:
+			if (!MULTIPLE_ENCODERS(encoders))
+				type = DRM_MODE_CONNECTOR_VGA;
+			else
+				type = DRM_MODE_CONNECTOR_DVII;
+			break;
+		case OUTPUT_TMDS:
+			if (!MULTIPLE_ENCODERS(encoders))
+				type = DRM_MODE_CONNECTOR_DVID;
+			else
+				type = DRM_MODE_CONNECTOR_DVII;
+			break;
+		case OUTPUT_LVDS:
+			type = DRM_MODE_CONNECTOR_LVDS;
+#if 0
+			/* don't create i2c adapter when lvds ddc not allowed */
+			if (dcbent->lvdsconf.use_straps_for_mode ||
+			    dev_priv->vbios->fp_no_ddc)
+				i2c_index = 0xf;
+#endif
+			break;
+		case OUTPUT_TV:
+			type = DRM_MODE_CONNECTOR_TV;
+			break;
+		default:
+			type = DRM_MODE_CONNECTOR_Unknown;
+			continue;
+		}
+
+		nouveau_connector_create(dev, dcbent->connector, type);
+	}
+
+	/* Save previous state */
+	NVLockVgaCrtcs(dev, false);
+
+	nouveau_hw_save_vga_fonts(dev, 1);
+
+	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+		crtc->funcs->save(crtc);
+
+	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+		struct drm_encoder_helper_funcs *func = encoder->helper_private;
+
+		func->save(encoder);
+	}
+
+	return 0;
+}
+
+void
+nv04_display_destroy(struct drm_device *dev)
+{
+	struct drm_encoder *encoder;
+	struct drm_crtc *crtc;
+
+	NV_DEBUG(dev, "\n");
+
+	/* Turn every CRTC off. */
+	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+		struct drm_mode_set modeset = {
+			.crtc = crtc,
+		};
+
+		crtc->funcs->set_config(&modeset);
+	}
+
+	/* Restore state */
+	NVLockVgaCrtcs(dev, false);
+
+	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+		struct drm_encoder_helper_funcs *func = encoder->helper_private;
+
+		func->restore(encoder);
+	}
+
+	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+		crtc->funcs->restore(crtc);
+
+	nouveau_hw_save_vga_fonts(dev, 0);
+
+	drm_mode_config_cleanup(dev);
+}
+
+void
+nv04_display_restore(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct drm_encoder *encoder;
+	struct drm_crtc *crtc;
+
+	NVLockVgaCrtcs(dev, false);
+
+	/* meh.. modeset apparently doesn't setup all the regs and depends
+	 * on pre-existing state, for now load the state of the card *before*
+	 * nouveau was loaded, and then do a modeset.
+	 *
+	 * best thing to do probably is to make save/restore routines not
+	 * save/restore "pre-load" state, but more general so we can save
+	 * on suspend too.
+	 */
+	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+		struct drm_encoder_helper_funcs *func = encoder->helper_private;
+
+		func->restore(encoder);
+	}
+
+	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+		crtc->funcs->restore(crtc);
+
+	if (nv_two_heads(dev)) {
+		NV_INFO(dev, "Restoring CRTC_OWNER to %d.\n",
+			dev_priv->crtc_owner);
+		NVSetOwner(dev, dev_priv->crtc_owner);
+	}
+
+	NVLockVgaCrtcs(dev, true);
+}
+
diff --git a/drivers/gpu/drm/nouveau/nv04_fb.c b/drivers/gpu/drm/nouveau/nv04_fb.c
new file mode 100644
index 0000000..638cf60
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv04_fb.c
@@ -0,0 +1,21 @@
+#include "drmP.h"
+#include "drm.h"
+#include "nouveau_drv.h"
+#include "nouveau_drm.h"
+
+int
+nv04_fb_init(struct drm_device *dev)
+{
+	/* This is what the DDX did for NV_ARCH_04, but a mmio-trace shows
+	 * nvidia reading PFB_CFG_0, then writing back its original value.
+	 * (which was 0x701114 in this case)
+	 */
+
+	nv_wr32(dev, NV04_PFB_CFG0, 0x1114);
+	return 0;
+}
+
+void
+nv04_fb_takedown(struct drm_device *dev)
+{
+}
diff --git a/drivers/gpu/drm/nouveau/nv04_fbcon.c b/drivers/gpu/drm/nouveau/nv04_fbcon.c
new file mode 100644
index 0000000..09a3107
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv04_fbcon.c
@@ -0,0 +1,316 @@
+/*
+ * Copyright 2009 Ben Skeggs
+ * Copyright 2008 Stuart Bennett
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "drmP.h"
+#include "nouveau_drv.h"
+#include "nouveau_dma.h"
+#include "nouveau_fbcon.h"
+
+static void
+nv04_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region)
+{
+	struct nouveau_fbcon_par *par = info->par;
+	struct drm_device *dev = par->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_channel *chan = dev_priv->channel;
+
+	if (info->state != FBINFO_STATE_RUNNING)
+		return;
+
+	if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 4)) {
+		NV_ERROR(dev, "GPU lockup - switching to software fbcon\n");
+		info->flags |= FBINFO_HWACCEL_DISABLED;
+	}
+
+	if (info->flags & FBINFO_HWACCEL_DISABLED) {
+		cfb_copyarea(info, region);
+		return;
+	}
+
+	BEGIN_RING(chan, NvSubImageBlit, 0x0300, 3);
+	OUT_RING(chan, (region->sy << 16) | region->sx);
+	OUT_RING(chan, (region->dy << 16) | region->dx);
+	OUT_RING(chan, (region->height << 16) | region->width);
+	FIRE_RING(chan);
+}
+
+static void
+nv04_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
+{
+	struct nouveau_fbcon_par *par = info->par;
+	struct drm_device *dev = par->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_channel *chan = dev_priv->channel;
+	uint32_t color = ((uint32_t *) info->pseudo_palette)[rect->color];
+
+	if (info->state != FBINFO_STATE_RUNNING)
+		return;
+
+	if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 7)) {
+		NV_ERROR(dev, "GPU lockup - switching to software fbcon\n");
+		info->flags |= FBINFO_HWACCEL_DISABLED;
+	}
+
+	if (info->flags & FBINFO_HWACCEL_DISABLED) {
+		cfb_fillrect(info, rect);
+		return;
+	}
+
+	BEGIN_RING(chan, NvSubGdiRect, 0x02fc, 1);
+	OUT_RING(chan, (rect->rop != ROP_COPY) ? 1 : 3);
+	BEGIN_RING(chan, NvSubGdiRect, 0x03fc, 1);
+	OUT_RING(chan, color);
+	BEGIN_RING(chan, NvSubGdiRect, 0x0400, 2);
+	OUT_RING(chan, (rect->dx << 16) | rect->dy);
+	OUT_RING(chan, (rect->width << 16) | rect->height);
+	FIRE_RING(chan);
+}
+
+static void
+nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
+{
+	struct nouveau_fbcon_par *par = info->par;
+	struct drm_device *dev = par->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_channel *chan = dev_priv->channel;
+	uint32_t fg;
+	uint32_t bg;
+	uint32_t dsize;
+	uint32_t width;
+	uint32_t *data = (uint32_t *)image->data;
+
+	if (info->state != FBINFO_STATE_RUNNING)
+		return;
+
+	if (image->depth != 1) {
+		cfb_imageblit(info, image);
+		return;
+	}
+
+	if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 8)) {
+		NV_ERROR(dev, "GPU lockup - switching to software fbcon\n");
+		info->flags |= FBINFO_HWACCEL_DISABLED;
+	}
+
+	if (info->flags & FBINFO_HWACCEL_DISABLED) {
+		cfb_imageblit(info, image);
+		return;
+	}
+
+	width = (image->width + 31) & ~31;
+	dsize = (width * image->height) >> 5;
+
+	if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
+	    info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
+		fg = ((uint32_t *) info->pseudo_palette)[image->fg_color];
+		bg = ((uint32_t *) info->pseudo_palette)[image->bg_color];
+	} else {
+		fg = image->fg_color;
+		bg = image->bg_color;
+	}
+
+	BEGIN_RING(chan, NvSubGdiRect, 0x0be4, 7);
+	OUT_RING(chan, (image->dy << 16) | (image->dx & 0xffff));
+	OUT_RING(chan, ((image->dy + image->height) << 16) |
+			 ((image->dx + image->width) & 0xffff));
+	OUT_RING(chan, bg);
+	OUT_RING(chan, fg);
+	OUT_RING(chan, (image->height << 16) | image->width);
+	OUT_RING(chan, (image->height << 16) | width);
+	OUT_RING(chan, (image->dy << 16) | (image->dx & 0xffff));
+
+	while (dsize) {
+		int iter_len = dsize > 128 ? 128 : dsize;
+
+		if (RING_SPACE(chan, iter_len + 1)) {
+			NV_ERROR(dev, "GPU lockup - switching to software fbcon\n");
+			info->flags |= FBINFO_HWACCEL_DISABLED;
+			cfb_imageblit(info, image);
+			return;
+		}
+
+		BEGIN_RING(chan, NvSubGdiRect, 0x0c00, iter_len);
+		OUT_RINGp(chan, data, iter_len);
+		data += iter_len;
+		dsize -= iter_len;
+	}
+
+	FIRE_RING(chan);
+}
+
+static int
+nv04_fbcon_grobj_new(struct drm_device *dev, int class, uint32_t handle)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_gpuobj *obj = NULL;
+	int ret;
+
+	ret = nouveau_gpuobj_gr_new(dev_priv->channel, class, &obj);
+	if (ret)
+		return ret;
+
+	ret = nouveau_gpuobj_ref_add(dev, dev_priv->channel, handle, obj, NULL);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+int
+nv04_fbcon_accel_init(struct fb_info *info)
+{
+	struct nouveau_fbcon_par *par = info->par;
+	struct drm_device *dev = par->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_channel *chan = dev_priv->channel;
+	int surface_fmt, pattern_fmt, rect_fmt;
+	int ret;
+
+	switch (info->var.bits_per_pixel) {
+	case 8:
+		surface_fmt = 1;
+		pattern_fmt = 3;
+		rect_fmt = 3;
+		break;
+	case 16:
+		surface_fmt = 4;
+		pattern_fmt = 1;
+		rect_fmt = 1;
+		break;
+	case 32:
+		switch (info->var.transp.length) {
+		case 0: /* depth 24 */
+		case 8: /* depth 32 */
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		surface_fmt = 6;
+		pattern_fmt = 3;
+		rect_fmt = 3;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = nv04_fbcon_grobj_new(dev, dev_priv->card_type >= NV_10 ?
+				   0x0062 : 0x0042, NvCtxSurf2D);
+	if (ret)
+		return ret;
+
+	ret = nv04_fbcon_grobj_new(dev, 0x0019, NvClipRect);
+	if (ret)
+		return ret;
+
+	ret = nv04_fbcon_grobj_new(dev, 0x0043, NvRop);
+	if (ret)
+		return ret;
+
+	ret = nv04_fbcon_grobj_new(dev, 0x0044, NvImagePatt);
+	if (ret)
+		return ret;
+
+	ret = nv04_fbcon_grobj_new(dev, 0x004a, NvGdiRect);
+	if (ret)
+		return ret;
+
+	ret = nv04_fbcon_grobj_new(dev, dev_priv->card_type >= NV_10 ?
+				   0x009f : 0x005f, NvImageBlit);
+	if (ret)
+		return ret;
+
+	if (RING_SPACE(chan, 49)) {
+		NV_ERROR(dev, "GPU lockup - switching to software fbcon\n");
+		info->flags |= FBINFO_HWACCEL_DISABLED;
+		return 0;
+	}
+
+	BEGIN_RING(chan, 1, 0x0000, 1);
+	OUT_RING(chan, NvCtxSurf2D);
+	BEGIN_RING(chan, 1, 0x0184, 2);
+	OUT_RING(chan, NvDmaFB);
+	OUT_RING(chan, NvDmaFB);
+	BEGIN_RING(chan, 1, 0x0300, 4);
+	OUT_RING(chan, surface_fmt);
+	OUT_RING(chan, info->fix.line_length | (info->fix.line_length << 16));
+	OUT_RING(chan, info->fix.smem_start - dev->mode_config.fb_base);
+	OUT_RING(chan, info->fix.smem_start - dev->mode_config.fb_base);
+
+	BEGIN_RING(chan, 1, 0x0000, 1);
+	OUT_RING(chan, NvRop);
+	BEGIN_RING(chan, 1, 0x0300, 1);
+	OUT_RING(chan, 0x55);
+
+	BEGIN_RING(chan, 1, 0x0000, 1);
+	OUT_RING(chan, NvImagePatt);
+	BEGIN_RING(chan, 1, 0x0300, 8);
+	OUT_RING(chan, pattern_fmt);
+#ifdef __BIG_ENDIAN
+	OUT_RING(chan, 2);
+#else
+	OUT_RING(chan, 1);
+#endif
+	OUT_RING(chan, 0);
+	OUT_RING(chan, 1);
+	OUT_RING(chan, ~0);
+	OUT_RING(chan, ~0);
+	OUT_RING(chan, ~0);
+	OUT_RING(chan, ~0);
+
+	BEGIN_RING(chan, 1, 0x0000, 1);
+	OUT_RING(chan, NvClipRect);
+	BEGIN_RING(chan, 1, 0x0300, 2);
+	OUT_RING(chan, 0);
+	OUT_RING(chan, (info->var.yres_virtual << 16) | info->var.xres_virtual);
+
+	BEGIN_RING(chan, NvSubImageBlit, 0x0000, 1);
+	OUT_RING(chan, NvImageBlit);
+	BEGIN_RING(chan, NvSubImageBlit, 0x019c, 1);
+	OUT_RING(chan, NvCtxSurf2D);
+	BEGIN_RING(chan, NvSubImageBlit, 0x02fc, 1);
+	OUT_RING(chan, 3);
+
+	BEGIN_RING(chan, NvSubGdiRect, 0x0000, 1);
+	OUT_RING(chan, NvGdiRect);
+	BEGIN_RING(chan, NvSubGdiRect, 0x0198, 1);
+	OUT_RING(chan, NvCtxSurf2D);
+	BEGIN_RING(chan, NvSubGdiRect, 0x0188, 2);
+	OUT_RING(chan, NvImagePatt);
+	OUT_RING(chan, NvRop);
+	BEGIN_RING(chan, NvSubGdiRect, 0x0304, 1);
+	OUT_RING(chan, 1);
+	BEGIN_RING(chan, NvSubGdiRect, 0x0300, 1);
+	OUT_RING(chan, rect_fmt);
+	BEGIN_RING(chan, NvSubGdiRect, 0x02fc, 1);
+	OUT_RING(chan, 3);
+
+	FIRE_RING(chan);
+
+	info->fbops->fb_fillrect = nv04_fbcon_fillrect;
+	info->fbops->fb_copyarea = nv04_fbcon_copyarea;
+	info->fbops->fb_imageblit = nv04_fbcon_imageblit;
+	return 0;
+}
+
diff --git a/drivers/gpu/drm/nouveau/nv04_fifo.c b/drivers/gpu/drm/nouveau/nv04_fifo.c
new file mode 100644
index 0000000..0c3cd53
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv04_fifo.c
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2007 Ben Skeggs.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "drmP.h"
+#include "drm.h"
+#include "nouveau_drv.h"
+
+#define NV04_RAMFC(c) (dev_priv->ramfc_offset + ((c) * NV04_RAMFC__SIZE))
+#define NV04_RAMFC__SIZE 32
+#define NV04_RAMFC_DMA_PUT                                       0x00
+#define NV04_RAMFC_DMA_GET                                       0x04
+#define NV04_RAMFC_DMA_INSTANCE                                  0x08
+#define NV04_RAMFC_DMA_STATE                                     0x0C
+#define NV04_RAMFC_DMA_FETCH                                     0x10
+#define NV04_RAMFC_ENGINE                                        0x14
+#define NV04_RAMFC_PULL1_ENGINE                                  0x18
+
+#define RAMFC_WR(offset, val) nv_wo32(dev, chan->ramfc->gpuobj, \
+					 NV04_RAMFC_##offset/4, (val))
+#define RAMFC_RD(offset)      nv_ro32(dev, chan->ramfc->gpuobj, \
+					 NV04_RAMFC_##offset/4)
+
+void
+nv04_fifo_disable(struct drm_device *dev)
+{
+	uint32_t tmp;
+
+	tmp = nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUSH);
+	nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUSH, tmp & ~1);
+	nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0, 0);
+	tmp = nv_rd32(dev, NV03_PFIFO_CACHE1_PULL1);
+	nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, tmp & ~1);
+}
+
+void
+nv04_fifo_enable(struct drm_device *dev)
+{
+	nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0, 1);
+	nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, 1);
+}
+
+bool
+nv04_fifo_reassign(struct drm_device *dev, bool enable)
+{
+	uint32_t reassign = nv_rd32(dev, NV03_PFIFO_CACHES);
+
+	nv_wr32(dev, NV03_PFIFO_CACHES, enable ? 1 : 0);
+	return (reassign == 1);
+}
+
+int
+nv04_fifo_channel_id(struct drm_device *dev)
+{
+	return nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH1) &
+			NV03_PFIFO_CACHE1_PUSH1_CHID_MASK;
+}
+
+int
+nv04_fifo_create_context(struct nouveau_channel *chan)
+{
+	struct drm_device *dev = chan->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	int ret;
+
+	ret = nouveau_gpuobj_new_fake(dev, NV04_RAMFC(chan->id), ~0,
+						NV04_RAMFC__SIZE,
+						NVOBJ_FLAG_ZERO_ALLOC |
+						NVOBJ_FLAG_ZERO_FREE,
+						NULL, &chan->ramfc);
+	if (ret)
+		return ret;
+
+	/* Setup initial state */
+	dev_priv->engine.instmem.prepare_access(dev, true);
+	RAMFC_WR(DMA_PUT, chan->pushbuf_base);
+	RAMFC_WR(DMA_GET, chan->pushbuf_base);
+	RAMFC_WR(DMA_INSTANCE, chan->pushbuf->instance >> 4);
+	RAMFC_WR(DMA_FETCH, (NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES |
+			     NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES |
+			     NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8 |
+#ifdef __BIG_ENDIAN
+			     NV_PFIFO_CACHE1_BIG_ENDIAN |
+#endif
+			     0));
+	dev_priv->engine.instmem.finish_access(dev);
+
+	/* enable the fifo dma operation */
+	nv_wr32(dev, NV04_PFIFO_MODE,
+		nv_rd32(dev, NV04_PFIFO_MODE) | (1 << chan->id));
+	return 0;
+}
+
+void
+nv04_fifo_destroy_context(struct nouveau_channel *chan)
+{
+	struct drm_device *dev = chan->dev;
+
+	nv_wr32(dev, NV04_PFIFO_MODE,
+		nv_rd32(dev, NV04_PFIFO_MODE) & ~(1 << chan->id));
+
+	nouveau_gpuobj_ref_del(dev, &chan->ramfc);
+}
+
+static void
+nv04_fifo_do_load_context(struct drm_device *dev, int chid)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	uint32_t fc = NV04_RAMFC(chid), tmp;
+
+	dev_priv->engine.instmem.prepare_access(dev, false);
+
+	nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUT, nv_ri32(dev, fc + 0));
+	nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_GET, nv_ri32(dev, fc + 4));
+	tmp = nv_ri32(dev, fc + 8);
+	nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_INSTANCE, tmp & 0xFFFF);
+	nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_DCOUNT, tmp >> 16);
+	nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_STATE, nv_ri32(dev, fc + 12));
+	nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_FETCH, nv_ri32(dev, fc + 16));
+	nv_wr32(dev, NV04_PFIFO_CACHE1_ENGINE, nv_ri32(dev, fc + 20));
+	nv_wr32(dev, NV04_PFIFO_CACHE1_PULL1, nv_ri32(dev, fc + 24));
+
+	dev_priv->engine.instmem.finish_access(dev);
+
+	nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0);
+	nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, 0);
+}
+
+int
+nv04_fifo_load_context(struct nouveau_channel *chan)
+{
+	uint32_t tmp;
+
+	nv_wr32(chan->dev, NV03_PFIFO_CACHE1_PUSH1,
+			   NV03_PFIFO_CACHE1_PUSH1_DMA | chan->id);
+	nv04_fifo_do_load_context(chan->dev, chan->id);
+	nv_wr32(chan->dev, NV04_PFIFO_CACHE1_DMA_PUSH, 1);
+
+	/* Reset NV04_PFIFO_CACHE1_DMA_CTL_AT_INFO to INVALID */
+	tmp = nv_rd32(chan->dev, NV04_PFIFO_CACHE1_DMA_CTL) & ~(1 << 31);
+	nv_wr32(chan->dev, NV04_PFIFO_CACHE1_DMA_CTL, tmp);
+
+	return 0;
+}
+
+int
+nv04_fifo_unload_context(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
+	struct nouveau_channel *chan = NULL;
+	uint32_t tmp;
+	int chid;
+
+	chid = pfifo->channel_id(dev);
+	if (chid < 0 || chid >= dev_priv->engine.fifo.channels)
+		return 0;
+
+	chan = dev_priv->fifos[chid];
+	if (!chan) {
+		NV_ERROR(dev, "Inactive channel on PFIFO: %d\n", chid);
+		return -EINVAL;
+	}
+
+	dev_priv->engine.instmem.prepare_access(dev, true);
+	RAMFC_WR(DMA_PUT, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUT));
+	RAMFC_WR(DMA_GET, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET));
+	tmp  = nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_DCOUNT) << 16;
+	tmp |= nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_INSTANCE);
+	RAMFC_WR(DMA_INSTANCE, tmp);
+	RAMFC_WR(DMA_STATE, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_STATE));
+	RAMFC_WR(DMA_FETCH, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_FETCH));
+	RAMFC_WR(ENGINE, nv_rd32(dev, NV04_PFIFO_CACHE1_ENGINE));
+	RAMFC_WR(PULL1_ENGINE, nv_rd32(dev, NV04_PFIFO_CACHE1_PULL1));
+	dev_priv->engine.instmem.finish_access(dev);
+
+	nv04_fifo_do_load_context(dev, pfifo->channels - 1);
+	nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, pfifo->channels - 1);
+	return 0;
+}
+
+static void
+nv04_fifo_init_reset(struct drm_device *dev)
+{
+	nv_wr32(dev, NV03_PMC_ENABLE,
+		nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PFIFO);
+	nv_wr32(dev, NV03_PMC_ENABLE,
+		nv_rd32(dev, NV03_PMC_ENABLE) |  NV_PMC_ENABLE_PFIFO);
+
+	nv_wr32(dev, 0x003224, 0x000f0078);
+	nv_wr32(dev, 0x002044, 0x0101ffff);
+	nv_wr32(dev, 0x002040, 0x000000ff);
+	nv_wr32(dev, 0x002500, 0x00000000);
+	nv_wr32(dev, 0x003000, 0x00000000);
+	nv_wr32(dev, 0x003050, 0x00000000);
+	nv_wr32(dev, 0x003200, 0x00000000);
+	nv_wr32(dev, 0x003250, 0x00000000);
+	nv_wr32(dev, 0x003220, 0x00000000);
+
+	nv_wr32(dev, 0x003250, 0x00000000);
+	nv_wr32(dev, 0x003270, 0x00000000);
+	nv_wr32(dev, 0x003210, 0x00000000);
+}
+
+static void
+nv04_fifo_init_ramxx(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	nv_wr32(dev, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ |
+				       ((dev_priv->ramht_bits - 9) << 16) |
+				       (dev_priv->ramht_offset >> 8));
+	nv_wr32(dev, NV03_PFIFO_RAMRO, dev_priv->ramro_offset>>8);
+	nv_wr32(dev, NV03_PFIFO_RAMFC, dev_priv->ramfc_offset >> 8);
+}
+
+static void
+nv04_fifo_init_intr(struct drm_device *dev)
+{
+	nv_wr32(dev, 0x002100, 0xffffffff);
+	nv_wr32(dev, 0x002140, 0xffffffff);
+}
+
+int
+nv04_fifo_init(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
+	int i;
+
+	nv04_fifo_init_reset(dev);
+	nv04_fifo_init_ramxx(dev);
+
+	nv04_fifo_do_load_context(dev, pfifo->channels - 1);
+	nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, pfifo->channels - 1);
+
+	nv04_fifo_init_intr(dev);
+	pfifo->enable(dev);
+
+	for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
+		if (dev_priv->fifos[i]) {
+			uint32_t mode = nv_rd32(dev, NV04_PFIFO_MODE);
+			nv_wr32(dev, NV04_PFIFO_MODE, mode | (1 << i));
+		}
+	}
+
+	return 0;
+}
+
diff --git a/drivers/gpu/drm/nouveau/nv04_graph.c b/drivers/gpu/drm/nouveau/nv04_graph.c
new file mode 100644
index 0000000..396ee92
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv04_graph.c
@@ -0,0 +1,579 @@
+/*
+ * Copyright 2007 Stephane Marchesin
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "drmP.h"
+#include "drm.h"
+#include "nouveau_drm.h"
+#include "nouveau_drv.h"
+
+static uint32_t nv04_graph_ctx_regs[] = {
+	NV04_PGRAPH_CTX_SWITCH1,
+	NV04_PGRAPH_CTX_SWITCH2,
+	NV04_PGRAPH_CTX_SWITCH3,
+	NV04_PGRAPH_CTX_SWITCH4,
+	NV04_PGRAPH_CTX_CACHE1,
+	NV04_PGRAPH_CTX_CACHE2,
+	NV04_PGRAPH_CTX_CACHE3,
+	NV04_PGRAPH_CTX_CACHE4,
+	0x00400184,
+	0x004001a4,
+	0x004001c4,
+	0x004001e4,
+	0x00400188,
+	0x004001a8,
+	0x004001c8,
+	0x004001e8,
+	0x0040018c,
+	0x004001ac,
+	0x004001cc,
+	0x004001ec,
+	0x00400190,
+	0x004001b0,
+	0x004001d0,
+	0x004001f0,
+	0x00400194,
+	0x004001b4,
+	0x004001d4,
+	0x004001f4,
+	0x00400198,
+	0x004001b8,
+	0x004001d8,
+	0x004001f8,
+	0x0040019c,
+	0x004001bc,
+	0x004001dc,
+	0x004001fc,
+	0x00400174,
+	NV04_PGRAPH_DMA_START_0,
+	NV04_PGRAPH_DMA_START_1,
+	NV04_PGRAPH_DMA_LENGTH,
+	NV04_PGRAPH_DMA_MISC,
+	NV04_PGRAPH_DMA_PITCH,
+	NV04_PGRAPH_BOFFSET0,
+	NV04_PGRAPH_BBASE0,
+	NV04_PGRAPH_BLIMIT0,
+	NV04_PGRAPH_BOFFSET1,
+	NV04_PGRAPH_BBASE1,
+	NV04_PGRAPH_BLIMIT1,
+	NV04_PGRAPH_BOFFSET2,
+	NV04_PGRAPH_BBASE2,
+	NV04_PGRAPH_BLIMIT2,
+	NV04_PGRAPH_BOFFSET3,
+	NV04_PGRAPH_BBASE3,
+	NV04_PGRAPH_BLIMIT3,
+	NV04_PGRAPH_BOFFSET4,
+	NV04_PGRAPH_BBASE4,
+	NV04_PGRAPH_BLIMIT4,
+	NV04_PGRAPH_BOFFSET5,
+	NV04_PGRAPH_BBASE5,
+	NV04_PGRAPH_BLIMIT5,
+	NV04_PGRAPH_BPITCH0,
+	NV04_PGRAPH_BPITCH1,
+	NV04_PGRAPH_BPITCH2,
+	NV04_PGRAPH_BPITCH3,
+	NV04_PGRAPH_BPITCH4,
+	NV04_PGRAPH_SURFACE,
+	NV04_PGRAPH_STATE,
+	NV04_PGRAPH_BSWIZZLE2,
+	NV04_PGRAPH_BSWIZZLE5,
+	NV04_PGRAPH_BPIXEL,
+	NV04_PGRAPH_NOTIFY,
+	NV04_PGRAPH_PATT_COLOR0,
+	NV04_PGRAPH_PATT_COLOR1,
+	NV04_PGRAPH_PATT_COLORRAM+0x00,
+	NV04_PGRAPH_PATT_COLORRAM+0x01,
+	NV04_PGRAPH_PATT_COLORRAM+0x02,
+	NV04_PGRAPH_PATT_COLORRAM+0x03,
+	NV04_PGRAPH_PATT_COLORRAM+0x04,
+	NV04_PGRAPH_PATT_COLORRAM+0x05,
+	NV04_PGRAPH_PATT_COLORRAM+0x06,
+	NV04_PGRAPH_PATT_COLORRAM+0x07,
+	NV04_PGRAPH_PATT_COLORRAM+0x08,
+	NV04_PGRAPH_PATT_COLORRAM+0x09,
+	NV04_PGRAPH_PATT_COLORRAM+0x0A,
+	NV04_PGRAPH_PATT_COLORRAM+0x0B,
+	NV04_PGRAPH_PATT_COLORRAM+0x0C,
+	NV04_PGRAPH_PATT_COLORRAM+0x0D,
+	NV04_PGRAPH_PATT_COLORRAM+0x0E,
+	NV04_PGRAPH_PATT_COLORRAM+0x0F,
+	NV04_PGRAPH_PATT_COLORRAM+0x10,
+	NV04_PGRAPH_PATT_COLORRAM+0x11,
+	NV04_PGRAPH_PATT_COLORRAM+0x12,
+	NV04_PGRAPH_PATT_COLORRAM+0x13,
+	NV04_PGRAPH_PATT_COLORRAM+0x14,
+	NV04_PGRAPH_PATT_COLORRAM+0x15,
+	NV04_PGRAPH_PATT_COLORRAM+0x16,
+	NV04_PGRAPH_PATT_COLORRAM+0x17,
+	NV04_PGRAPH_PATT_COLORRAM+0x18,
+	NV04_PGRAPH_PATT_COLORRAM+0x19,
+	NV04_PGRAPH_PATT_COLORRAM+0x1A,
+	NV04_PGRAPH_PATT_COLORRAM+0x1B,
+	NV04_PGRAPH_PATT_COLORRAM+0x1C,
+	NV04_PGRAPH_PATT_COLORRAM+0x1D,
+	NV04_PGRAPH_PATT_COLORRAM+0x1E,
+	NV04_PGRAPH_PATT_COLORRAM+0x1F,
+	NV04_PGRAPH_PATT_COLORRAM+0x20,
+	NV04_PGRAPH_PATT_COLORRAM+0x21,
+	NV04_PGRAPH_PATT_COLORRAM+0x22,
+	NV04_PGRAPH_PATT_COLORRAM+0x23,
+	NV04_PGRAPH_PATT_COLORRAM+0x24,
+	NV04_PGRAPH_PATT_COLORRAM+0x25,
+	NV04_PGRAPH_PATT_COLORRAM+0x26,
+	NV04_PGRAPH_PATT_COLORRAM+0x27,
+	NV04_PGRAPH_PATT_COLORRAM+0x28,
+	NV04_PGRAPH_PATT_COLORRAM+0x29,
+	NV04_PGRAPH_PATT_COLORRAM+0x2A,
+	NV04_PGRAPH_PATT_COLORRAM+0x2B,
+	NV04_PGRAPH_PATT_COLORRAM+0x2C,
+	NV04_PGRAPH_PATT_COLORRAM+0x2D,
+	NV04_PGRAPH_PATT_COLORRAM+0x2E,
+	NV04_PGRAPH_PATT_COLORRAM+0x2F,
+	NV04_PGRAPH_PATT_COLORRAM+0x30,
+	NV04_PGRAPH_PATT_COLORRAM+0x31,
+	NV04_PGRAPH_PATT_COLORRAM+0x32,
+	NV04_PGRAPH_PATT_COLORRAM+0x33,
+	NV04_PGRAPH_PATT_COLORRAM+0x34,
+	NV04_PGRAPH_PATT_COLORRAM+0x35,
+	NV04_PGRAPH_PATT_COLORRAM+0x36,
+	NV04_PGRAPH_PATT_COLORRAM+0x37,
+	NV04_PGRAPH_PATT_COLORRAM+0x38,
+	NV04_PGRAPH_PATT_COLORRAM+0x39,
+	NV04_PGRAPH_PATT_COLORRAM+0x3A,
+	NV04_PGRAPH_PATT_COLORRAM+0x3B,
+	NV04_PGRAPH_PATT_COLORRAM+0x3C,
+	NV04_PGRAPH_PATT_COLORRAM+0x3D,
+	NV04_PGRAPH_PATT_COLORRAM+0x3E,
+	NV04_PGRAPH_PATT_COLORRAM+0x3F,
+	NV04_PGRAPH_PATTERN,
+	0x0040080c,
+	NV04_PGRAPH_PATTERN_SHAPE,
+	0x00400600,
+	NV04_PGRAPH_ROP3,
+	NV04_PGRAPH_CHROMA,
+	NV04_PGRAPH_BETA_AND,
+	NV04_PGRAPH_BETA_PREMULT,
+	NV04_PGRAPH_CONTROL0,
+	NV04_PGRAPH_CONTROL1,
+	NV04_PGRAPH_CONTROL2,
+	NV04_PGRAPH_BLEND,
+	NV04_PGRAPH_STORED_FMT,
+	NV04_PGRAPH_SOURCE_COLOR,
+	0x00400560,
+	0x00400568,
+	0x00400564,
+	0x0040056c,
+	0x00400400,
+	0x00400480,
+	0x00400404,
+	0x00400484,
+	0x00400408,
+	0x00400488,
+	0x0040040c,
+	0x0040048c,
+	0x00400410,
+	0x00400490,
+	0x00400414,
+	0x00400494,
+	0x00400418,
+	0x00400498,
+	0x0040041c,
+	0x0040049c,
+	0x00400420,
+	0x004004a0,
+	0x00400424,
+	0x004004a4,
+	0x00400428,
+	0x004004a8,
+	0x0040042c,
+	0x004004ac,
+	0x00400430,
+	0x004004b0,
+	0x00400434,
+	0x004004b4,
+	0x00400438,
+	0x004004b8,
+	0x0040043c,
+	0x004004bc,
+	0x00400440,
+	0x004004c0,
+	0x00400444,
+	0x004004c4,
+	0x00400448,
+	0x004004c8,
+	0x0040044c,
+	0x004004cc,
+	0x00400450,
+	0x004004d0,
+	0x00400454,
+	0x004004d4,
+	0x00400458,
+	0x004004d8,
+	0x0040045c,
+	0x004004dc,
+	0x00400460,
+	0x004004e0,
+	0x00400464,
+	0x004004e4,
+	0x00400468,
+	0x004004e8,
+	0x0040046c,
+	0x004004ec,
+	0x00400470,
+	0x004004f0,
+	0x00400474,
+	0x004004f4,
+	0x00400478,
+	0x004004f8,
+	0x0040047c,
+	0x004004fc,
+	0x0040053c,
+	0x00400544,
+	0x00400540,
+	0x00400548,
+	0x00400560,
+	0x00400568,
+	0x00400564,
+	0x0040056c,
+	0x00400534,
+	0x00400538,
+	0x00400514,
+	0x00400518,
+	0x0040051c,
+	0x00400520,
+	0x00400524,
+	0x00400528,
+	0x0040052c,
+	0x00400530,
+	0x00400d00,
+	0x00400d40,
+	0x00400d80,
+	0x00400d04,
+	0x00400d44,
+	0x00400d84,
+	0x00400d08,
+	0x00400d48,
+	0x00400d88,
+	0x00400d0c,
+	0x00400d4c,
+	0x00400d8c,
+	0x00400d10,
+	0x00400d50,
+	0x00400d90,
+	0x00400d14,
+	0x00400d54,
+	0x00400d94,
+	0x00400d18,
+	0x00400d58,
+	0x00400d98,
+	0x00400d1c,
+	0x00400d5c,
+	0x00400d9c,
+	0x00400d20,
+	0x00400d60,
+	0x00400da0,
+	0x00400d24,
+	0x00400d64,
+	0x00400da4,
+	0x00400d28,
+	0x00400d68,
+	0x00400da8,
+	0x00400d2c,
+	0x00400d6c,
+	0x00400dac,
+	0x00400d30,
+	0x00400d70,
+	0x00400db0,
+	0x00400d34,
+	0x00400d74,
+	0x00400db4,
+	0x00400d38,
+	0x00400d78,
+	0x00400db8,
+	0x00400d3c,
+	0x00400d7c,
+	0x00400dbc,
+	0x00400590,
+	0x00400594,
+	0x00400598,
+	0x0040059c,
+	0x004005a8,
+	0x004005ac,
+	0x004005b0,
+	0x004005b4,
+	0x004005c0,
+	0x004005c4,
+	0x004005c8,
+	0x004005cc,
+	0x004005d0,
+	0x004005d4,
+	0x004005d8,
+	0x004005dc,
+	0x004005e0,
+	NV04_PGRAPH_PASSTHRU_0,
+	NV04_PGRAPH_PASSTHRU_1,
+	NV04_PGRAPH_PASSTHRU_2,
+	NV04_PGRAPH_DVD_COLORFMT,
+	NV04_PGRAPH_SCALED_FORMAT,
+	NV04_PGRAPH_MISC24_0,
+	NV04_PGRAPH_MISC24_1,
+	NV04_PGRAPH_MISC24_2,
+	0x00400500,
+	0x00400504,
+	NV04_PGRAPH_VALID1,
+	NV04_PGRAPH_VALID2
+
+
+};
+
+struct graph_state {
+	int nv04[ARRAY_SIZE(nv04_graph_ctx_regs)];
+};
+
+struct nouveau_channel *
+nv04_graph_channel(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	int chid = dev_priv->engine.fifo.channels;
+
+	if (nv_rd32(dev, NV04_PGRAPH_CTX_CONTROL) & 0x00010000)
+		chid = nv_rd32(dev, NV04_PGRAPH_CTX_USER) >> 24;
+
+	if (chid >= dev_priv->engine.fifo.channels)
+		return NULL;
+
+	return dev_priv->fifos[chid];
+}
+
+void
+nv04_graph_context_switch(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
+	struct nouveau_channel *chan = NULL;
+	int chid;
+
+	pgraph->fifo_access(dev, false);
+	nouveau_wait_for_idle(dev);
+
+	/* If previous context is valid, we need to save it */
+	pgraph->unload_context(dev);
+
+	/* Load context for next channel */
+	chid = dev_priv->engine.fifo.channel_id(dev);
+	chan = dev_priv->fifos[chid];
+	if (chan)
+		nv04_graph_load_context(chan);
+
+	pgraph->fifo_access(dev, true);
+}
+
+int nv04_graph_create_context(struct nouveau_channel *chan)
+{
+	struct graph_state *pgraph_ctx;
+	NV_DEBUG(chan->dev, "nv04_graph_context_create %d\n", chan->id);
+
+	chan->pgraph_ctx = pgraph_ctx = kzalloc(sizeof(*pgraph_ctx),
+						GFP_KERNEL);
+	if (pgraph_ctx == NULL)
+		return -ENOMEM;
+
+	/* dev_priv->fifos[channel].pgraph_ctx_user = channel << 24; */
+	pgraph_ctx->nv04[0] = 0x0001ffff;
+	/* is it really needed ??? */
+#if 0
+	dev_priv->fifos[channel].pgraph_ctx[1] =
+					nv_rd32(dev, NV_PGRAPH_DEBUG_4);
+	dev_priv->fifos[channel].pgraph_ctx[2] =
+					nv_rd32(dev, 0x004006b0);
+#endif
+	return 0;
+}
+
+void nv04_graph_destroy_context(struct nouveau_channel *chan)
+{
+	struct graph_state *pgraph_ctx = chan->pgraph_ctx;
+
+	kfree(pgraph_ctx);
+	chan->pgraph_ctx = NULL;
+}
+
+int nv04_graph_load_context(struct nouveau_channel *chan)
+{
+	struct drm_device *dev = chan->dev;
+	struct graph_state *pgraph_ctx = chan->pgraph_ctx;
+	uint32_t tmp;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(nv04_graph_ctx_regs); i++)
+		nv_wr32(dev, nv04_graph_ctx_regs[i], pgraph_ctx->nv04[i]);
+
+	nv_wr32(dev, NV04_PGRAPH_CTX_CONTROL, 0x10010100);
+	nv_wr32(dev, NV04_PGRAPH_CTX_USER, chan->id << 24);
+	tmp = nv_rd32(dev, NV04_PGRAPH_FFINTFC_ST2);
+	nv_wr32(dev, NV04_PGRAPH_FFINTFC_ST2, tmp & 0x000fffff);
+	return 0;
+}
+
+int
+nv04_graph_unload_context(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
+	struct nouveau_channel *chan = NULL;
+	struct graph_state *ctx;
+	uint32_t tmp;
+	int i;
+
+	chan = pgraph->channel(dev);
+	if (!chan)
+		return 0;
+	ctx = chan->pgraph_ctx;
+
+	for (i = 0; i < ARRAY_SIZE(nv04_graph_ctx_regs); i++)
+		ctx->nv04[i] = nv_rd32(dev, nv04_graph_ctx_regs[i]);
+
+	nv_wr32(dev, NV04_PGRAPH_CTX_CONTROL, 0x10000000);
+	tmp  = nv_rd32(dev, NV04_PGRAPH_CTX_USER) & 0x00ffffff;
+	tmp |= (dev_priv->engine.fifo.channels - 1) << 24;
+	nv_wr32(dev, NV04_PGRAPH_CTX_USER, tmp);
+	return 0;
+}
+
+int nv04_graph_init(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	uint32_t tmp;
+
+	nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) &
+			~NV_PMC_ENABLE_PGRAPH);
+	nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) |
+			 NV_PMC_ENABLE_PGRAPH);
+
+	/* Enable PGRAPH interrupts */
+	nv_wr32(dev, NV03_PGRAPH_INTR, 0xFFFFFFFF);
+	nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF);
+
+	nv_wr32(dev, NV04_PGRAPH_VALID1, 0);
+	nv_wr32(dev, NV04_PGRAPH_VALID2, 0);
+	/*nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x000001FF);
+	nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x001FFFFF);*/
+	nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x1231c000);
+	/*1231C000 blob, 001 haiku*/
+	//*V_WRITE(NV04_PGRAPH_DEBUG_1, 0xf2d91100);*/
+	nv_wr32(dev, NV04_PGRAPH_DEBUG_1, 0x72111100);
+	/*0x72111100 blob , 01 haiku*/
+	/*nv_wr32(dev, NV04_PGRAPH_DEBUG_2, 0x11d5f870);*/
+	nv_wr32(dev, NV04_PGRAPH_DEBUG_2, 0x11d5f071);
+	/*haiku same*/
+
+	/*nv_wr32(dev, NV04_PGRAPH_DEBUG_3, 0xfad4ff31);*/
+	nv_wr32(dev, NV04_PGRAPH_DEBUG_3, 0xf0d4ff31);
+	/*haiku and blob 10d4*/
+
+	nv_wr32(dev, NV04_PGRAPH_STATE        , 0xFFFFFFFF);
+	nv_wr32(dev, NV04_PGRAPH_CTX_CONTROL  , 0x10000100);
+	tmp  = nv_rd32(dev, NV04_PGRAPH_CTX_USER) & 0x00ffffff;
+	tmp |= dev_priv->engine.fifo.channels << 24;
+	nv_wr32(dev, NV04_PGRAPH_CTX_USER, tmp);
+
+	/* These don't belong here, they're part of a per-channel context */
+	nv_wr32(dev, NV04_PGRAPH_PATTERN_SHAPE, 0x00000000);
+	nv_wr32(dev, NV04_PGRAPH_BETA_AND     , 0xFFFFFFFF);
+
+	return 0;
+}
+
+void nv04_graph_takedown(struct drm_device *dev)
+{
+}
+
+void
+nv04_graph_fifo_access(struct drm_device *dev, bool enabled)
+{
+	if (enabled)
+		nv_wr32(dev, NV04_PGRAPH_FIFO,
+					nv_rd32(dev, NV04_PGRAPH_FIFO) | 1);
+	else
+		nv_wr32(dev, NV04_PGRAPH_FIFO,
+					nv_rd32(dev, NV04_PGRAPH_FIFO) & ~1);
+}
+
+static int
+nv04_graph_mthd_set_ref(struct nouveau_channel *chan, int grclass,
+			int mthd, uint32_t data)
+{
+	chan->fence.last_sequence_irq = data;
+	nouveau_fence_handler(chan->dev, chan->id);
+	return 0;
+}
+
+static int
+nv04_graph_mthd_set_operation(struct nouveau_channel *chan, int grclass,
+			      int mthd, uint32_t data)
+{
+	struct drm_device *dev = chan->dev;
+	uint32_t instance = nv_rd32(dev, NV04_PGRAPH_CTX_SWITCH4) & 0xffff;
+	int subc = (nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR) >> 13) & 0x7;
+	uint32_t tmp;
+
+	tmp  = nv_ri32(dev, instance);
+	tmp &= ~0x00038000;
+	tmp |= ((data & 7) << 15);
+
+	nv_wi32(dev, instance, tmp);
+	nv_wr32(dev, NV04_PGRAPH_CTX_SWITCH1, tmp);
+	nv_wr32(dev, NV04_PGRAPH_CTX_CACHE1 + subc, tmp);
+	return 0;
+}
+
+static struct nouveau_pgraph_object_method nv04_graph_mthds_m2mf[] = {
+	{ 0x0150, nv04_graph_mthd_set_ref },
+	{}
+};
+
+static struct nouveau_pgraph_object_method nv04_graph_mthds_set_operation[] = {
+	{ 0x02fc, nv04_graph_mthd_set_operation },
+	{},
+};
+
+struct nouveau_pgraph_object_class nv04_graph_grclass[] = {
+	{ 0x0039, false, nv04_graph_mthds_m2mf },
+	{ 0x004a, false, nv04_graph_mthds_set_operation }, /* gdirect */
+	{ 0x005f, false, nv04_graph_mthds_set_operation }, /* imageblit */
+	{ 0x0061, false, nv04_graph_mthds_set_operation }, /* ifc */
+	{ 0x0077, false, nv04_graph_mthds_set_operation }, /* sifm */
+	{ 0x0030, false, NULL }, /* null */
+	{ 0x0042, false, NULL }, /* surf2d */
+	{ 0x0043, false, NULL }, /* rop */
+	{ 0x0012, false, NULL }, /* beta1 */
+	{ 0x0072, false, NULL }, /* beta4 */
+	{ 0x0019, false, NULL }, /* cliprect */
+	{ 0x0044, false, NULL }, /* pattern */
+	{ 0x0052, false, NULL }, /* swzsurf */
+	{ 0x0053, false, NULL }, /* surf3d */
+	{ 0x0054, false, NULL }, /* tex_tri */
+	{ 0x0055, false, NULL }, /* multitex_tri */
+	{}
+};
+
diff --git a/drivers/gpu/drm/nouveau/nv04_instmem.c b/drivers/gpu/drm/nouveau/nv04_instmem.c
new file mode 100644
index 0000000..a20c206
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv04_instmem.c
@@ -0,0 +1,208 @@
+#include "drmP.h"
+#include "drm.h"
+#include "nouveau_drv.h"
+
+/* returns the size of fifo context */
+static int
+nouveau_fifo_ctx_size(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	if (dev_priv->chipset >= 0x40)
+		return 128;
+	else
+	if (dev_priv->chipset >= 0x17)
+		return 64;
+
+	return 32;
+}
+
+static void
+nv04_instmem_determine_amount(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	int i;
+
+	/* Figure out how much instance memory we need */
+	if (dev_priv->card_type >= NV_40) {
+		/* We'll want more instance memory than this on some NV4x cards.
+		 * There's a 16MB aperture to play with that maps onto the end
+		 * of vram.  For now, only reserve a small piece until we know
+		 * more about what each chipset requires.
+		 */
+		switch (dev_priv->chipset & 0xf0) {
+		case 0x40:
+		case 0x47:
+		case 0x49:
+		case 0x4b:
+			dev_priv->ramin_rsvd_vram = (2 * 1024 * 1024);
+			break;
+		default:
+			dev_priv->ramin_rsvd_vram = (1 * 1024 * 1024);
+			break;
+		}
+	} else {
+		/*XXX: what *are* the limits on <NV40 cards?
+		 */
+		dev_priv->ramin_rsvd_vram = (512 * 1024);
+	}
+	NV_DEBUG(dev, "RAMIN size: %dKiB\n", dev_priv->ramin_rsvd_vram >> 10);
+
+	/* Clear all of it, except the BIOS image that's in the first 64KiB */
+	dev_priv->engine.instmem.prepare_access(dev, true);
+	for (i = 64 * 1024; i < dev_priv->ramin_rsvd_vram; i += 4)
+		nv_wi32(dev, i, 0x00000000);
+	dev_priv->engine.instmem.finish_access(dev);
+}
+
+static void
+nv04_instmem_configure_fixed_tables(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_engine *engine = &dev_priv->engine;
+
+	/* FIFO hash table (RAMHT)
+	 *   use 4k hash table at RAMIN+0x10000
+	 *   TODO: extend the hash table
+	 */
+	dev_priv->ramht_offset = 0x10000;
+	dev_priv->ramht_bits   = 9;
+	dev_priv->ramht_size   = (1 << dev_priv->ramht_bits); /* nr entries */
+	dev_priv->ramht_size  *= 8; /* 2 32-bit values per entry in RAMHT */
+	NV_DEBUG(dev, "RAMHT offset=0x%x, size=%d\n", dev_priv->ramht_offset,
+						      dev_priv->ramht_size);
+
+	/* FIFO runout table (RAMRO) - 512k at 0x11200 */
+	dev_priv->ramro_offset = 0x11200;
+	dev_priv->ramro_size   = 512;
+	NV_DEBUG(dev, "RAMRO offset=0x%x, size=%d\n", dev_priv->ramro_offset,
+						      dev_priv->ramro_size);
+
+	/* FIFO context table (RAMFC)
+	 *   NV40  : Not sure exactly how to position RAMFC on some cards,
+	 *           0x30002 seems to position it at RAMIN+0x20000 on these
+	 *           cards.  RAMFC is 4kb (32 fifos, 128byte entries).
+	 *   Others: Position RAMFC at RAMIN+0x11400
+	 */
+	dev_priv->ramfc_size = engine->fifo.channels *
+						nouveau_fifo_ctx_size(dev);
+	switch (dev_priv->card_type) {
+	case NV_40:
+		dev_priv->ramfc_offset = 0x20000;
+		break;
+	case NV_30:
+	case NV_20:
+	case NV_10:
+	case NV_04:
+	default:
+		dev_priv->ramfc_offset = 0x11400;
+		break;
+	}
+	NV_DEBUG(dev, "RAMFC offset=0x%x, size=%d\n", dev_priv->ramfc_offset,
+						      dev_priv->ramfc_size);
+}
+
+int nv04_instmem_init(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	uint32_t offset;
+	int ret = 0;
+
+	nv04_instmem_determine_amount(dev);
+	nv04_instmem_configure_fixed_tables(dev);
+
+	/* Create a heap to manage RAMIN allocations, we don't allocate
+	 * the space that was reserved for RAMHT/FC/RO.
+	 */
+	offset = dev_priv->ramfc_offset + dev_priv->ramfc_size;
+
+	/* It appears RAMRO (or something?) is controlled by 0x2220/0x2230
+	 * on certain NV4x chipsets as well as RAMFC.  When 0x2230 == 0
+	 * ("new style" control) the upper 16-bits of 0x2220 points at this
+	 * other mysterious table that's clobbering important things.
+	 *
+	 * We're now pointing this at RAMIN+0x30000 to avoid RAMFC getting
+	 * smashed to pieces on us, so reserve 0x30000-0x40000 too..
+	 */
+	if (dev_priv->card_type >= NV_40) {
+		if (offset < 0x40000)
+			offset = 0x40000;
+	}
+
+	ret = nouveau_mem_init_heap(&dev_priv->ramin_heap,
+				    offset, dev_priv->ramin_rsvd_vram - offset);
+	if (ret) {
+		dev_priv->ramin_heap = NULL;
+		NV_ERROR(dev, "Failed to init RAMIN heap\n");
+	}
+
+	return ret;
+}
+
+void
+nv04_instmem_takedown(struct drm_device *dev)
+{
+}
+
+int
+nv04_instmem_populate(struct drm_device *dev, struct nouveau_gpuobj *gpuobj, uint32_t *sz)
+{
+	if (gpuobj->im_backing)
+		return -EINVAL;
+
+	return 0;
+}
+
+void
+nv04_instmem_clear(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	if (gpuobj && gpuobj->im_backing) {
+		if (gpuobj->im_bound)
+			dev_priv->engine.instmem.unbind(dev, gpuobj);
+		gpuobj->im_backing = NULL;
+	}
+}
+
+int
+nv04_instmem_bind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
+{
+	if (!gpuobj->im_pramin || gpuobj->im_bound)
+		return -EINVAL;
+
+	gpuobj->im_bound = 1;
+	return 0;
+}
+
+int
+nv04_instmem_unbind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
+{
+	if (gpuobj->im_bound == 0)
+		return -EINVAL;
+
+	gpuobj->im_bound = 0;
+	return 0;
+}
+
+void
+nv04_instmem_prepare_access(struct drm_device *dev, bool write)
+{
+}
+
+void
+nv04_instmem_finish_access(struct drm_device *dev)
+{
+}
+
+int
+nv04_instmem_suspend(struct drm_device *dev)
+{
+	return 0;
+}
+
+void
+nv04_instmem_resume(struct drm_device *dev)
+{
+}
+
diff --git a/drivers/gpu/drm/nouveau/nv04_mc.c b/drivers/gpu/drm/nouveau/nv04_mc.c
new file mode 100644
index 0000000..617ed1e
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv04_mc.c
@@ -0,0 +1,20 @@
+#include "drmP.h"
+#include "drm.h"
+#include "nouveau_drv.h"
+#include "nouveau_drm.h"
+
+int
+nv04_mc_init(struct drm_device *dev)
+{
+	/* Power up everything, resetting each individual unit will
+	 * be done later if needed.
+	 */
+
+	nv_wr32(dev, NV03_PMC_ENABLE, 0xFFFFFFFF);
+	return 0;
+}
+
+void
+nv04_mc_takedown(struct drm_device *dev)
+{
+}
diff --git a/drivers/gpu/drm/nouveau/nv04_timer.c b/drivers/gpu/drm/nouveau/nv04_timer.c
new file mode 100644
index 0000000..1d09ddd
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv04_timer.c
@@ -0,0 +1,51 @@
+#include "drmP.h"
+#include "drm.h"
+#include "nouveau_drv.h"
+#include "nouveau_drm.h"
+
+int
+nv04_timer_init(struct drm_device *dev)
+{
+	nv_wr32(dev, NV04_PTIMER_INTR_EN_0, 0x00000000);
+	nv_wr32(dev, NV04_PTIMER_INTR_0, 0xFFFFFFFF);
+
+	/* Just use the pre-existing values when possible for now; these regs
+	 * are not written in nv (driver writer missed a /4 on the address), and
+	 * writing 8 and 3 to the correct regs breaks the timings on the LVDS
+	 * hardware sequencing microcode.
+	 * A correct solution (involving calculations with the GPU PLL) can
+	 * be done when kernel modesetting lands
+	 */
+	if (!nv_rd32(dev, NV04_PTIMER_NUMERATOR) ||
+				!nv_rd32(dev, NV04_PTIMER_DENOMINATOR)) {
+		nv_wr32(dev, NV04_PTIMER_NUMERATOR, 0x00000008);
+		nv_wr32(dev, NV04_PTIMER_DENOMINATOR, 0x00000003);
+	}
+
+	return 0;
+}
+
+uint64_t
+nv04_timer_read(struct drm_device *dev)
+{
+	uint32_t low;
+	/* From kmmio dumps on nv28 this looks like how the blob does this.
+	 * It reads the high dword twice, before and after.
+	 * The only explanation seems to be that the 64-bit timer counter
+	 * advances between high and low dword reads and may corrupt the
+	 * result. Not confirmed.
+	 */
+	uint32_t high2 = nv_rd32(dev, NV04_PTIMER_TIME_1);
+	uint32_t high1;
+	do {
+		high1 = high2;
+		low = nv_rd32(dev, NV04_PTIMER_TIME_0);
+		high2 = nv_rd32(dev, NV04_PTIMER_TIME_1);
+	} while (high1 != high2);
+	return (((uint64_t)high2) << 32) | (uint64_t)low;
+}
+
+void
+nv04_timer_takedown(struct drm_device *dev)
+{
+}
diff --git a/drivers/gpu/drm/nouveau/nv04_tv.c b/drivers/gpu/drm/nouveau/nv04_tv.c
new file mode 100644
index 0000000..9c63099
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv04_tv.c
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2009 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "drmP.h"
+#include "nouveau_drv.h"
+#include "nouveau_encoder.h"
+#include "nouveau_connector.h"
+#include "nouveau_crtc.h"
+#include "nouveau_hw.h"
+#include "drm_crtc_helper.h"
+
+#include "i2c/ch7006.h"
+
+static struct {
+	struct i2c_board_info board_info;
+	struct drm_encoder_funcs funcs;
+	struct drm_encoder_helper_funcs hfuncs;
+	void *params;
+
+} nv04_tv_encoder_info[] = {
+	{
+		.board_info = { I2C_BOARD_INFO("ch7006", 0x75) },
+		.params = &(struct ch7006_encoder_params) {
+			CH7006_FORMAT_RGB24m12I, CH7006_CLOCK_MASTER,
+			0, 0, 0,
+			CH7006_SYNC_SLAVE, CH7006_SYNC_SEPARATED,
+			CH7006_POUT_3_3V, CH7006_ACTIVE_HSYNC
+		},
+	},
+};
+
+static bool probe_i2c_addr(struct i2c_adapter *adapter, int addr)
+{
+	struct i2c_msg msg = {
+		.addr = addr,
+		.len = 0,
+	};
+
+	return i2c_transfer(adapter, &msg, 1) == 1;
+}
+
+int nv04_tv_identify(struct drm_device *dev, int i2c_index)
+{
+	struct nouveau_i2c_chan *i2c;
+	bool was_locked;
+	int i, ret;
+
+	NV_TRACE(dev, "Probing TV encoders on I2C bus: %d\n", i2c_index);
+
+	i2c = nouveau_i2c_find(dev, i2c_index);
+	if (!i2c)
+		return -ENODEV;
+
+	was_locked = NVLockVgaCrtcs(dev, false);
+
+	for (i = 0; i < ARRAY_SIZE(nv04_tv_encoder_info); i++) {
+		if (probe_i2c_addr(&i2c->adapter,
+				   nv04_tv_encoder_info[i].board_info.addr)) {
+			ret = i;
+			break;
+		}
+	}
+
+	if (i < ARRAY_SIZE(nv04_tv_encoder_info)) {
+		NV_TRACE(dev, "Detected TV encoder: %s\n",
+			 nv04_tv_encoder_info[i].board_info.type);
+
+	} else {
+		NV_TRACE(dev, "No TV encoders found.\n");
+		i = -ENODEV;
+	}
+
+	NVLockVgaCrtcs(dev, was_locked);
+	return i;
+}
+
+#define PLLSEL_TV_CRTC1_MASK				\
+	(NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1		\
+	 | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1)
+#define PLLSEL_TV_CRTC2_MASK				\
+	(NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2		\
+	 | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2)
+
+static void nv04_tv_dpms(struct drm_encoder *encoder, int mode)
+{
+	struct drm_device *dev = encoder->dev;
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nv04_mode_state *state = &dev_priv->mode_reg;
+	uint8_t crtc1A;
+
+	NV_INFO(dev, "Setting dpms mode %d on TV encoder (output %d)\n",
+		mode, nv_encoder->dcb->index);
+
+	state->pllsel &= ~(PLLSEL_TV_CRTC1_MASK | PLLSEL_TV_CRTC2_MASK);
+
+	if (mode == DRM_MODE_DPMS_ON) {
+		int head = nouveau_crtc(encoder->crtc)->index;
+		crtc1A = NVReadVgaCrtc(dev, head, NV_CIO_CRE_RPC1_INDEX);
+
+		state->pllsel |= head ? PLLSEL_TV_CRTC2_MASK :
+					PLLSEL_TV_CRTC1_MASK;
+
+		/* Inhibit hsync */
+		crtc1A |= 0x80;
+
+		NVWriteVgaCrtc(dev, head, NV_CIO_CRE_RPC1_INDEX, crtc1A);
+	}
+
+	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT, state->pllsel);
+
+	to_encoder_slave(encoder)->slave_funcs->dpms(encoder, mode);
+}
+
+static void nv04_tv_bind(struct drm_device *dev, int head, bool bind)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nv04_crtc_reg *state = &dev_priv->mode_reg.crtc_reg[head];
+
+	state->tv_setup = 0;
+
+	if (bind) {
+		state->CRTC[NV_CIO_CRE_LCD__INDEX] = 0;
+		state->CRTC[NV_CIO_CRE_49] |= 0x10;
+	} else {
+		state->CRTC[NV_CIO_CRE_49] &= ~0x10;
+	}
+
+	NVWriteVgaCrtc(dev, head, NV_CIO_CRE_LCD__INDEX,
+		       state->CRTC[NV_CIO_CRE_LCD__INDEX]);
+	NVWriteVgaCrtc(dev, head, NV_CIO_CRE_49,
+		       state->CRTC[NV_CIO_CRE_49]);
+	NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP,
+		      state->tv_setup);
+}
+
+static void nv04_tv_prepare(struct drm_encoder *encoder)
+{
+	struct drm_device *dev = encoder->dev;
+	int head = nouveau_crtc(encoder->crtc)->index;
+	struct drm_encoder_helper_funcs *helper = encoder->helper_private;
+
+	helper->dpms(encoder, DRM_MODE_DPMS_OFF);
+
+	nv04_dfp_disable(dev, head);
+
+	if (nv_two_heads(dev))
+		nv04_tv_bind(dev, head ^ 1, false);
+
+	nv04_tv_bind(dev, head, true);
+}
+
+static void nv04_tv_mode_set(struct drm_encoder *encoder,
+			     struct drm_display_mode *mode,
+			     struct drm_display_mode *adjusted_mode)
+{
+	struct drm_device *dev = encoder->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
+	struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index];
+
+	regp->tv_htotal = adjusted_mode->htotal;
+	regp->tv_vtotal = adjusted_mode->vtotal;
+
+	/* These delay the TV signals with respect to the VGA port,
+	 * they might be useful if we ever allow a CRTC to drive
+	 * multiple outputs.
+	 */
+	regp->tv_hskew = 1;
+	regp->tv_hsync_delay = 1;
+	regp->tv_hsync_delay2 = 64;
+	regp->tv_vskew = 1;
+	regp->tv_vsync_delay = 1;
+
+	to_encoder_slave(encoder)->slave_funcs->mode_set(encoder, mode, adjusted_mode);
+}
+
+static void nv04_tv_commit(struct drm_encoder *encoder)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct drm_device *dev = encoder->dev;
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
+	struct drm_encoder_helper_funcs *helper = encoder->helper_private;
+
+	helper->dpms(encoder, DRM_MODE_DPMS_ON);
+
+	NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
+		      drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base), nv_crtc->index,
+		      '@' + ffs(nv_encoder->dcb->or));
+}
+
+static void nv04_tv_destroy(struct drm_encoder *encoder)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+
+	to_encoder_slave(encoder)->slave_funcs->destroy(encoder);
+
+	drm_encoder_cleanup(encoder);
+
+	kfree(nv_encoder);
+}
+
+int nv04_tv_create(struct drm_device *dev, struct dcb_entry *entry)
+{
+	struct nouveau_encoder *nv_encoder;
+	struct drm_encoder *encoder;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct i2c_adapter *adap;
+	struct drm_encoder_funcs *funcs = NULL;
+	struct drm_encoder_helper_funcs *hfuncs = NULL;
+	struct drm_encoder_slave_funcs *sfuncs = NULL;
+	int i2c_index = entry->i2c_index;
+	int type, ret;
+	bool was_locked;
+
+	/* Ensure that we can talk to this encoder */
+	type = nv04_tv_identify(dev, i2c_index);
+	if (type < 0)
+		return type;
+
+	/* Allocate the necessary memory */
+	nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
+	if (!nv_encoder)
+		return -ENOMEM;
+
+	/* Initialize the common members */
+	encoder = to_drm_encoder(nv_encoder);
+
+	funcs = &nv04_tv_encoder_info[type].funcs;
+	hfuncs = &nv04_tv_encoder_info[type].hfuncs;
+
+	drm_encoder_init(dev, encoder, funcs, DRM_MODE_ENCODER_TVDAC);
+	drm_encoder_helper_add(encoder, hfuncs);
+
+	encoder->possible_crtcs = entry->heads;
+	encoder->possible_clones = 0;
+
+	nv_encoder->dcb = entry;
+	nv_encoder->or = ffs(entry->or) - 1;
+
+	/* Run the slave-specific initialization */
+	adap = &dev_priv->vbios->dcb->i2c[i2c_index].chan->adapter;
+
+	was_locked = NVLockVgaCrtcs(dev, false);
+
+	ret = drm_i2c_encoder_init(encoder->dev, to_encoder_slave(encoder), adap,
+				   &nv04_tv_encoder_info[type].board_info);
+
+	NVLockVgaCrtcs(dev, was_locked);
+
+	if (ret < 0)
+		goto fail;
+
+	/* Fill the function pointers */
+	sfuncs = to_encoder_slave(encoder)->slave_funcs;
+
+	*funcs = (struct drm_encoder_funcs) {
+		.destroy = nv04_tv_destroy,
+	};
+
+	*hfuncs = (struct drm_encoder_helper_funcs) {
+		.dpms = nv04_tv_dpms,
+		.save = sfuncs->save,
+		.restore = sfuncs->restore,
+		.mode_fixup = sfuncs->mode_fixup,
+		.prepare = nv04_tv_prepare,
+		.commit = nv04_tv_commit,
+		.mode_set = nv04_tv_mode_set,
+		.detect = sfuncs->detect,
+	};
+
+	/* Set the slave encoder configuration */
+	sfuncs->set_config(encoder, nv04_tv_encoder_info[type].params);
+
+	return 0;
+
+fail:
+	drm_encoder_cleanup(encoder);
+
+	kfree(nv_encoder);
+	return ret;
+}
diff --git a/drivers/gpu/drm/nouveau/nv10_fb.c b/drivers/gpu/drm/nouveau/nv10_fb.c
new file mode 100644
index 0000000..79e2d10
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv10_fb.c
@@ -0,0 +1,24 @@
+#include "drmP.h"
+#include "drm.h"
+#include "nouveau_drv.h"
+#include "nouveau_drm.h"
+
+int
+nv10_fb_init(struct drm_device *dev)
+{
+	uint32_t fb_bar_size;
+	int i;
+
+	fb_bar_size = drm_get_resource_len(dev, 0) - 1;
+	for (i = 0; i < NV10_PFB_TILE__SIZE; i++) {
+		nv_wr32(dev, NV10_PFB_TILE(i), 0);
+		nv_wr32(dev, NV10_PFB_TLIMIT(i), fb_bar_size);
+	}
+
+	return 0;
+}
+
+void
+nv10_fb_takedown(struct drm_device *dev)
+{
+}
diff --git a/drivers/gpu/drm/nouveau/nv10_fifo.c b/drivers/gpu/drm/nouveau/nv10_fifo.c
new file mode 100644
index 0000000..7aeabf2
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv10_fifo.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2007 Ben Skeggs.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "drmP.h"
+#include "drm.h"
+#include "nouveau_drv.h"
+
+#define NV10_RAMFC(c) (dev_priv->ramfc_offset + ((c) * NV10_RAMFC__SIZE))
+#define NV10_RAMFC__SIZE ((dev_priv->chipset) >= 0x17 ? 64 : 32)
+
+int
+nv10_fifo_channel_id(struct drm_device *dev)
+{
+	return nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH1) &
+			NV10_PFIFO_CACHE1_PUSH1_CHID_MASK;
+}
+
+int
+nv10_fifo_create_context(struct nouveau_channel *chan)
+{
+	struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
+	struct drm_device *dev = chan->dev;
+	uint32_t fc = NV10_RAMFC(chan->id);
+	int ret;
+
+	ret = nouveau_gpuobj_new_fake(dev, NV10_RAMFC(chan->id), ~0,
+				      NV10_RAMFC__SIZE, NVOBJ_FLAG_ZERO_ALLOC |
+				      NVOBJ_FLAG_ZERO_FREE, NULL, &chan->ramfc);
+	if (ret)
+		return ret;
+
+	/* Fill entries that are seen filled in dumps of nvidia driver just
+	 * after channel's is put into DMA mode
+	 */
+	dev_priv->engine.instmem.prepare_access(dev, true);
+	nv_wi32(dev, fc +  0, chan->pushbuf_base);
+	nv_wi32(dev, fc +  4, chan->pushbuf_base);
+	nv_wi32(dev, fc + 12, chan->pushbuf->instance >> 4);
+	nv_wi32(dev, fc + 20, NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES |
+			      NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES |
+			      NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8 |
+#ifdef __BIG_ENDIAN
+			      NV_PFIFO_CACHE1_BIG_ENDIAN |
+#endif
+			      0);
+	dev_priv->engine.instmem.finish_access(dev);
+
+	/* enable the fifo dma operation */
+	nv_wr32(dev, NV04_PFIFO_MODE,
+		nv_rd32(dev, NV04_PFIFO_MODE) | (1 << chan->id));
+	return 0;
+}
+
+void
+nv10_fifo_destroy_context(struct nouveau_channel *chan)
+{
+	struct drm_device *dev = chan->dev;
+
+	nv_wr32(dev, NV04_PFIFO_MODE,
+			nv_rd32(dev, NV04_PFIFO_MODE) & ~(1 << chan->id));
+
+	nouveau_gpuobj_ref_del(dev, &chan->ramfc);
+}
+
+static void
+nv10_fifo_do_load_context(struct drm_device *dev, int chid)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	uint32_t fc = NV10_RAMFC(chid), tmp;
+
+	dev_priv->engine.instmem.prepare_access(dev, false);
+
+	nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUT, nv_ri32(dev, fc + 0));
+	nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_GET, nv_ri32(dev, fc + 4));
+	nv_wr32(dev, NV10_PFIFO_CACHE1_REF_CNT, nv_ri32(dev, fc + 8));
+
+	tmp = nv_ri32(dev, fc + 12);
+	nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_INSTANCE, tmp & 0xFFFF);
+	nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_DCOUNT, tmp >> 16);
+
+	nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_STATE, nv_ri32(dev, fc + 16));
+	nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_FETCH, nv_ri32(dev, fc + 20));
+	nv_wr32(dev, NV04_PFIFO_CACHE1_ENGINE, nv_ri32(dev, fc + 24));
+	nv_wr32(dev, NV04_PFIFO_CACHE1_PULL1, nv_ri32(dev, fc + 28));
+
+	if (dev_priv->chipset < 0x17)
+		goto out;
+
+	nv_wr32(dev, NV10_PFIFO_CACHE1_ACQUIRE_VALUE, nv_ri32(dev, fc + 32));
+	tmp = nv_ri32(dev, fc + 36);
+	nv_wr32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMESTAMP, tmp);
+	nv_wr32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMEOUT, nv_ri32(dev, fc + 40));
+	nv_wr32(dev, NV10_PFIFO_CACHE1_SEMAPHORE, nv_ri32(dev, fc + 44));
+	nv_wr32(dev, NV10_PFIFO_CACHE1_DMA_SUBROUTINE, nv_ri32(dev, fc + 48));
+
+out:
+	dev_priv->engine.instmem.finish_access(dev);
+
+	nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0);
+	nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, 0);
+}
+
+int
+nv10_fifo_load_context(struct nouveau_channel *chan)
+{
+	struct drm_device *dev = chan->dev;
+	uint32_t tmp;
+
+	nv10_fifo_do_load_context(dev, chan->id);
+
+	nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1,
+		     NV03_PFIFO_CACHE1_PUSH1_DMA | chan->id);
+	nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 1);
+
+	/* Reset NV04_PFIFO_CACHE1_DMA_CTL_AT_INFO to INVALID */
+	tmp = nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_CTL) & ~(1 << 31);
+	nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_CTL, tmp);
+
+	return 0;
+}
+
+int
+nv10_fifo_unload_context(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
+	uint32_t fc, tmp;
+	int chid;
+
+	chid = pfifo->channel_id(dev);
+	if (chid < 0 || chid >= dev_priv->engine.fifo.channels)
+		return 0;
+	fc = NV10_RAMFC(chid);
+
+	dev_priv->engine.instmem.prepare_access(dev, true);
+
+	nv_wi32(dev, fc +  0, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUT));
+	nv_wi32(dev, fc +  4, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET));
+	nv_wi32(dev, fc +  8, nv_rd32(dev, NV10_PFIFO_CACHE1_REF_CNT));
+	tmp  = nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_INSTANCE) & 0xFFFF;
+	tmp |= (nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_DCOUNT) << 16);
+	nv_wi32(dev, fc + 12, tmp);
+	nv_wi32(dev, fc + 16, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_STATE));
+	nv_wi32(dev, fc + 20, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_FETCH));
+	nv_wi32(dev, fc + 24, nv_rd32(dev, NV04_PFIFO_CACHE1_ENGINE));
+	nv_wi32(dev, fc + 28, nv_rd32(dev, NV04_PFIFO_CACHE1_PULL1));
+
+	if (dev_priv->chipset < 0x17)
+		goto out;
+
+	nv_wi32(dev, fc + 32, nv_rd32(dev, NV10_PFIFO_CACHE1_ACQUIRE_VALUE));
+	tmp = nv_rd32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMESTAMP);
+	nv_wi32(dev, fc + 36, tmp);
+	nv_wi32(dev, fc + 40, nv_rd32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMEOUT));
+	nv_wi32(dev, fc + 44, nv_rd32(dev, NV10_PFIFO_CACHE1_SEMAPHORE));
+	nv_wi32(dev, fc + 48, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET));
+
+out:
+	dev_priv->engine.instmem.finish_access(dev);
+
+	nv10_fifo_do_load_context(dev, pfifo->channels - 1);
+	nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, pfifo->channels - 1);
+	return 0;
+}
+
+static void
+nv10_fifo_init_reset(struct drm_device *dev)
+{
+	nv_wr32(dev, NV03_PMC_ENABLE,
+		nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PFIFO);
+	nv_wr32(dev, NV03_PMC_ENABLE,
+		nv_rd32(dev, NV03_PMC_ENABLE) |  NV_PMC_ENABLE_PFIFO);
+
+	nv_wr32(dev, 0x003224, 0x000f0078);
+	nv_wr32(dev, 0x002044, 0x0101ffff);
+	nv_wr32(dev, 0x002040, 0x000000ff);
+	nv_wr32(dev, 0x002500, 0x00000000);
+	nv_wr32(dev, 0x003000, 0x00000000);
+	nv_wr32(dev, 0x003050, 0x00000000);
+
+	nv_wr32(dev, 0x003258, 0x00000000);
+	nv_wr32(dev, 0x003210, 0x00000000);
+	nv_wr32(dev, 0x003270, 0x00000000);
+}
+
+static void
+nv10_fifo_init_ramxx(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	nv_wr32(dev, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ |
+				       ((dev_priv->ramht_bits - 9) << 16) |
+				       (dev_priv->ramht_offset >> 8));
+	nv_wr32(dev, NV03_PFIFO_RAMRO, dev_priv->ramro_offset>>8);
+
+	if (dev_priv->chipset < 0x17) {
+		nv_wr32(dev, NV03_PFIFO_RAMFC, dev_priv->ramfc_offset >> 8);
+	} else {
+		nv_wr32(dev, NV03_PFIFO_RAMFC, (dev_priv->ramfc_offset >> 8) |
+					       (1 << 16) /* 64 Bytes entry*/);
+		/* XXX nvidia blob set bit 18, 21,23 for nv20 & nv30 */
+	}
+}
+
+static void
+nv10_fifo_init_intr(struct drm_device *dev)
+{
+	nv_wr32(dev, 0x002100, 0xffffffff);
+	nv_wr32(dev, 0x002140, 0xffffffff);
+}
+
+int
+nv10_fifo_init(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
+	int i;
+
+	nv10_fifo_init_reset(dev);
+	nv10_fifo_init_ramxx(dev);
+
+	nv10_fifo_do_load_context(dev, pfifo->channels - 1);
+	nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, pfifo->channels - 1);
+
+	nv10_fifo_init_intr(dev);
+	pfifo->enable(dev);
+	pfifo->reassign(dev, true);
+
+	for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
+		if (dev_priv->fifos[i]) {
+			uint32_t mode = nv_rd32(dev, NV04_PFIFO_MODE);
+			nv_wr32(dev, NV04_PFIFO_MODE, mode | (1 << i));
+		}
+	}
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nv10_graph.c b/drivers/gpu/drm/nouveau/nv10_graph.c
new file mode 100644
index 0000000..6bf6804
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv10_graph.c
@@ -0,0 +1,892 @@
+/*
+ * Copyright 2007 Matthieu CASTET <castet.matthieu@free.fr>
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "drmP.h"
+#include "drm.h"
+#include "nouveau_drm.h"
+#include "nouveau_drv.h"
+
+#define NV10_FIFO_NUMBER 32
+
+struct pipe_state {
+	uint32_t pipe_0x0000[0x040/4];
+	uint32_t pipe_0x0040[0x010/4];
+	uint32_t pipe_0x0200[0x0c0/4];
+	uint32_t pipe_0x4400[0x080/4];
+	uint32_t pipe_0x6400[0x3b0/4];
+	uint32_t pipe_0x6800[0x2f0/4];
+	uint32_t pipe_0x6c00[0x030/4];
+	uint32_t pipe_0x7000[0x130/4];
+	uint32_t pipe_0x7400[0x0c0/4];
+	uint32_t pipe_0x7800[0x0c0/4];
+};
+
+static int nv10_graph_ctx_regs[] = {
+	NV10_PGRAPH_CTX_SWITCH1,
+	NV10_PGRAPH_CTX_SWITCH2,
+	NV10_PGRAPH_CTX_SWITCH3,
+	NV10_PGRAPH_CTX_SWITCH4,
+	NV10_PGRAPH_CTX_SWITCH5,
+	NV10_PGRAPH_CTX_CACHE1,	/* 8 values from 0x400160 to 0x40017c */
+	NV10_PGRAPH_CTX_CACHE2,	/* 8 values from 0x400180 to 0x40019c */
+	NV10_PGRAPH_CTX_CACHE3,	/* 8 values from 0x4001a0 to 0x4001bc */
+	NV10_PGRAPH_CTX_CACHE4,	/* 8 values from 0x4001c0 to 0x4001dc */
+	NV10_PGRAPH_CTX_CACHE5,	/* 8 values from 0x4001e0 to 0x4001fc */
+	0x00400164,
+	0x00400184,
+	0x004001a4,
+	0x004001c4,
+	0x004001e4,
+	0x00400168,
+	0x00400188,
+	0x004001a8,
+	0x004001c8,
+	0x004001e8,
+	0x0040016c,
+	0x0040018c,
+	0x004001ac,
+	0x004001cc,
+	0x004001ec,
+	0x00400170,
+	0x00400190,
+	0x004001b0,
+	0x004001d0,
+	0x004001f0,
+	0x00400174,
+	0x00400194,
+	0x004001b4,
+	0x004001d4,
+	0x004001f4,
+	0x00400178,
+	0x00400198,
+	0x004001b8,
+	0x004001d8,
+	0x004001f8,
+	0x0040017c,
+	0x0040019c,
+	0x004001bc,
+	0x004001dc,
+	0x004001fc,
+	NV10_PGRAPH_CTX_USER,
+	NV04_PGRAPH_DMA_START_0,
+	NV04_PGRAPH_DMA_START_1,
+	NV04_PGRAPH_DMA_LENGTH,
+	NV04_PGRAPH_DMA_MISC,
+	NV10_PGRAPH_DMA_PITCH,
+	NV04_PGRAPH_BOFFSET0,
+	NV04_PGRAPH_BBASE0,
+	NV04_PGRAPH_BLIMIT0,
+	NV04_PGRAPH_BOFFSET1,
+	NV04_PGRAPH_BBASE1,
+	NV04_PGRAPH_BLIMIT1,
+	NV04_PGRAPH_BOFFSET2,
+	NV04_PGRAPH_BBASE2,
+	NV04_PGRAPH_BLIMIT2,
+	NV04_PGRAPH_BOFFSET3,
+	NV04_PGRAPH_BBASE3,
+	NV04_PGRAPH_BLIMIT3,
+	NV04_PGRAPH_BOFFSET4,
+	NV04_PGRAPH_BBASE4,
+	NV04_PGRAPH_BLIMIT4,
+	NV04_PGRAPH_BOFFSET5,
+	NV04_PGRAPH_BBASE5,
+	NV04_PGRAPH_BLIMIT5,
+	NV04_PGRAPH_BPITCH0,
+	NV04_PGRAPH_BPITCH1,
+	NV04_PGRAPH_BPITCH2,
+	NV04_PGRAPH_BPITCH3,
+	NV04_PGRAPH_BPITCH4,
+	NV10_PGRAPH_SURFACE,
+	NV10_PGRAPH_STATE,
+	NV04_PGRAPH_BSWIZZLE2,
+	NV04_PGRAPH_BSWIZZLE5,
+	NV04_PGRAPH_BPIXEL,
+	NV10_PGRAPH_NOTIFY,
+	NV04_PGRAPH_PATT_COLOR0,
+	NV04_PGRAPH_PATT_COLOR1,
+	NV04_PGRAPH_PATT_COLORRAM, /* 64 values from 0x400900 to 0x4009fc */
+	0x00400904,
+	0x00400908,
+	0x0040090c,
+	0x00400910,
+	0x00400914,
+	0x00400918,
+	0x0040091c,
+	0x00400920,
+	0x00400924,
+	0x00400928,
+	0x0040092c,
+	0x00400930,
+	0x00400934,
+	0x00400938,
+	0x0040093c,
+	0x00400940,
+	0x00400944,
+	0x00400948,
+	0x0040094c,
+	0x00400950,
+	0x00400954,
+	0x00400958,
+	0x0040095c,
+	0x00400960,
+	0x00400964,
+	0x00400968,
+	0x0040096c,
+	0x00400970,
+	0x00400974,
+	0x00400978,
+	0x0040097c,
+	0x00400980,
+	0x00400984,
+	0x00400988,
+	0x0040098c,
+	0x00400990,
+	0x00400994,
+	0x00400998,
+	0x0040099c,
+	0x004009a0,
+	0x004009a4,
+	0x004009a8,
+	0x004009ac,
+	0x004009b0,
+	0x004009b4,
+	0x004009b8,
+	0x004009bc,
+	0x004009c0,
+	0x004009c4,
+	0x004009c8,
+	0x004009cc,
+	0x004009d0,
+	0x004009d4,
+	0x004009d8,
+	0x004009dc,
+	0x004009e0,
+	0x004009e4,
+	0x004009e8,
+	0x004009ec,
+	0x004009f0,
+	0x004009f4,
+	0x004009f8,
+	0x004009fc,
+	NV04_PGRAPH_PATTERN,	/* 2 values from 0x400808 to 0x40080c */
+	0x0040080c,
+	NV04_PGRAPH_PATTERN_SHAPE,
+	NV03_PGRAPH_MONO_COLOR0,
+	NV04_PGRAPH_ROP3,
+	NV04_PGRAPH_CHROMA,
+	NV04_PGRAPH_BETA_AND,
+	NV04_PGRAPH_BETA_PREMULT,
+	0x00400e70,
+	0x00400e74,
+	0x00400e78,
+	0x00400e7c,
+	0x00400e80,
+	0x00400e84,
+	0x00400e88,
+	0x00400e8c,
+	0x00400ea0,
+	0x00400ea4,
+	0x00400ea8,
+	0x00400e90,
+	0x00400e94,
+	0x00400e98,
+	0x00400e9c,
+	NV10_PGRAPH_WINDOWCLIP_HORIZONTAL, /* 8 values from 0x400f00-0x400f1c */
+	NV10_PGRAPH_WINDOWCLIP_VERTICAL,   /* 8 values from 0x400f20-0x400f3c */
+	0x00400f04,
+	0x00400f24,
+	0x00400f08,
+	0x00400f28,
+	0x00400f0c,
+	0x00400f2c,
+	0x00400f10,
+	0x00400f30,
+	0x00400f14,
+	0x00400f34,
+	0x00400f18,
+	0x00400f38,
+	0x00400f1c,
+	0x00400f3c,
+	NV10_PGRAPH_XFMODE0,
+	NV10_PGRAPH_XFMODE1,
+	NV10_PGRAPH_GLOBALSTATE0,
+	NV10_PGRAPH_GLOBALSTATE1,
+	NV04_PGRAPH_STORED_FMT,
+	NV04_PGRAPH_SOURCE_COLOR,
+	NV03_PGRAPH_ABS_X_RAM,	/* 32 values from 0x400400 to 0x40047c */
+	NV03_PGRAPH_ABS_Y_RAM,	/* 32 values from 0x400480 to 0x4004fc */
+	0x00400404,
+	0x00400484,
+	0x00400408,
+	0x00400488,
+	0x0040040c,
+	0x0040048c,
+	0x00400410,
+	0x00400490,
+	0x00400414,
+	0x00400494,
+	0x00400418,
+	0x00400498,
+	0x0040041c,
+	0x0040049c,
+	0x00400420,
+	0x004004a0,
+	0x00400424,
+	0x004004a4,
+	0x00400428,
+	0x004004a8,
+	0x0040042c,
+	0x004004ac,
+	0x00400430,
+	0x004004b0,
+	0x00400434,
+	0x004004b4,
+	0x00400438,
+	0x004004b8,
+	0x0040043c,
+	0x004004bc,
+	0x00400440,
+	0x004004c0,
+	0x00400444,
+	0x004004c4,
+	0x00400448,
+	0x004004c8,
+	0x0040044c,
+	0x004004cc,
+	0x00400450,
+	0x004004d0,
+	0x00400454,
+	0x004004d4,
+	0x00400458,
+	0x004004d8,
+	0x0040045c,
+	0x004004dc,
+	0x00400460,
+	0x004004e0,
+	0x00400464,
+	0x004004e4,
+	0x00400468,
+	0x004004e8,
+	0x0040046c,
+	0x004004ec,
+	0x00400470,
+	0x004004f0,
+	0x00400474,
+	0x004004f4,
+	0x00400478,
+	0x004004f8,
+	0x0040047c,
+	0x004004fc,
+	NV03_PGRAPH_ABS_UCLIP_XMIN,
+	NV03_PGRAPH_ABS_UCLIP_XMAX,
+	NV03_PGRAPH_ABS_UCLIP_YMIN,
+	NV03_PGRAPH_ABS_UCLIP_YMAX,
+	0x00400550,
+	0x00400558,
+	0x00400554,
+	0x0040055c,
+	NV03_PGRAPH_ABS_UCLIPA_XMIN,
+	NV03_PGRAPH_ABS_UCLIPA_XMAX,
+	NV03_PGRAPH_ABS_UCLIPA_YMIN,
+	NV03_PGRAPH_ABS_UCLIPA_YMAX,
+	NV03_PGRAPH_ABS_ICLIP_XMAX,
+	NV03_PGRAPH_ABS_ICLIP_YMAX,
+	NV03_PGRAPH_XY_LOGIC_MISC0,
+	NV03_PGRAPH_XY_LOGIC_MISC1,
+	NV03_PGRAPH_XY_LOGIC_MISC2,
+	NV03_PGRAPH_XY_LOGIC_MISC3,
+	NV03_PGRAPH_CLIPX_0,
+	NV03_PGRAPH_CLIPX_1,
+	NV03_PGRAPH_CLIPY_0,
+	NV03_PGRAPH_CLIPY_1,
+	NV10_PGRAPH_COMBINER0_IN_ALPHA,
+	NV10_PGRAPH_COMBINER1_IN_ALPHA,
+	NV10_PGRAPH_COMBINER0_IN_RGB,
+	NV10_PGRAPH_COMBINER1_IN_RGB,
+	NV10_PGRAPH_COMBINER_COLOR0,
+	NV10_PGRAPH_COMBINER_COLOR1,
+	NV10_PGRAPH_COMBINER0_OUT_ALPHA,
+	NV10_PGRAPH_COMBINER1_OUT_ALPHA,
+	NV10_PGRAPH_COMBINER0_OUT_RGB,
+	NV10_PGRAPH_COMBINER1_OUT_RGB,
+	NV10_PGRAPH_COMBINER_FINAL0,
+	NV10_PGRAPH_COMBINER_FINAL1,
+	0x00400e00,
+	0x00400e04,
+	0x00400e08,
+	0x00400e0c,
+	0x00400e10,
+	0x00400e14,
+	0x00400e18,
+	0x00400e1c,
+	0x00400e20,
+	0x00400e24,
+	0x00400e28,
+	0x00400e2c,
+	0x00400e30,
+	0x00400e34,
+	0x00400e38,
+	0x00400e3c,
+	NV04_PGRAPH_PASSTHRU_0,
+	NV04_PGRAPH_PASSTHRU_1,
+	NV04_PGRAPH_PASSTHRU_2,
+	NV10_PGRAPH_DIMX_TEXTURE,
+	NV10_PGRAPH_WDIMX_TEXTURE,
+	NV10_PGRAPH_DVD_COLORFMT,
+	NV10_PGRAPH_SCALED_FORMAT,
+	NV04_PGRAPH_MISC24_0,
+	NV04_PGRAPH_MISC24_1,
+	NV04_PGRAPH_MISC24_2,
+	NV03_PGRAPH_X_MISC,
+	NV03_PGRAPH_Y_MISC,
+	NV04_PGRAPH_VALID1,
+	NV04_PGRAPH_VALID2,
+};
+
+static int nv17_graph_ctx_regs[] = {
+	NV10_PGRAPH_DEBUG_4,
+	0x004006b0,
+	0x00400eac,
+	0x00400eb0,
+	0x00400eb4,
+	0x00400eb8,
+	0x00400ebc,
+	0x00400ec0,
+	0x00400ec4,
+	0x00400ec8,
+	0x00400ecc,
+	0x00400ed0,
+	0x00400ed4,
+	0x00400ed8,
+	0x00400edc,
+	0x00400ee0,
+	0x00400a00,
+	0x00400a04,
+};
+
+struct graph_state {
+	int nv10[ARRAY_SIZE(nv10_graph_ctx_regs)];
+	int nv17[ARRAY_SIZE(nv17_graph_ctx_regs)];
+	struct pipe_state pipe_state;
+};
+
+static void nv10_graph_save_pipe(struct nouveau_channel *chan)
+{
+	struct drm_device *dev = chan->dev;
+	struct graph_state *pgraph_ctx = chan->pgraph_ctx;
+	struct pipe_state *fifo_pipe_state = &pgraph_ctx->pipe_state;
+	int i;
+#define PIPE_SAVE(addr) \
+	do { \
+		nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, addr); \
+		for (i = 0; i < ARRAY_SIZE(fifo_pipe_state->pipe_##addr); i++) \
+			fifo_pipe_state->pipe_##addr[i] = nv_rd32(dev, NV10_PGRAPH_PIPE_DATA); \
+	} while (0)
+
+	PIPE_SAVE(0x4400);
+	PIPE_SAVE(0x0200);
+	PIPE_SAVE(0x6400);
+	PIPE_SAVE(0x6800);
+	PIPE_SAVE(0x6c00);
+	PIPE_SAVE(0x7000);
+	PIPE_SAVE(0x7400);
+	PIPE_SAVE(0x7800);
+	PIPE_SAVE(0x0040);
+	PIPE_SAVE(0x0000);
+
+#undef PIPE_SAVE
+}
+
+static void nv10_graph_load_pipe(struct nouveau_channel *chan)
+{
+	struct drm_device *dev = chan->dev;
+	struct graph_state *pgraph_ctx = chan->pgraph_ctx;
+	struct pipe_state *fifo_pipe_state = &pgraph_ctx->pipe_state;
+	int i;
+	uint32_t xfmode0, xfmode1;
+#define PIPE_RESTORE(addr) \
+	do { \
+		nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, addr); \
+		for (i = 0; i < ARRAY_SIZE(fifo_pipe_state->pipe_##addr); i++) \
+			nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, fifo_pipe_state->pipe_##addr[i]); \
+	} while (0)
+
+
+	nouveau_wait_for_idle(dev);
+	/* XXX check haiku comments */
+	xfmode0 = nv_rd32(dev, NV10_PGRAPH_XFMODE0);
+	xfmode1 = nv_rd32(dev, NV10_PGRAPH_XFMODE1);
+	nv_wr32(dev, NV10_PGRAPH_XFMODE0, 0x10000000);
+	nv_wr32(dev, NV10_PGRAPH_XFMODE1, 0x00000000);
+	nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, 0x000064c0);
+	for (i = 0; i < 4; i++)
+		nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x3f800000);
+	for (i = 0; i < 4; i++)
+		nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x00000000);
+
+	nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, 0x00006ab0);
+	for (i = 0; i < 3; i++)
+		nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x3f800000);
+
+	nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, 0x00006a80);
+	for (i = 0; i < 3; i++)
+		nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x00000000);
+
+	nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, 0x00000040);
+	nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x00000008);
+
+
+	PIPE_RESTORE(0x0200);
+	nouveau_wait_for_idle(dev);
+
+	/* restore XFMODE */
+	nv_wr32(dev, NV10_PGRAPH_XFMODE0, xfmode0);
+	nv_wr32(dev, NV10_PGRAPH_XFMODE1, xfmode1);
+	PIPE_RESTORE(0x6400);
+	PIPE_RESTORE(0x6800);
+	PIPE_RESTORE(0x6c00);
+	PIPE_RESTORE(0x7000);
+	PIPE_RESTORE(0x7400);
+	PIPE_RESTORE(0x7800);
+	PIPE_RESTORE(0x4400);
+	PIPE_RESTORE(0x0000);
+	PIPE_RESTORE(0x0040);
+	nouveau_wait_for_idle(dev);
+
+#undef PIPE_RESTORE
+}
+
+static void nv10_graph_create_pipe(struct nouveau_channel *chan)
+{
+	struct drm_device *dev = chan->dev;
+	struct graph_state *pgraph_ctx = chan->pgraph_ctx;
+	struct pipe_state *fifo_pipe_state = &pgraph_ctx->pipe_state;
+	uint32_t *fifo_pipe_state_addr;
+	int i;
+#define PIPE_INIT(addr) \
+	do { \
+		fifo_pipe_state_addr = fifo_pipe_state->pipe_##addr; \
+	} while (0)
+#define PIPE_INIT_END(addr) \
+	do { \
+		uint32_t *__end_addr = fifo_pipe_state->pipe_##addr + \
+				ARRAY_SIZE(fifo_pipe_state->pipe_##addr); \
+		if (fifo_pipe_state_addr != __end_addr) \
+			NV_ERROR(dev, "incomplete pipe init for 0x%x :  %p/%p\n", \
+				addr, fifo_pipe_state_addr, __end_addr); \
+	} while (0)
+#define NV_WRITE_PIPE_INIT(value) *(fifo_pipe_state_addr++) = value
+
+	PIPE_INIT(0x0200);
+	for (i = 0; i < 48; i++)
+		NV_WRITE_PIPE_INIT(0x00000000);
+	PIPE_INIT_END(0x0200);
+
+	PIPE_INIT(0x6400);
+	for (i = 0; i < 211; i++)
+		NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x3f800000);
+	NV_WRITE_PIPE_INIT(0x40000000);
+	NV_WRITE_PIPE_INIT(0x40000000);
+	NV_WRITE_PIPE_INIT(0x40000000);
+	NV_WRITE_PIPE_INIT(0x40000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x3f800000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x3f000000);
+	NV_WRITE_PIPE_INIT(0x3f000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x3f800000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x3f800000);
+	NV_WRITE_PIPE_INIT(0x3f800000);
+	NV_WRITE_PIPE_INIT(0x3f800000);
+	NV_WRITE_PIPE_INIT(0x3f800000);
+	PIPE_INIT_END(0x6400);
+
+	PIPE_INIT(0x6800);
+	for (i = 0; i < 162; i++)
+		NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x3f800000);
+	for (i = 0; i < 25; i++)
+		NV_WRITE_PIPE_INIT(0x00000000);
+	PIPE_INIT_END(0x6800);
+
+	PIPE_INIT(0x6c00);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0xbf800000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	PIPE_INIT_END(0x6c00);
+
+	PIPE_INIT(0x7000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x7149f2ca);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x7149f2ca);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x7149f2ca);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x7149f2ca);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x7149f2ca);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x7149f2ca);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x7149f2ca);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x00000000);
+	NV_WRITE_PIPE_INIT(0x7149f2ca);
+	for (i = 0; i < 35; i++)
+		NV_WRITE_PIPE_INIT(0x00000000);
+	PIPE_INIT_END(0x7000);
+
+	PIPE_INIT(0x7400);
+	for (i = 0; i < 48; i++)
+		NV_WRITE_PIPE_INIT(0x00000000);
+	PIPE_INIT_END(0x7400);
+
+	PIPE_INIT(0x7800);
+	for (i = 0; i < 48; i++)
+		NV_WRITE_PIPE_INIT(0x00000000);
+	PIPE_INIT_END(0x7800);
+
+	PIPE_INIT(0x4400);
+	for (i = 0; i < 32; i++)
+		NV_WRITE_PIPE_INIT(0x00000000);
+	PIPE_INIT_END(0x4400);
+
+	PIPE_INIT(0x0000);
+	for (i = 0; i < 16; i++)
+		NV_WRITE_PIPE_INIT(0x00000000);
+	PIPE_INIT_END(0x0000);
+
+	PIPE_INIT(0x0040);
+	for (i = 0; i < 4; i++)
+		NV_WRITE_PIPE_INIT(0x00000000);
+	PIPE_INIT_END(0x0040);
+
+#undef PIPE_INIT
+#undef PIPE_INIT_END
+#undef NV_WRITE_PIPE_INIT
+}
+
+static int nv10_graph_ctx_regs_find_offset(struct drm_device *dev, int reg)
+{
+	int i;
+	for (i = 0; i < ARRAY_SIZE(nv10_graph_ctx_regs); i++) {
+		if (nv10_graph_ctx_regs[i] == reg)
+			return i;
+	}
+	NV_ERROR(dev, "unknow offset nv10_ctx_regs %d\n", reg);
+	return -1;
+}
+
+static int nv17_graph_ctx_regs_find_offset(struct drm_device *dev, int reg)
+{
+	int i;
+	for (i = 0; i < ARRAY_SIZE(nv17_graph_ctx_regs); i++) {
+		if (nv17_graph_ctx_regs[i] == reg)
+			return i;
+	}
+	NV_ERROR(dev, "unknow offset nv17_ctx_regs %d\n", reg);
+	return -1;
+}
+
+int nv10_graph_load_context(struct nouveau_channel *chan)
+{
+	struct drm_device *dev = chan->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct graph_state *pgraph_ctx = chan->pgraph_ctx;
+	uint32_t tmp;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(nv10_graph_ctx_regs); i++)
+		nv_wr32(dev, nv10_graph_ctx_regs[i], pgraph_ctx->nv10[i]);
+	if (dev_priv->chipset >= 0x17) {
+		for (i = 0; i < ARRAY_SIZE(nv17_graph_ctx_regs); i++)
+			nv_wr32(dev, nv17_graph_ctx_regs[i],
+						pgraph_ctx->nv17[i]);
+	}
+
+	nv10_graph_load_pipe(chan);
+
+	nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10010100);
+	tmp = nv_rd32(dev, NV10_PGRAPH_CTX_USER);
+	nv_wr32(dev, NV10_PGRAPH_CTX_USER, (tmp & 0xffffff) | chan->id << 24);
+	tmp = nv_rd32(dev, NV10_PGRAPH_FFINTFC_ST2);
+	nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2, tmp & 0xcfffffff);
+	return 0;
+}
+
+int
+nv10_graph_unload_context(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
+	struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
+	struct nouveau_channel *chan;
+	struct graph_state *ctx;
+	uint32_t tmp;
+	int i;
+
+	chan = pgraph->channel(dev);
+	if (!chan)
+		return 0;
+	ctx = chan->pgraph_ctx;
+
+	for (i = 0; i < ARRAY_SIZE(nv10_graph_ctx_regs); i++)
+		ctx->nv10[i] = nv_rd32(dev, nv10_graph_ctx_regs[i]);
+
+	if (dev_priv->chipset >= 0x17) {
+		for (i = 0; i < ARRAY_SIZE(nv17_graph_ctx_regs); i++)
+			ctx->nv17[i] = nv_rd32(dev, nv17_graph_ctx_regs[i]);
+	}
+
+	nv10_graph_save_pipe(chan);
+
+	nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000000);
+	tmp  = nv_rd32(dev, NV10_PGRAPH_CTX_USER) & 0x00ffffff;
+	tmp |= (pfifo->channels - 1) << 24;
+	nv_wr32(dev, NV10_PGRAPH_CTX_USER, tmp);
+	return 0;
+}
+
+void
+nv10_graph_context_switch(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
+	struct nouveau_channel *chan = NULL;
+	int chid;
+
+	pgraph->fifo_access(dev, false);
+	nouveau_wait_for_idle(dev);
+
+	/* If previous context is valid, we need to save it */
+	nv10_graph_unload_context(dev);
+
+	/* Load context for next channel */
+	chid = (nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR) >> 20) & 0x1f;
+	chan = dev_priv->fifos[chid];
+	if (chan)
+		nv10_graph_load_context(chan);
+
+	pgraph->fifo_access(dev, true);
+}
+
+#define NV_WRITE_CTX(reg, val) do { \
+	int offset = nv10_graph_ctx_regs_find_offset(dev, reg); \
+	if (offset > 0) \
+		pgraph_ctx->nv10[offset] = val; \
+	} while (0)
+
+#define NV17_WRITE_CTX(reg, val) do { \
+	int offset = nv17_graph_ctx_regs_find_offset(dev, reg); \
+	if (offset > 0) \
+		pgraph_ctx->nv17[offset] = val; \
+	} while (0)
+
+struct nouveau_channel *
+nv10_graph_channel(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	int chid = dev_priv->engine.fifo.channels;
+
+	if (nv_rd32(dev, NV10_PGRAPH_CTX_CONTROL) & 0x00010000)
+		chid = nv_rd32(dev, NV10_PGRAPH_CTX_USER) >> 24;
+
+	if (chid >= dev_priv->engine.fifo.channels)
+		return NULL;
+
+	return dev_priv->fifos[chid];
+}
+
+int nv10_graph_create_context(struct nouveau_channel *chan)
+{
+	struct drm_device *dev = chan->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct graph_state *pgraph_ctx;
+
+	NV_DEBUG(dev, "nv10_graph_context_create %d\n", chan->id);
+
+	chan->pgraph_ctx = pgraph_ctx = kzalloc(sizeof(*pgraph_ctx),
+						GFP_KERNEL);
+	if (pgraph_ctx == NULL)
+		return -ENOMEM;
+
+
+	NV_WRITE_CTX(0x00400e88, 0x08000000);
+	NV_WRITE_CTX(0x00400e9c, 0x4b7fffff);
+	NV_WRITE_CTX(NV03_PGRAPH_XY_LOGIC_MISC0, 0x0001ffff);
+	NV_WRITE_CTX(0x00400e10, 0x00001000);
+	NV_WRITE_CTX(0x00400e14, 0x00001000);
+	NV_WRITE_CTX(0x00400e30, 0x00080008);
+	NV_WRITE_CTX(0x00400e34, 0x00080008);
+	if (dev_priv->chipset >= 0x17) {
+		/* is it really needed ??? */
+		NV17_WRITE_CTX(NV10_PGRAPH_DEBUG_4,
+					nv_rd32(dev, NV10_PGRAPH_DEBUG_4));
+		NV17_WRITE_CTX(0x004006b0, nv_rd32(dev, 0x004006b0));
+		NV17_WRITE_CTX(0x00400eac, 0x0fff0000);
+		NV17_WRITE_CTX(0x00400eb0, 0x0fff0000);
+		NV17_WRITE_CTX(0x00400ec0, 0x00000080);
+		NV17_WRITE_CTX(0x00400ed0, 0x00000080);
+	}
+	NV_WRITE_CTX(NV10_PGRAPH_CTX_USER, chan->id << 24);
+
+	nv10_graph_create_pipe(chan);
+	return 0;
+}
+
+void nv10_graph_destroy_context(struct nouveau_channel *chan)
+{
+	struct graph_state *pgraph_ctx = chan->pgraph_ctx;
+
+	kfree(pgraph_ctx);
+	chan->pgraph_ctx = NULL;
+}
+
+int nv10_graph_init(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	uint32_t tmp;
+	int i;
+
+	nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) &
+			~NV_PMC_ENABLE_PGRAPH);
+	nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) |
+			 NV_PMC_ENABLE_PGRAPH);
+
+	nv_wr32(dev, NV03_PGRAPH_INTR   , 0xFFFFFFFF);
+	nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF);
+
+	nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0xFFFFFFFF);
+	nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x00000000);
+	nv_wr32(dev, NV04_PGRAPH_DEBUG_1, 0x00118700);
+	/* nv_wr32(dev, NV04_PGRAPH_DEBUG_2, 0x24E00810); */ /* 0x25f92ad9 */
+	nv_wr32(dev, NV04_PGRAPH_DEBUG_2, 0x25f92ad9);
+	nv_wr32(dev, NV04_PGRAPH_DEBUG_3, 0x55DE0830 |
+				      (1<<29) |
+				      (1<<31));
+	if (dev_priv->chipset >= 0x17) {
+		nv_wr32(dev, NV10_PGRAPH_DEBUG_4, 0x1f000000);
+		nv_wr32(dev, 0x004006b0, 0x40000020);
+	} else
+		nv_wr32(dev, NV10_PGRAPH_DEBUG_4, 0x00000000);
+
+	/* copy tile info from PFB */
+	for (i = 0; i < NV10_PFB_TILE__SIZE; i++) {
+		nv_wr32(dev, NV10_PGRAPH_TILE(i),
+					nv_rd32(dev, NV10_PFB_TILE(i)));
+		nv_wr32(dev, NV10_PGRAPH_TLIMIT(i),
+					nv_rd32(dev, NV10_PFB_TLIMIT(i)));
+		nv_wr32(dev, NV10_PGRAPH_TSIZE(i),
+					nv_rd32(dev, NV10_PFB_TSIZE(i)));
+		nv_wr32(dev, NV10_PGRAPH_TSTATUS(i),
+					nv_rd32(dev, NV10_PFB_TSTATUS(i)));
+	}
+
+	nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH1, 0x00000000);
+	nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH2, 0x00000000);
+	nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH3, 0x00000000);
+	nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH4, 0x00000000);
+	nv_wr32(dev, NV10_PGRAPH_STATE      , 0xFFFFFFFF);
+
+	tmp  = nv_rd32(dev, NV10_PGRAPH_CTX_USER) & 0x00ffffff;
+	tmp |= (dev_priv->engine.fifo.channels - 1) << 24;
+	nv_wr32(dev, NV10_PGRAPH_CTX_USER, tmp);
+	nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000100);
+	nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2, 0x08000000);
+
+	return 0;
+}
+
+void nv10_graph_takedown(struct drm_device *dev)
+{
+}
+
+struct nouveau_pgraph_object_class nv10_graph_grclass[] = {
+	{ 0x0030, false, NULL }, /* null */
+	{ 0x0039, false, NULL }, /* m2mf */
+	{ 0x004a, false, NULL }, /* gdirect */
+	{ 0x005f, false, NULL }, /* imageblit */
+	{ 0x009f, false, NULL }, /* imageblit (nv12) */
+	{ 0x008a, false, NULL }, /* ifc */
+	{ 0x0089, false, NULL }, /* sifm */
+	{ 0x0062, false, NULL }, /* surf2d */
+	{ 0x0043, false, NULL }, /* rop */
+	{ 0x0012, false, NULL }, /* beta1 */
+	{ 0x0072, false, NULL }, /* beta4 */
+	{ 0x0019, false, NULL }, /* cliprect */
+	{ 0x0044, false, NULL }, /* pattern */
+	{ 0x0052, false, NULL }, /* swzsurf */
+	{ 0x0093, false, NULL }, /* surf3d */
+	{ 0x0094, false, NULL }, /* tex_tri */
+	{ 0x0095, false, NULL }, /* multitex_tri */
+	{ 0x0056, false, NULL }, /* celcius (nv10) */
+	{ 0x0096, false, NULL }, /* celcius (nv11) */
+	{ 0x0099, false, NULL }, /* celcius (nv17) */
+	{}
+};
diff --git a/drivers/gpu/drm/nouveau/nv17_gpio.c b/drivers/gpu/drm/nouveau/nv17_gpio.c
new file mode 100644
index 0000000..2e58c331
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv17_gpio.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2009 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "drmP.h"
+#include "nouveau_drv.h"
+#include "nouveau_hw.h"
+
+static bool
+get_gpio_location(struct dcb_gpio_entry *ent, uint32_t *reg, uint32_t *shift,
+		  uint32_t *mask)
+{
+	if (ent->line < 2) {
+		*reg = NV_PCRTC_GPIO;
+		*shift = ent->line * 16;
+		*mask = 0x11;
+
+	} else if (ent->line < 10) {
+		*reg = NV_PCRTC_GPIO_EXT;
+		*shift = (ent->line - 2) * 4;
+		*mask = 0x3;
+
+	} else if (ent->line < 14) {
+		*reg = NV_PCRTC_850;
+		*shift = (ent->line - 10) * 4;
+		*mask = 0x3;
+
+	} else {
+		return false;
+	}
+
+	return true;
+}
+
+int
+nv17_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag)
+{
+	struct dcb_gpio_entry *ent = nouveau_bios_gpio_entry(dev, tag);
+	uint32_t reg, shift, mask, value;
+
+	if (!ent)
+		return -ENODEV;
+
+	if (!get_gpio_location(ent, &reg, &shift, &mask))
+		return -ENODEV;
+
+	value = NVReadCRTC(dev, 0, reg) >> shift;
+
+	return (ent->invert ? 1 : 0) ^ (value & 1);
+}
+
+int
+nv17_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state)
+{
+	struct dcb_gpio_entry *ent = nouveau_bios_gpio_entry(dev, tag);
+	uint32_t reg, shift, mask, value;
+
+	if (!ent)
+		return -ENODEV;
+
+	if (!get_gpio_location(ent, &reg, &shift, &mask))
+		return -ENODEV;
+
+	value = ((ent->invert ? 1 : 0) ^ (state ? 1 : 0)) << shift;
+	mask = ~(mask << shift);
+
+	NVWriteCRTC(dev, 0, reg, value | (NVReadCRTC(dev, 0, reg) & mask));
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nv17_tv.c b/drivers/gpu/drm/nouveau/nv17_tv.c
new file mode 100644
index 0000000..46cfd9c
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv17_tv.c
@@ -0,0 +1,681 @@
+/*
+ * Copyright (C) 2009 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "drmP.h"
+#include "drm_crtc_helper.h"
+#include "nouveau_drv.h"
+#include "nouveau_encoder.h"
+#include "nouveau_connector.h"
+#include "nouveau_crtc.h"
+#include "nouveau_hw.h"
+#include "nv17_tv.h"
+
+enum drm_connector_status nv17_tv_detect(struct drm_encoder *encoder,
+					 struct drm_connector *connector,
+					 uint32_t pin_mask)
+{
+	struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
+
+	tv_enc->pin_mask = pin_mask >> 28 & 0xe;
+
+	switch (tv_enc->pin_mask) {
+	case 0x2:
+	case 0x4:
+		tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Composite;
+		break;
+	case 0xc:
+		tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_SVIDEO;
+		break;
+	case 0xe:
+		if (nouveau_encoder(encoder)->dcb->tvconf.has_component_output)
+			tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Component;
+		else
+			tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_SCART;
+		break;
+	default:
+		tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
+		break;
+	}
+
+	drm_connector_property_set_value(connector,
+			encoder->dev->mode_config.tv_subconnector_property,
+							tv_enc->subconnector);
+
+	return tv_enc->subconnector ? connector_status_connected :
+					connector_status_disconnected;
+}
+
+static const struct {
+	int hdisplay;
+	int vdisplay;
+} modes[] = {
+	{ 640, 400 },
+	{ 640, 480 },
+	{ 720, 480 },
+	{ 720, 576 },
+	{ 800, 600 },
+	{ 1024, 768 },
+	{ 1280, 720 },
+	{ 1280, 1024 },
+	{ 1920, 1080 }
+};
+
+static int nv17_tv_get_modes(struct drm_encoder *encoder,
+			     struct drm_connector *connector)
+{
+	struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
+	struct drm_display_mode *mode;
+	struct drm_display_mode *output_mode;
+	int n = 0;
+	int i;
+
+	if (tv_norm->kind != CTV_ENC_MODE) {
+		struct drm_display_mode *tv_mode;
+
+		for (tv_mode = nv17_tv_modes; tv_mode->hdisplay; tv_mode++) {
+			mode = drm_mode_duplicate(encoder->dev, tv_mode);
+
+			mode->clock = tv_norm->tv_enc_mode.vrefresh *
+						mode->htotal / 1000 *
+						mode->vtotal / 1000;
+
+			if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
+				mode->clock *= 2;
+
+			if (mode->hdisplay == tv_norm->tv_enc_mode.hdisplay &&
+			    mode->vdisplay == tv_norm->tv_enc_mode.vdisplay)
+				mode->type |= DRM_MODE_TYPE_PREFERRED;
+
+			drm_mode_probed_add(connector, mode);
+			n++;
+		}
+		return n;
+	}
+
+	/* tv_norm->kind == CTV_ENC_MODE */
+	output_mode = &tv_norm->ctv_enc_mode.mode;
+	for (i = 0; i < ARRAY_SIZE(modes); i++) {
+		if (modes[i].hdisplay > output_mode->hdisplay ||
+		    modes[i].vdisplay > output_mode->vdisplay)
+			continue;
+
+		if (modes[i].hdisplay == output_mode->hdisplay &&
+		    modes[i].vdisplay == output_mode->vdisplay) {
+			mode = drm_mode_duplicate(encoder->dev, output_mode);
+			mode->type |= DRM_MODE_TYPE_PREFERRED;
+		} else {
+			mode = drm_cvt_mode(encoder->dev, modes[i].hdisplay,
+				modes[i].vdisplay, 60, false,
+				output_mode->flags & DRM_MODE_FLAG_INTERLACE,
+				false);
+		}
+
+		/* CVT modes are sometimes unsuitable... */
+		if (output_mode->hdisplay <= 720
+		    || output_mode->hdisplay >= 1920) {
+			mode->htotal = output_mode->htotal;
+			mode->hsync_start = (mode->hdisplay + (mode->htotal
+					     - mode->hdisplay) * 9 / 10) & ~7;
+			mode->hsync_end = mode->hsync_start + 8;
+		}
+		if (output_mode->vdisplay >= 1024) {
+			mode->vtotal = output_mode->vtotal;
+			mode->vsync_start = output_mode->vsync_start;
+			mode->vsync_end = output_mode->vsync_end;
+		}
+
+		mode->type |= DRM_MODE_TYPE_DRIVER;
+		drm_mode_probed_add(connector, mode);
+		n++;
+	}
+	return n;
+}
+
+static int nv17_tv_mode_valid(struct drm_encoder *encoder,
+			      struct drm_display_mode *mode)
+{
+	struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
+
+	if (tv_norm->kind == CTV_ENC_MODE) {
+		struct drm_display_mode *output_mode =
+						&tv_norm->ctv_enc_mode.mode;
+
+		if (mode->clock > 400000)
+			return MODE_CLOCK_HIGH;
+
+		if (mode->hdisplay > output_mode->hdisplay ||
+		    mode->vdisplay > output_mode->vdisplay)
+			return MODE_BAD;
+
+		if ((mode->flags & DRM_MODE_FLAG_INTERLACE) !=
+		    (output_mode->flags & DRM_MODE_FLAG_INTERLACE))
+			return MODE_NO_INTERLACE;
+
+		if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
+			return MODE_NO_DBLESCAN;
+
+	} else {
+		const int vsync_tolerance = 600;
+
+		if (mode->clock > 70000)
+			return MODE_CLOCK_HIGH;
+
+		if (abs(drm_mode_vrefresh(mode) * 1000 -
+			tv_norm->tv_enc_mode.vrefresh) > vsync_tolerance)
+			return MODE_VSYNC;
+
+		/* The encoder takes care of the actual interlacing */
+		if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+			return MODE_NO_INTERLACE;
+	}
+
+	return MODE_OK;
+}
+
+static bool nv17_tv_mode_fixup(struct drm_encoder *encoder,
+			       struct drm_display_mode *mode,
+			       struct drm_display_mode *adjusted_mode)
+{
+	struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
+
+	if (tv_norm->kind == CTV_ENC_MODE)
+		adjusted_mode->clock = tv_norm->ctv_enc_mode.mode.clock;
+	else
+		adjusted_mode->clock = 90000;
+
+	return true;
+}
+
+static void  nv17_tv_dpms(struct drm_encoder *encoder, int mode)
+{
+	struct drm_device *dev = encoder->dev;
+	struct nv17_tv_state *regs = &to_tv_enc(encoder)->state;
+	struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
+
+	if (nouveau_encoder(encoder)->last_dpms == mode)
+		return;
+	nouveau_encoder(encoder)->last_dpms = mode;
+
+	NV_TRACE(dev, "Setting dpms mode %d on TV encoder (output %d)\n",
+		 mode, nouveau_encoder(encoder)->dcb->index);
+
+	regs->ptv_200 &= ~1;
+
+	if (tv_norm->kind == CTV_ENC_MODE) {
+		nv04_dfp_update_fp_control(encoder, mode);
+
+	} else {
+		nv04_dfp_update_fp_control(encoder, DRM_MODE_DPMS_OFF);
+
+		if (mode == DRM_MODE_DPMS_ON)
+			regs->ptv_200 |= 1;
+	}
+
+	nv_load_ptv(dev, regs, 200);
+
+	nv17_gpio_set(dev, DCB_GPIO_TVDAC1, mode == DRM_MODE_DPMS_ON);
+	nv17_gpio_set(dev, DCB_GPIO_TVDAC0, mode == DRM_MODE_DPMS_ON);
+
+	nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON);
+}
+
+static void nv17_tv_prepare(struct drm_encoder *encoder)
+{
+	struct drm_device *dev = encoder->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct drm_encoder_helper_funcs *helper = encoder->helper_private;
+	struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
+	int head = nouveau_crtc(encoder->crtc)->index;
+	uint8_t *cr_lcd = &dev_priv->mode_reg.crtc_reg[head].CRTC[
+							NV_CIO_CRE_LCD__INDEX];
+	uint32_t dacclk_off = NV_PRAMDAC_DACCLK +
+					nv04_dac_output_offset(encoder);
+	uint32_t dacclk;
+
+	helper->dpms(encoder, DRM_MODE_DPMS_OFF);
+
+	nv04_dfp_disable(dev, head);
+
+	/* Unbind any FP encoders from this head if we need the FP
+	 * stuff enabled. */
+	if (tv_norm->kind == CTV_ENC_MODE) {
+		struct drm_encoder *enc;
+
+		list_for_each_entry(enc, &dev->mode_config.encoder_list, head) {
+			struct dcb_entry *dcb = nouveau_encoder(enc)->dcb;
+
+			if ((dcb->type == OUTPUT_TMDS ||
+			     dcb->type == OUTPUT_LVDS) &&
+			     !enc->crtc &&
+			     nv04_dfp_get_bound_head(dev, dcb) == head) {
+				nv04_dfp_bind_head(dev, dcb, head ^ 1,
+						dev_priv->VBIOS.fp.dual_link);
+			}
+		}
+
+	}
+
+	/* Some NV4x have unknown values (0x3f, 0x50, 0x54, 0x6b, 0x79, 0x7f)
+	 * at LCD__INDEX which we don't alter
+	 */
+	if (!(*cr_lcd & 0x44)) {
+		if (tv_norm->kind == CTV_ENC_MODE)
+			*cr_lcd = 0x1 | (head ? 0x0 : 0x8);
+		else
+			*cr_lcd = 0;
+	}
+
+	/* Set the DACCLK register */
+	dacclk = (NVReadRAMDAC(dev, 0, dacclk_off) & ~0x30) | 0x1;
+
+	if (dev_priv->card_type == NV_40)
+		dacclk |= 0x1a << 16;
+
+	if (tv_norm->kind == CTV_ENC_MODE) {
+		dacclk |=  0x20;
+
+		if (head)
+			dacclk |= 0x100;
+		else
+			dacclk &= ~0x100;
+
+	} else {
+		dacclk |= 0x10;
+
+	}
+
+	NVWriteRAMDAC(dev, 0, dacclk_off, dacclk);
+}
+
+static void nv17_tv_mode_set(struct drm_encoder *encoder,
+			     struct drm_display_mode *drm_mode,
+			     struct drm_display_mode *adjusted_mode)
+{
+	struct drm_device *dev = encoder->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	int head = nouveau_crtc(encoder->crtc)->index;
+	struct nv04_crtc_reg *regs = &dev_priv->mode_reg.crtc_reg[head];
+	struct nv17_tv_state *tv_regs = &to_tv_enc(encoder)->state;
+	struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
+	int i;
+
+	regs->CRTC[NV_CIO_CRE_53] = 0x40; /* FP_HTIMING */
+	regs->CRTC[NV_CIO_CRE_54] = 0; /* FP_VTIMING */
+	regs->ramdac_630 = 0x2; /* turn off green mode (tv test pattern?) */
+	regs->tv_setup = 1;
+	regs->ramdac_8c0 = 0x0;
+
+	if (tv_norm->kind == TV_ENC_MODE) {
+		tv_regs->ptv_200 = 0x13111100;
+		if (head)
+			tv_regs->ptv_200 |= 0x10;
+
+		tv_regs->ptv_20c = 0x808010;
+		tv_regs->ptv_304 = 0x2d00000;
+		tv_regs->ptv_600 = 0x0;
+		tv_regs->ptv_60c = 0x0;
+		tv_regs->ptv_610 = 0x1e00000;
+
+		if (tv_norm->tv_enc_mode.vdisplay == 576) {
+			tv_regs->ptv_508 = 0x1200000;
+			tv_regs->ptv_614 = 0x33;
+
+		} else if (tv_norm->tv_enc_mode.vdisplay == 480) {
+			tv_regs->ptv_508 = 0xf00000;
+			tv_regs->ptv_614 = 0x13;
+		}
+
+		if (dev_priv->card_type >= NV_30) {
+			tv_regs->ptv_500 = 0xe8e0;
+			tv_regs->ptv_504 = 0x1710;
+			tv_regs->ptv_604 = 0x0;
+			tv_regs->ptv_608 = 0x0;
+		} else {
+			if (tv_norm->tv_enc_mode.vdisplay == 576) {
+				tv_regs->ptv_604 = 0x20;
+				tv_regs->ptv_608 = 0x10;
+				tv_regs->ptv_500 = 0x19710;
+				tv_regs->ptv_504 = 0x68f0;
+
+			} else if (tv_norm->tv_enc_mode.vdisplay == 480) {
+				tv_regs->ptv_604 = 0x10;
+				tv_regs->ptv_608 = 0x20;
+				tv_regs->ptv_500 = 0x4b90;
+				tv_regs->ptv_504 = 0x1b480;
+			}
+		}
+
+		for (i = 0; i < 0x40; i++)
+			tv_regs->tv_enc[i] = tv_norm->tv_enc_mode.tv_enc[i];
+
+	} else {
+		struct drm_display_mode *output_mode =
+						&tv_norm->ctv_enc_mode.mode;
+
+		/* The registers in PRAMDAC+0xc00 control some timings and CSC
+		 * parameters for the CTV encoder (It's only used for "HD" TV
+		 * modes, I don't think I have enough working to guess what
+		 * they exactly mean...), it's probably connected at the
+		 * output of the FP encoder, but it also needs the analog
+		 * encoder in its OR enabled and routed to the head it's
+		 * using. It's enabled with the DACCLK register, bits [5:4].
+		 */
+		for (i = 0; i < 38; i++)
+			regs->ctv_regs[i] = tv_norm->ctv_enc_mode.ctv_regs[i];
+
+		regs->fp_horiz_regs[FP_DISPLAY_END] = output_mode->hdisplay - 1;
+		regs->fp_horiz_regs[FP_TOTAL] = output_mode->htotal - 1;
+		regs->fp_horiz_regs[FP_SYNC_START] =
+						output_mode->hsync_start - 1;
+		regs->fp_horiz_regs[FP_SYNC_END] = output_mode->hsync_end - 1;
+		regs->fp_horiz_regs[FP_CRTC] = output_mode->hdisplay +
+			max((output_mode->hdisplay-600)/40 - 1, 1);
+
+		regs->fp_vert_regs[FP_DISPLAY_END] = output_mode->vdisplay - 1;
+		regs->fp_vert_regs[FP_TOTAL] = output_mode->vtotal - 1;
+		regs->fp_vert_regs[FP_SYNC_START] =
+						output_mode->vsync_start - 1;
+		regs->fp_vert_regs[FP_SYNC_END] = output_mode->vsync_end - 1;
+		regs->fp_vert_regs[FP_CRTC] = output_mode->vdisplay - 1;
+
+		regs->fp_control = NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS |
+			NV_PRAMDAC_FP_TG_CONTROL_READ_PROG |
+			NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12;
+
+		if (output_mode->flags & DRM_MODE_FLAG_PVSYNC)
+			regs->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS;
+		if (output_mode->flags & DRM_MODE_FLAG_PHSYNC)
+			regs->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS;
+
+		regs->fp_debug_0 = NV_PRAMDAC_FP_DEBUG_0_YWEIGHT_ROUND |
+			NV_PRAMDAC_FP_DEBUG_0_XWEIGHT_ROUND |
+			NV_PRAMDAC_FP_DEBUG_0_YINTERP_BILINEAR |
+			NV_PRAMDAC_FP_DEBUG_0_XINTERP_BILINEAR |
+			NV_RAMDAC_FP_DEBUG_0_TMDS_ENABLED |
+			NV_PRAMDAC_FP_DEBUG_0_YSCALE_ENABLE |
+			NV_PRAMDAC_FP_DEBUG_0_XSCALE_ENABLE;
+
+		regs->fp_debug_2 = 0;
+
+		regs->fp_margin_color = 0x801080;
+
+	}
+}
+
+static void nv17_tv_commit(struct drm_encoder *encoder)
+{
+	struct drm_device *dev = encoder->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct drm_encoder_helper_funcs *helper = encoder->helper_private;
+
+	if (get_tv_norm(encoder)->kind == TV_ENC_MODE) {
+		nv17_tv_update_rescaler(encoder);
+		nv17_tv_update_properties(encoder);
+	} else {
+		nv17_ctv_update_rescaler(encoder);
+	}
+
+	nv17_tv_state_load(dev, &to_tv_enc(encoder)->state);
+
+	/* This could use refinement for flatpanels, but it should work */
+	if (dev_priv->chipset < 0x44)
+		NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL +
+					nv04_dac_output_offset(encoder),
+					0xf0000000);
+	else
+		NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL +
+					nv04_dac_output_offset(encoder),
+					0x00100000);
+
+	helper->dpms(encoder, DRM_MODE_DPMS_ON);
+
+	NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
+		drm_get_connector_name(
+			&nouveau_encoder_connector_get(nv_encoder)->base),
+		nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
+}
+
+static void nv17_tv_save(struct drm_encoder *encoder)
+{
+	struct drm_device *dev = encoder->dev;
+	struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
+
+	nouveau_encoder(encoder)->restore.output =
+					NVReadRAMDAC(dev, 0,
+					NV_PRAMDAC_DACCLK +
+					nv04_dac_output_offset(encoder));
+
+	nv17_tv_state_save(dev, &tv_enc->saved_state);
+
+	tv_enc->state.ptv_200 = tv_enc->saved_state.ptv_200;
+}
+
+static void nv17_tv_restore(struct drm_encoder *encoder)
+{
+	struct drm_device *dev = encoder->dev;
+
+	NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK +
+				nv04_dac_output_offset(encoder),
+				nouveau_encoder(encoder)->restore.output);
+
+	nv17_tv_state_load(dev, &to_tv_enc(encoder)->saved_state);
+}
+
+static int nv17_tv_create_resources(struct drm_encoder *encoder,
+				    struct drm_connector *connector)
+{
+	struct drm_device *dev = encoder->dev;
+	struct drm_mode_config *conf = &dev->mode_config;
+	struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
+	struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
+	int num_tv_norms = dcb->tvconf.has_component_output ? NUM_TV_NORMS :
+							NUM_LD_TV_NORMS;
+	int i;
+
+	if (nouveau_tv_norm) {
+		for (i = 0; i < num_tv_norms; i++) {
+			if (!strcmp(nv17_tv_norm_names[i], nouveau_tv_norm)) {
+				tv_enc->tv_norm = i;
+				break;
+			}
+		}
+
+		if (i == num_tv_norms)
+			NV_WARN(dev, "Invalid TV norm setting \"%s\"\n",
+				nouveau_tv_norm);
+	}
+
+	drm_mode_create_tv_properties(dev, num_tv_norms, nv17_tv_norm_names);
+
+	drm_connector_attach_property(connector,
+					conf->tv_select_subconnector_property,
+					tv_enc->select_subconnector);
+	drm_connector_attach_property(connector,
+					conf->tv_subconnector_property,
+					tv_enc->subconnector);
+	drm_connector_attach_property(connector,
+					conf->tv_mode_property,
+					tv_enc->tv_norm);
+	drm_connector_attach_property(connector,
+					conf->tv_flicker_reduction_property,
+					tv_enc->flicker);
+	drm_connector_attach_property(connector,
+					conf->tv_saturation_property,
+					tv_enc->saturation);
+	drm_connector_attach_property(connector,
+					conf->tv_hue_property,
+					tv_enc->hue);
+	drm_connector_attach_property(connector,
+					conf->tv_overscan_property,
+					tv_enc->overscan);
+
+	return 0;
+}
+
+static int nv17_tv_set_property(struct drm_encoder *encoder,
+				struct drm_connector *connector,
+				struct drm_property *property,
+				uint64_t val)
+{
+	struct drm_mode_config *conf = &encoder->dev->mode_config;
+	struct drm_crtc *crtc = encoder->crtc;
+	struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
+	struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
+	bool modes_changed = false;
+
+	if (property == conf->tv_overscan_property) {
+		tv_enc->overscan = val;
+		if (encoder->crtc) {
+			if (tv_norm->kind == CTV_ENC_MODE)
+				nv17_ctv_update_rescaler(encoder);
+			else
+				nv17_tv_update_rescaler(encoder);
+		}
+
+	} else if (property == conf->tv_saturation_property) {
+		if (tv_norm->kind != TV_ENC_MODE)
+			return -EINVAL;
+
+		tv_enc->saturation = val;
+		nv17_tv_update_properties(encoder);
+
+	} else if (property == conf->tv_hue_property) {
+		if (tv_norm->kind != TV_ENC_MODE)
+			return -EINVAL;
+
+		tv_enc->hue = val;
+		nv17_tv_update_properties(encoder);
+
+	} else if (property == conf->tv_flicker_reduction_property) {
+		if (tv_norm->kind != TV_ENC_MODE)
+			return -EINVAL;
+
+		tv_enc->flicker = val;
+		if (encoder->crtc)
+			nv17_tv_update_rescaler(encoder);
+
+	} else if (property == conf->tv_mode_property) {
+		if (connector->dpms != DRM_MODE_DPMS_OFF)
+			return -EINVAL;
+
+		tv_enc->tv_norm = val;
+
+		modes_changed = true;
+
+	} else if (property == conf->tv_select_subconnector_property) {
+		if (tv_norm->kind != TV_ENC_MODE)
+			return -EINVAL;
+
+		tv_enc->select_subconnector = val;
+		nv17_tv_update_properties(encoder);
+
+	} else {
+		return -EINVAL;
+	}
+
+	if (modes_changed) {
+		drm_helper_probe_single_connector_modes(connector, 0, 0);
+
+		/* Disable the crtc to ensure a full modeset is
+		 * performed whenever it's turned on again. */
+		if (crtc) {
+			struct drm_mode_set modeset = {
+				.crtc = crtc,
+			};
+
+			crtc->funcs->set_config(&modeset);
+		}
+	}
+
+	return 0;
+}
+
+static void nv17_tv_destroy(struct drm_encoder *encoder)
+{
+	struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
+
+	NV_DEBUG(encoder->dev, "\n");
+
+	drm_encoder_cleanup(encoder);
+	kfree(tv_enc);
+}
+
+static struct drm_encoder_helper_funcs nv17_tv_helper_funcs = {
+	.dpms = nv17_tv_dpms,
+	.save = nv17_tv_save,
+	.restore = nv17_tv_restore,
+	.mode_fixup = nv17_tv_mode_fixup,
+	.prepare = nv17_tv_prepare,
+	.commit = nv17_tv_commit,
+	.mode_set = nv17_tv_mode_set,
+	.detect = nv17_dac_detect,
+};
+
+static struct drm_encoder_slave_funcs nv17_tv_slave_funcs = {
+	.get_modes = nv17_tv_get_modes,
+	.mode_valid = nv17_tv_mode_valid,
+	.create_resources = nv17_tv_create_resources,
+	.set_property = nv17_tv_set_property,
+};
+
+static struct drm_encoder_funcs nv17_tv_funcs = {
+	.destroy = nv17_tv_destroy,
+};
+
+int nv17_tv_create(struct drm_device *dev, struct dcb_entry *entry)
+{
+	struct drm_encoder *encoder;
+	struct nv17_tv_encoder *tv_enc = NULL;
+
+	tv_enc = kzalloc(sizeof(*tv_enc), GFP_KERNEL);
+	if (!tv_enc)
+		return -ENOMEM;
+
+	tv_enc->overscan = 50;
+	tv_enc->flicker = 50;
+	tv_enc->saturation = 50;
+	tv_enc->hue = 0;
+	tv_enc->tv_norm = TV_NORM_PAL;
+	tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
+	tv_enc->select_subconnector = DRM_MODE_SUBCONNECTOR_Automatic;
+	tv_enc->pin_mask = 0;
+
+	encoder = to_drm_encoder(&tv_enc->base);
+
+	tv_enc->base.dcb = entry;
+	tv_enc->base.or = ffs(entry->or) - 1;
+
+	drm_encoder_init(dev, encoder, &nv17_tv_funcs, DRM_MODE_ENCODER_TVDAC);
+	drm_encoder_helper_add(encoder, &nv17_tv_helper_funcs);
+	to_encoder_slave(encoder)->slave_funcs = &nv17_tv_slave_funcs;
+
+	encoder->possible_crtcs = entry->heads;
+	encoder->possible_clones = 0;
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nv17_tv.h b/drivers/gpu/drm/nouveau/nv17_tv.h
new file mode 100644
index 0000000..c00977c
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv17_tv.h
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2009 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef __NV17_TV_H__
+#define __NV17_TV_H__
+
+struct nv17_tv_state {
+	uint8_t tv_enc[0x40];
+
+	uint32_t hfilter[4][7];
+	uint32_t hfilter2[4][7];
+	uint32_t vfilter[4][7];
+
+	uint32_t ptv_200;
+	uint32_t ptv_204;
+	uint32_t ptv_208;
+	uint32_t ptv_20c;
+	uint32_t ptv_304;
+	uint32_t ptv_500;
+	uint32_t ptv_504;
+	uint32_t ptv_508;
+	uint32_t ptv_600;
+	uint32_t ptv_604;
+	uint32_t ptv_608;
+	uint32_t ptv_60c;
+	uint32_t ptv_610;
+	uint32_t ptv_614;
+};
+
+enum nv17_tv_norm{
+	TV_NORM_PAL,
+	TV_NORM_PAL_M,
+	TV_NORM_PAL_N,
+	TV_NORM_PAL_NC,
+	TV_NORM_NTSC_M,
+	TV_NORM_NTSC_J,
+	NUM_LD_TV_NORMS,
+	TV_NORM_HD480I = NUM_LD_TV_NORMS,
+	TV_NORM_HD480P,
+	TV_NORM_HD576I,
+	TV_NORM_HD576P,
+	TV_NORM_HD720P,
+	TV_NORM_HD1080I,
+	NUM_TV_NORMS
+};
+
+struct nv17_tv_encoder {
+	struct nouveau_encoder base;
+
+	struct nv17_tv_state state;
+	struct nv17_tv_state saved_state;
+
+	int overscan;
+	int flicker;
+	int saturation;
+	int hue;
+	enum nv17_tv_norm tv_norm;
+	int subconnector;
+	int select_subconnector;
+	uint32_t pin_mask;
+};
+#define to_tv_enc(x) container_of(nouveau_encoder(x),		\
+				  struct nv17_tv_encoder, base)
+
+extern char *nv17_tv_norm_names[NUM_TV_NORMS];
+
+extern struct nv17_tv_norm_params {
+	enum {
+		TV_ENC_MODE,
+		CTV_ENC_MODE,
+	} kind;
+
+	union {
+		struct {
+			int hdisplay;
+			int vdisplay;
+			int vrefresh; /* mHz */
+
+			uint8_t tv_enc[0x40];
+		} tv_enc_mode;
+
+		struct {
+			struct drm_display_mode mode;
+
+			uint32_t ctv_regs[38];
+		} ctv_enc_mode;
+	};
+
+} nv17_tv_norms[NUM_TV_NORMS];
+#define get_tv_norm(enc) (&nv17_tv_norms[to_tv_enc(enc)->tv_norm])
+
+extern struct drm_display_mode nv17_tv_modes[];
+
+static inline int interpolate(int y0, int y1, int y2, int x)
+{
+	return y1 + (x < 50 ? y1 - y0 : y2 - y1) * (x - 50) / 50;
+}
+
+void nv17_tv_state_save(struct drm_device *dev, struct nv17_tv_state *state);
+void nv17_tv_state_load(struct drm_device *dev, struct nv17_tv_state *state);
+void nv17_tv_update_properties(struct drm_encoder *encoder);
+void nv17_tv_update_rescaler(struct drm_encoder *encoder);
+void nv17_ctv_update_rescaler(struct drm_encoder *encoder);
+
+/* TV hardware access functions */
+
+static inline void nv_write_ptv(struct drm_device *dev, uint32_t reg, uint32_t val)
+{
+	nv_wr32(dev, reg, val);
+}
+
+static inline uint32_t nv_read_ptv(struct drm_device *dev, uint32_t reg)
+{
+	return nv_rd32(dev, reg);
+}
+
+static inline void nv_write_tv_enc(struct drm_device *dev, uint8_t reg, uint8_t val)
+{
+	nv_write_ptv(dev, NV_PTV_TV_INDEX, reg);
+	nv_write_ptv(dev, NV_PTV_TV_DATA, val);
+}
+
+static inline uint8_t nv_read_tv_enc(struct drm_device *dev, uint8_t reg)
+{
+	nv_write_ptv(dev, NV_PTV_TV_INDEX, reg);
+	return nv_read_ptv(dev, NV_PTV_TV_DATA);
+}
+
+#define nv_load_ptv(dev, state, reg) nv_write_ptv(dev, NV_PTV_OFFSET + 0x##reg, state->ptv_##reg)
+#define nv_save_ptv(dev, state, reg) state->ptv_##reg = nv_read_ptv(dev, NV_PTV_OFFSET + 0x##reg)
+#define nv_load_tv_enc(dev, state, reg) nv_write_tv_enc(dev, 0x##reg, state->tv_enc[0x##reg])
+
+#endif
diff --git a/drivers/gpu/drm/nouveau/nv17_tv_modes.c b/drivers/gpu/drm/nouveau/nv17_tv_modes.c
new file mode 100644
index 0000000..d64683d
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv17_tv_modes.c
@@ -0,0 +1,583 @@
+/*
+ * Copyright (C) 2009 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "drmP.h"
+#include "drm_crtc_helper.h"
+#include "nouveau_drv.h"
+#include "nouveau_encoder.h"
+#include "nouveau_crtc.h"
+#include "nouveau_hw.h"
+#include "nv17_tv.h"
+
+char *nv17_tv_norm_names[NUM_TV_NORMS] = {
+	[TV_NORM_PAL] = "PAL",
+	[TV_NORM_PAL_M] = "PAL-M",
+	[TV_NORM_PAL_N] = "PAL-N",
+	[TV_NORM_PAL_NC] = "PAL-Nc",
+	[TV_NORM_NTSC_M] = "NTSC-M",
+	[TV_NORM_NTSC_J] = "NTSC-J",
+	[TV_NORM_HD480I] = "hd480i",
+	[TV_NORM_HD480P] = "hd480p",
+	[TV_NORM_HD576I] = "hd576i",
+	[TV_NORM_HD576P] = "hd576p",
+	[TV_NORM_HD720P] = "hd720p",
+	[TV_NORM_HD1080I] = "hd1080i"
+};
+
+/* TV standard specific parameters */
+
+struct nv17_tv_norm_params nv17_tv_norms[NUM_TV_NORMS] = {
+	[TV_NORM_PAL] = { TV_ENC_MODE, {
+			.tv_enc_mode = { 720, 576, 50000, {
+					0x2a, 0x9, 0x8a, 0xcb, 0x0, 0x0, 0xb, 0x18,
+					0x7e, 0x40, 0x8a, 0x35, 0x27, 0x0, 0x34, 0x3,
+					0x3e, 0x3, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c,
+					0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x3,
+					0xd3, 0x4, 0xd4, 0x1, 0x2, 0x0, 0xa, 0x5,
+					0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0,
+					0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b,
+					0xbd, 0x15, 0x5, 0x15, 0x3e, 0x3, 0x0, 0x0
+				} } } },
+
+	[TV_NORM_PAL_M] = { TV_ENC_MODE, {
+			.tv_enc_mode = { 720, 480, 59940, {
+					0x21, 0xe6, 0xef, 0xe3, 0x0, 0x0, 0xb, 0x18,
+					0x7e, 0x44, 0x76, 0x32, 0x25, 0x0, 0x3c, 0x0,
+					0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83,
+					0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1,
+					0xc5, 0x4, 0xc5, 0x1, 0x2, 0x0, 0xa, 0x5,
+					0x0, 0x18, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0,
+					0x0, 0xb4, 0x0, 0x15, 0x40, 0x10, 0x0, 0x9c,
+					0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0
+				} } } },
+
+	[TV_NORM_PAL_N] = { TV_ENC_MODE, {
+			.tv_enc_mode = { 720, 576, 50000, {
+					0x2a, 0x9, 0x8a, 0xcb, 0x0, 0x0, 0xb, 0x18,
+					0x7e, 0x40, 0x8a, 0x32, 0x25, 0x0, 0x3c, 0x0,
+					0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c,
+					0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1,
+					0xc5, 0x4, 0xc5, 0x1, 0x2, 0x0, 0xa, 0x5,
+					0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0,
+					0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b,
+					0xbd, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0
+				} } } },
+
+	[TV_NORM_PAL_NC] = { TV_ENC_MODE, {
+			.tv_enc_mode = { 720, 576, 50000, {
+					0x21, 0xf6, 0x94, 0x46, 0x0, 0x0, 0xb, 0x18,
+					0x7e, 0x44, 0x8a, 0x35, 0x27, 0x0, 0x34, 0x3,
+					0x3e, 0x3, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c,
+					0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x3,
+					0xd3, 0x4, 0xd4, 0x1, 0x2, 0x0, 0xa, 0x5,
+					0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0,
+					0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b,
+					0xbd, 0x15, 0x5, 0x15, 0x3e, 0x3, 0x0, 0x0
+				} } } },
+
+	[TV_NORM_NTSC_M] = { TV_ENC_MODE, {
+			.tv_enc_mode = { 720, 480, 59940, {
+					0x21, 0xf0, 0x7c, 0x1f, 0x0, 0x0, 0xb, 0x18,
+					0x7e, 0x44, 0x76, 0x48, 0x0, 0x0, 0x3c, 0x0,
+					0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83,
+					0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1,
+					0xc5, 0x4, 0xc5, 0x1, 0x2, 0x0, 0xa, 0x5,
+					0x0, 0x16, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0,
+					0x0, 0xb4, 0x0, 0x15, 0x4, 0x10, 0x0, 0x9c,
+					0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0
+				} } } },
+
+	[TV_NORM_NTSC_J] = { TV_ENC_MODE, {
+			.tv_enc_mode = { 720, 480, 59940, {
+					0x21, 0xf0, 0x7c, 0x1f, 0x0, 0x0, 0xb, 0x18,
+					0x7e, 0x44, 0x76, 0x48, 0x0, 0x0, 0x32, 0x0,
+					0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83,
+					0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1,
+					0xcf, 0x4, 0xcf, 0x1, 0x2, 0x0, 0xa, 0x5,
+					0x0, 0x16, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0,
+					0x0, 0xb4, 0x0, 0x15, 0x4, 0x10, 0x0, 0xa4,
+					0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0
+				} } } },
+
+	[TV_NORM_HD480I] = { TV_ENC_MODE, {
+			.tv_enc_mode = { 720, 480, 59940, {
+					0x21, 0xf0, 0x7c, 0x1f, 0x0, 0x0, 0xb, 0x18,
+					0x7e, 0x44, 0x76, 0x48, 0x0, 0x0, 0x32, 0x0,
+					0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83,
+					0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1,
+					0xcf, 0x4, 0xcf, 0x1, 0x2, 0x0, 0xa, 0x5,
+					0x0, 0x16, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0,
+					0x0, 0xb4, 0x0, 0x15, 0x4, 0x10, 0x0, 0xa4,
+					0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0
+				} } } },
+
+	[TV_NORM_HD576I] = { TV_ENC_MODE, {
+			.tv_enc_mode = { 720, 576, 50000, {
+					0x2a, 0x9, 0x8a, 0xcb, 0x0, 0x0, 0xb, 0x18,
+					0x7e, 0x40, 0x8a, 0x35, 0x27, 0x0, 0x34, 0x3,
+					0x3e, 0x3, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c,
+					0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x3,
+					0xd3, 0x4, 0xd4, 0x1, 0x2, 0x0, 0xa, 0x5,
+					0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0,
+					0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b,
+					0xbd, 0x15, 0x5, 0x15, 0x3e, 0x3, 0x0, 0x0
+				} } } },
+
+
+	[TV_NORM_HD480P] = { CTV_ENC_MODE, {
+			.ctv_enc_mode = {
+				.mode = { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000,
+						   720, 735, 743, 858, 0, 480, 490, 494, 525, 0,
+						   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+				.ctv_regs = { 0x3540000, 0x0, 0x0, 0x314,
+					      0x354003a, 0x40000, 0x6f0344, 0x18100000,
+					      0x10160004, 0x10060005, 0x1006000c, 0x10060020,
+					      0x10060021, 0x140e0022, 0x10060202, 0x1802020a,
+					      0x1810020b, 0x10000fff, 0x10000fff, 0x10000fff,
+					      0x10000fff, 0x10000fff, 0x10000fff, 0x70,
+					      0x3ff0000, 0x57, 0x2e001e, 0x258012c,
+					      0xa0aa04ec, 0x30, 0x80960019, 0x12c0300,
+					      0x2019, 0x600, 0x32060019, 0x0, 0x0, 0x400
+				} } } },
+
+	[TV_NORM_HD576P] = { CTV_ENC_MODE, {
+			.ctv_enc_mode = {
+				.mode = { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000,
+						   720, 730, 738, 864, 0, 576, 581, 585, 625, 0,
+						   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+				.ctv_regs = { 0x3540000, 0x0, 0x0, 0x314,
+					      0x354003a, 0x40000, 0x6f0344, 0x18100000,
+					      0x10060001, 0x10060009, 0x10060026, 0x10060027,
+					      0x140e0028, 0x10060268, 0x1810026d, 0x10000fff,
+					      0x10000fff, 0x10000fff, 0x10000fff, 0x10000fff,
+					      0x10000fff, 0x10000fff, 0x10000fff, 0x69,
+					      0x3ff0000, 0x57, 0x2e001e, 0x258012c,
+					      0xa0aa04ec, 0x30, 0x80960019, 0x12c0300,
+					      0x2019, 0x600, 0x32060019, 0x0, 0x0, 0x400
+				} } } },
+
+	[TV_NORM_HD720P] = { CTV_ENC_MODE, {
+			.ctv_enc_mode = {
+				.mode = { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250,
+						   1280, 1349, 1357, 1650, 0, 720, 725, 730, 750, 0,
+						   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+				.ctv_regs = { 0x1260394, 0x0, 0x0, 0x622,
+					      0x66b0021, 0x6004a, 0x1210626, 0x8170000,
+					      0x70004, 0x70016, 0x70017, 0x40f0018,
+					      0x702e8, 0x81702ed, 0xfff, 0xfff,
+					      0xfff, 0xfff, 0xfff, 0xfff,
+					      0xfff, 0xfff, 0xfff, 0x0,
+					      0x2e40001, 0x58, 0x2e001e, 0x258012c,
+					      0xa0aa04ec, 0x30, 0x810c0039, 0x12c0300,
+					      0xc0002039, 0x600, 0x32060039, 0x0, 0x0, 0x0
+				} } } },
+
+	[TV_NORM_HD1080I] = { CTV_ENC_MODE, {
+			.ctv_enc_mode = {
+				.mode = { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250,
+						   1920, 1961, 2049, 2200, 0, 1080, 1084, 1088, 1125, 0,
+						   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC
+						   | DRM_MODE_FLAG_INTERLACE) },
+				.ctv_regs = { 0xac0420, 0x44c0478, 0x4a4, 0x4fc0868,
+					      0x8940028, 0x60054, 0xe80870, 0xbf70000,
+					      0xbc70004, 0x70005, 0x70012, 0x70013,
+					      0x40f0014, 0x70230, 0xbf70232, 0xbf70233,
+					      0x1c70237, 0x70238, 0x70244, 0x70245,
+					      0x40f0246, 0x70462, 0x1f70464, 0x0,
+					      0x2e40001, 0x58, 0x2e001e, 0x258012c,
+					      0xa0aa04ec, 0x30, 0x815f004c, 0x12c0300,
+					      0xc000204c, 0x600, 0x3206004c, 0x0, 0x0, 0x0
+				} } } }
+};
+
+/*
+ * The following is some guesswork on how the TV encoder flicker
+ * filter/rescaler works:
+ *
+ * It seems to use some sort of resampling filter, it is controlled
+ * through the registers at NV_PTV_HFILTER and NV_PTV_VFILTER, they
+ * control the horizontal and vertical stage respectively, there is
+ * also NV_PTV_HFILTER2 the blob fills identically to NV_PTV_HFILTER,
+ * but they seem to do nothing. A rough guess might be that they could
+ * be used to independently control the filtering of each interlaced
+ * field, but I don't know how they are enabled. The whole filtering
+ * process seems to be disabled with bits 26:27 of PTV_200, but we
+ * aren't doing that.
+ *
+ * The layout of both register sets is the same:
+ *
+ * A: [BASE+0x18]...[BASE+0x0] [BASE+0x58]..[BASE+0x40]
+ * B: [BASE+0x34]...[BASE+0x1c] [BASE+0x74]..[BASE+0x5c]
+ *
+ * Each coefficient is stored in bits [31],[15:9] in two's complement
+ * format. They seem to be some kind of weights used in a low-pass
+ * filter. Both A and B coefficients are applied to the 14 nearest
+ * samples on each side (Listed from nearest to furthermost.  They
+ * roughly cover 2 framebuffer pixels on each side).  They are
+ * probably multiplied with some more hardwired weights before being
+ * used: B-coefficients are applied the same on both sides,
+ * A-coefficients are inverted before being applied to the opposite
+ * side.
+ *
+ * After all the hassle, I got the following formula by empirical
+ * means...
+ */
+
+#define calc_overscan(o) interpolate(0x100, 0xe1, 0xc1, o)
+
+#define id1 (1LL << 8)
+#define id2 (1LL << 16)
+#define id3 (1LL << 24)
+#define id4 (1LL << 32)
+#define id5 (1LL << 48)
+
+static struct filter_params{
+	int64_t k1;
+	int64_t ki;
+	int64_t ki2;
+	int64_t ki3;
+	int64_t kr;
+	int64_t kir;
+	int64_t ki2r;
+	int64_t ki3r;
+	int64_t kf;
+	int64_t kif;
+	int64_t ki2f;
+	int64_t ki3f;
+	int64_t krf;
+	int64_t kirf;
+	int64_t ki2rf;
+	int64_t ki3rf;
+} fparams[2][4] = {
+	/* Horizontal filter parameters */
+	{
+		{64.311690 * id5, -39.516924 * id5, 6.586143 * id5, 0.000002 * id5,
+		 0.051285 * id4, 26.168746 * id4, -4.361449 * id4, -0.000001 * id4,
+		 9.308169 * id3, 78.180965 * id3, -13.030158 * id3, -0.000001 * id3,
+		 -8.801540 * id1, -46.572890 * id1, 7.762145 * id1, -0.000000 * id1},
+		{-44.565569 * id5, -68.081246 * id5, 39.812074 * id5, -4.009316 * id5,
+		 29.832207 * id4, 50.047322 * id4, -25.380017 * id4, 2.546422 * id4,
+		 104.605622 * id3, 141.908641 * id3, -74.322319 * id3, 7.484316 * id3,
+		 -37.081621 * id1, -90.397510 * id1, 42.784229 * id1, -4.289952 * id1},
+		{-56.793244 * id5, 31.153584 * id5, -5.192247 * id5, -0.000003 * id5,
+		 33.541131 * id4, -34.149302 * id4, 5.691537 * id4, 0.000002 * id4,
+		 87.196610 * id3, -88.995169 * id3, 14.832456 * id3, 0.000012 * id3,
+		 17.288138 * id1, 71.864786 * id1, -11.977408 * id1, -0.000009 * id1},
+		{51.787796 * id5, 21.211771 * id5, -18.993730 * id5, 1.853310 * id5,
+		 -41.470726 * id4, -17.775823 * id4, 13.057821 * id4, -1.15823 * id4,
+		 -154.235673 * id3, -44.878641 * id3, 40.656077 * id3, -3.695595 * id3,
+		 112.201065 * id1, 39.992155 * id1, -25.155714 * id1, 2.113984 * id1},
+	},
+
+	/* Vertical filter parameters */
+	{
+		{67.601979 * id5, 0.428319 * id5, -0.071318 * id5, -0.000012 * id5,
+		 -3.402339 * id4, 0.000209 * id4, -0.000092 * id4, 0.000010 * id4,
+		 -9.180996 * id3, 6.111270 * id3, -1.024457 * id3, 0.001043 * id3,
+		 6.060315 * id1, -0.017425 * id1, 0.007830 * id1, -0.000869 * id1},
+		{6.755647 * id5, 5.841348 * id5, 1.469734 * id5, -0.149656 * id5,
+		 8.293120 * id4, -1.192888 * id4, -0.947652 * id4, 0.094507 * id4,
+		 37.526655 * id3, 10.257875 * id3, -10.823275 * id3, 1.081497 * id3,
+		 -2.361928 * id1, -2.059432 * id1, 1.840671 * id1, -0.168100 * id1},
+		{-14.780391 * id5, -16.042148 * id5, 2.673692 * id5, -0.000000 * id5,
+		 39.541978 * id4, 5.680053 * id4, -0.946676 * id4, 0.000000 * id4,
+		 152.994486 * id3, 12.625439 * id3, -2.119579 * id3, 0.002708 * id3,
+		 -38.125089 * id1, -0.855880 * id1, 0.155359 * id1, -0.002245 * id1},
+		{-27.476193 * id5, -1.454976 * id5, 1.286557 * id5, 0.025346 * id5,
+		 20.687300 * id4, 3.014003 * id4, -0.557786 * id4, -0.01311 * id4,
+		 60.008737 * id3, -0.738273 * id3, 5.408217 * id3, -0.796798 * id3,
+		 -17.296835 * id1, 4.438577 * id1, -2.809420 * id1, 0.385491 * id1},
+	}
+};
+
+static void tv_setup_filter(struct drm_encoder *encoder)
+{
+	struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
+	struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
+	struct drm_display_mode *mode = &encoder->crtc->mode;
+	uint32_t (*filters[])[4][7] = {&tv_enc->state.hfilter,
+				       &tv_enc->state.vfilter};
+	int i, j, k;
+	int32_t overscan = calc_overscan(tv_enc->overscan);
+	int64_t flicker = (tv_enc->flicker - 50) * (id3 / 100);
+	uint64_t rs[] = {mode->hdisplay * id3,
+			 mode->vdisplay * id3};
+
+	do_div(rs[0], overscan * tv_norm->tv_enc_mode.hdisplay);
+	do_div(rs[1], overscan * tv_norm->tv_enc_mode.vdisplay);
+
+	for (k = 0; k < 2; k++) {
+		rs[k] = max((int64_t)rs[k], id2);
+
+		for (j = 0; j < 4; j++) {
+			struct filter_params *p = &fparams[k][j];
+
+			for (i = 0; i < 7; i++) {
+				int64_t c = (p->k1 + p->ki*i + p->ki2*i*i + p->ki3*i*i*i)
+					+ (p->kr + p->kir*i + p->ki2r*i*i + p->ki3r*i*i*i)*rs[k]
+					+ (p->kf + p->kif*i + p->ki2f*i*i + p->ki3f*i*i*i)*flicker
+					+ (p->krf + p->kirf*i + p->ki2rf*i*i + p->ki3rf*i*i*i)*flicker*rs[k];
+
+				(*filters[k])[j][i] = (c + id5/2) >> 39 & (0x1 << 31 | 0x7f << 9);
+			}
+		}
+	}
+}
+
+/* Hardware state saving/restoring */
+
+static void tv_save_filter(struct drm_device *dev, uint32_t base, uint32_t regs[4][7])
+{
+	int i, j;
+	uint32_t offsets[] = { base, base + 0x1c, base + 0x40, base + 0x5c };
+
+	for (i = 0; i < 4; i++) {
+		for (j = 0; j < 7; j++)
+			regs[i][j] = nv_read_ptv(dev, offsets[i]+4*j);
+	}
+}
+
+static void tv_load_filter(struct drm_device *dev, uint32_t base, uint32_t regs[4][7])
+{
+	int i, j;
+	uint32_t offsets[] = { base, base + 0x1c, base + 0x40, base + 0x5c };
+
+	for (i = 0; i < 4; i++) {
+		for (j = 0; j < 7; j++)
+			nv_write_ptv(dev, offsets[i]+4*j, regs[i][j]);
+	}
+}
+
+void nv17_tv_state_save(struct drm_device *dev, struct nv17_tv_state *state)
+{
+	int i;
+
+	for (i = 0; i < 0x40; i++)
+		state->tv_enc[i] = nv_read_tv_enc(dev, i);
+
+	tv_save_filter(dev, NV_PTV_HFILTER, state->hfilter);
+	tv_save_filter(dev, NV_PTV_HFILTER2, state->hfilter2);
+	tv_save_filter(dev, NV_PTV_VFILTER, state->vfilter);
+
+	nv_save_ptv(dev, state, 200);
+	nv_save_ptv(dev, state, 204);
+	nv_save_ptv(dev, state, 208);
+	nv_save_ptv(dev, state, 20c);
+	nv_save_ptv(dev, state, 304);
+	nv_save_ptv(dev, state, 500);
+	nv_save_ptv(dev, state, 504);
+	nv_save_ptv(dev, state, 508);
+	nv_save_ptv(dev, state, 600);
+	nv_save_ptv(dev, state, 604);
+	nv_save_ptv(dev, state, 608);
+	nv_save_ptv(dev, state, 60c);
+	nv_save_ptv(dev, state, 610);
+	nv_save_ptv(dev, state, 614);
+}
+
+void nv17_tv_state_load(struct drm_device *dev, struct nv17_tv_state *state)
+{
+	int i;
+
+	for (i = 0; i < 0x40; i++)
+		nv_write_tv_enc(dev, i, state->tv_enc[i]);
+
+	tv_load_filter(dev, NV_PTV_HFILTER, state->hfilter);
+	tv_load_filter(dev, NV_PTV_HFILTER2, state->hfilter2);
+	tv_load_filter(dev, NV_PTV_VFILTER, state->vfilter);
+
+	nv_load_ptv(dev, state, 200);
+	nv_load_ptv(dev, state, 204);
+	nv_load_ptv(dev, state, 208);
+	nv_load_ptv(dev, state, 20c);
+	nv_load_ptv(dev, state, 304);
+	nv_load_ptv(dev, state, 500);
+	nv_load_ptv(dev, state, 504);
+	nv_load_ptv(dev, state, 508);
+	nv_load_ptv(dev, state, 600);
+	nv_load_ptv(dev, state, 604);
+	nv_load_ptv(dev, state, 608);
+	nv_load_ptv(dev, state, 60c);
+	nv_load_ptv(dev, state, 610);
+	nv_load_ptv(dev, state, 614);
+
+	/* This is required for some settings to kick in. */
+	nv_write_tv_enc(dev, 0x3e, 1);
+	nv_write_tv_enc(dev, 0x3e, 0);
+}
+
+/* Timings similar to the ones the blob sets */
+
+struct drm_display_mode nv17_tv_modes[] = {
+	{ DRM_MODE("320x200", DRM_MODE_TYPE_DRIVER, 0,
+		   320, 344, 392, 560, 0, 200, 200, 202, 220, 0,
+		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC
+		   | DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) },
+	{ DRM_MODE("320x240", DRM_MODE_TYPE_DRIVER, 0,
+		   320, 344, 392, 560, 0, 240, 240, 246, 263, 0,
+		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC
+		   | DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) },
+	{ DRM_MODE("400x300", DRM_MODE_TYPE_DRIVER, 0,
+		   400, 432, 496, 640, 0, 300, 300, 303, 314, 0,
+		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC
+		   | DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) },
+	{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 0,
+		   640, 672, 768, 880, 0, 480, 480, 492, 525, 0,
+		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
+	{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 0,
+		   720, 752, 872, 960, 0, 480, 480, 493, 525, 0,
+		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
+	{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 0,
+		   720, 776, 856, 960, 0, 576, 576, 588, 597, 0,
+		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
+	{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 0,
+		   800, 840, 920, 1040, 0, 600, 600, 604, 618, 0,
+		   DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
+	{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 0,
+		   1024, 1064, 1200, 1344, 0, 768, 768, 777, 806, 0,
+		   DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
+	{}
+};
+
+void nv17_tv_update_properties(struct drm_encoder *encoder)
+{
+	struct drm_device *dev = encoder->dev;
+	struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
+	struct nv17_tv_state *regs = &tv_enc->state;
+	struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
+	int subconnector = tv_enc->select_subconnector ?
+						tv_enc->select_subconnector :
+						tv_enc->subconnector;
+
+	switch (subconnector) {
+	case DRM_MODE_SUBCONNECTOR_Composite:
+	{
+		regs->ptv_204 = 0x2;
+
+		/* The composite connector may be found on either pin. */
+		if (tv_enc->pin_mask & 0x4)
+			regs->ptv_204 |= 0x010000;
+		else if (tv_enc->pin_mask & 0x2)
+			regs->ptv_204 |= 0x100000;
+		else
+			regs->ptv_204 |= 0x110000;
+
+		regs->tv_enc[0x7] = 0x10;
+		break;
+	}
+	case DRM_MODE_SUBCONNECTOR_SVIDEO:
+		regs->ptv_204 = 0x11012;
+		regs->tv_enc[0x7] = 0x18;
+		break;
+
+	case DRM_MODE_SUBCONNECTOR_Component:
+		regs->ptv_204 = 0x111333;
+		regs->tv_enc[0x7] = 0x14;
+		break;
+
+	case DRM_MODE_SUBCONNECTOR_SCART:
+		regs->ptv_204 = 0x111012;
+		regs->tv_enc[0x7] = 0x18;
+		break;
+	}
+
+	regs->tv_enc[0x20] = interpolate(0, tv_norm->tv_enc_mode.tv_enc[0x20], 255,
+					 tv_enc->saturation);
+	regs->tv_enc[0x22] = interpolate(0, tv_norm->tv_enc_mode.tv_enc[0x22], 255,
+					 tv_enc->saturation);
+	regs->tv_enc[0x25] = tv_enc->hue * 255 / 100;
+
+	nv_load_ptv(dev, regs, 204);
+	nv_load_tv_enc(dev, regs, 7);
+	nv_load_tv_enc(dev, regs, 20);
+	nv_load_tv_enc(dev, regs, 22);
+	nv_load_tv_enc(dev, regs, 25);
+}
+
+void nv17_tv_update_rescaler(struct drm_encoder *encoder)
+{
+	struct drm_device *dev = encoder->dev;
+	struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
+	struct nv17_tv_state *regs = &tv_enc->state;
+
+	regs->ptv_208 = 0x40 | (calc_overscan(tv_enc->overscan) << 8);
+
+	tv_setup_filter(encoder);
+
+	nv_load_ptv(dev, regs, 208);
+	tv_load_filter(dev, NV_PTV_HFILTER, regs->hfilter);
+	tv_load_filter(dev, NV_PTV_HFILTER2, regs->hfilter2);
+	tv_load_filter(dev, NV_PTV_VFILTER, regs->vfilter);
+}
+
+void nv17_ctv_update_rescaler(struct drm_encoder *encoder)
+{
+	struct drm_device *dev = encoder->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
+	int head = nouveau_crtc(encoder->crtc)->index;
+	struct nv04_crtc_reg *regs = &dev_priv->mode_reg.crtc_reg[head];
+	struct drm_display_mode *crtc_mode = &encoder->crtc->mode;
+	struct drm_display_mode *output_mode = &get_tv_norm(encoder)->ctv_enc_mode.mode;
+	int overscan, hmargin, vmargin, hratio, vratio;
+
+	/* The rescaler doesn't do the right thing for interlaced modes. */
+	if (output_mode->flags & DRM_MODE_FLAG_INTERLACE)
+		overscan = 100;
+	else
+		overscan = tv_enc->overscan;
+
+	hmargin = (output_mode->hdisplay - crtc_mode->hdisplay) / 2;
+	vmargin = (output_mode->vdisplay - crtc_mode->vdisplay) / 2;
+
+	hmargin = interpolate(0, min(hmargin, output_mode->hdisplay/20), hmargin,
+			      overscan);
+	vmargin = interpolate(0, min(vmargin, output_mode->vdisplay/20), vmargin,
+			      overscan);
+
+	hratio = crtc_mode->hdisplay * 0x800 / (output_mode->hdisplay - 2*hmargin);
+	vratio = crtc_mode->vdisplay * 0x800 / (output_mode->vdisplay - 2*vmargin) & ~3;
+
+	regs->fp_horiz_regs[FP_VALID_START] = hmargin;
+	regs->fp_horiz_regs[FP_VALID_END] = output_mode->hdisplay - hmargin - 1;
+	regs->fp_vert_regs[FP_VALID_START] = vmargin;
+	regs->fp_vert_regs[FP_VALID_END] = output_mode->vdisplay - vmargin - 1;
+
+	regs->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_YSCALE_TESTMODE_ENABLE |
+		XLATE(vratio, 0, NV_PRAMDAC_FP_DEBUG_1_YSCALE_VALUE) |
+		NV_PRAMDAC_FP_DEBUG_1_XSCALE_TESTMODE_ENABLE |
+		XLATE(hratio, 0, NV_PRAMDAC_FP_DEBUG_1_XSCALE_VALUE);
+
+	NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HVALID_START,
+		      regs->fp_horiz_regs[FP_VALID_START]);
+	NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HVALID_END,
+		      regs->fp_horiz_regs[FP_VALID_END]);
+	NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_VVALID_START,
+		      regs->fp_vert_regs[FP_VALID_START]);
+	NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_VVALID_END,
+		      regs->fp_vert_regs[FP_VALID_END]);
+	NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_1, regs->fp_debug_1);
+}
diff --git a/drivers/gpu/drm/nouveau/nv20_graph.c b/drivers/gpu/drm/nouveau/nv20_graph.c
new file mode 100644
index 0000000..18ba74f
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv20_graph.c
@@ -0,0 +1,780 @@
+#include "drmP.h"
+#include "drm.h"
+#include "nouveau_drv.h"
+#include "nouveau_drm.h"
+
+/*
+ * NV20
+ * -----
+ * There are 3 families :
+ * NV20 is 0x10de:0x020*
+ * NV25/28 is 0x10de:0x025* / 0x10de:0x028*
+ * NV2A is 0x10de:0x02A0
+ *
+ * NV30
+ * -----
+ * There are 3 families :
+ * NV30/31 is 0x10de:0x030* / 0x10de:0x031*
+ * NV34 is 0x10de:0x032*
+ * NV35/36 is 0x10de:0x033* / 0x10de:0x034*
+ *
+ * Not seen in the wild, no dumps (probably NV35) :
+ * NV37 is 0x10de:0x00fc, 0x10de:0x00fd
+ * NV38 is 0x10de:0x0333, 0x10de:0x00fe
+ *
+ */
+
+#define NV20_GRCTX_SIZE (3580*4)
+#define NV25_GRCTX_SIZE (3529*4)
+#define NV2A_GRCTX_SIZE (3500*4)
+
+#define NV30_31_GRCTX_SIZE (24392)
+#define NV34_GRCTX_SIZE    (18140)
+#define NV35_36_GRCTX_SIZE (22396)
+
+static void
+nv20_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
+{
+	int i;
+
+	nv_wo32(dev, ctx, 0x033c/4, 0xffff0000);
+	nv_wo32(dev, ctx, 0x03a0/4, 0x0fff0000);
+	nv_wo32(dev, ctx, 0x03a4/4, 0x0fff0000);
+	nv_wo32(dev, ctx, 0x047c/4, 0x00000101);
+	nv_wo32(dev, ctx, 0x0490/4, 0x00000111);
+	nv_wo32(dev, ctx, 0x04a8/4, 0x44400000);
+	for (i = 0x04d4; i <= 0x04e0; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x00030303);
+	for (i = 0x04f4; i <= 0x0500; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x00080000);
+	for (i = 0x050c; i <= 0x0518; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x01012000);
+	for (i = 0x051c; i <= 0x0528; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x000105b8);
+	for (i = 0x052c; i <= 0x0538; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x00080008);
+	for (i = 0x055c; i <= 0x0598; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x07ff0000);
+	nv_wo32(dev, ctx, 0x05a4/4, 0x4b7fffff);
+	nv_wo32(dev, ctx, 0x05fc/4, 0x00000001);
+	nv_wo32(dev, ctx, 0x0604/4, 0x00004000);
+	nv_wo32(dev, ctx, 0x0610/4, 0x00000001);
+	nv_wo32(dev, ctx, 0x0618/4, 0x00040000);
+	nv_wo32(dev, ctx, 0x061c/4, 0x00010000);
+	for (i = 0x1c1c; i <= 0x248c; i += 16) {
+		nv_wo32(dev, ctx, (i + 0)/4, 0x10700ff9);
+		nv_wo32(dev, ctx, (i + 4)/4, 0x0436086c);
+		nv_wo32(dev, ctx, (i + 8)/4, 0x000c001b);
+	}
+	nv_wo32(dev, ctx, 0x281c/4, 0x3f800000);
+	nv_wo32(dev, ctx, 0x2830/4, 0x3f800000);
+	nv_wo32(dev, ctx, 0x285c/4, 0x40000000);
+	nv_wo32(dev, ctx, 0x2860/4, 0x3f800000);
+	nv_wo32(dev, ctx, 0x2864/4, 0x3f000000);
+	nv_wo32(dev, ctx, 0x286c/4, 0x40000000);
+	nv_wo32(dev, ctx, 0x2870/4, 0x3f800000);
+	nv_wo32(dev, ctx, 0x2878/4, 0xbf800000);
+	nv_wo32(dev, ctx, 0x2880/4, 0xbf800000);
+	nv_wo32(dev, ctx, 0x34a4/4, 0x000fe000);
+	nv_wo32(dev, ctx, 0x3530/4, 0x000003f8);
+	nv_wo32(dev, ctx, 0x3540/4, 0x002fe000);
+	for (i = 0x355c; i <= 0x3578; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x001c527c);
+}
+
+static void
+nv25_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
+{
+	int i;
+
+	nv_wo32(dev, ctx, 0x035c/4, 0xffff0000);
+	nv_wo32(dev, ctx, 0x03c0/4, 0x0fff0000);
+	nv_wo32(dev, ctx, 0x03c4/4, 0x0fff0000);
+	nv_wo32(dev, ctx, 0x049c/4, 0x00000101);
+	nv_wo32(dev, ctx, 0x04b0/4, 0x00000111);
+	nv_wo32(dev, ctx, 0x04c8/4, 0x00000080);
+	nv_wo32(dev, ctx, 0x04cc/4, 0xffff0000);
+	nv_wo32(dev, ctx, 0x04d0/4, 0x00000001);
+	nv_wo32(dev, ctx, 0x04e4/4, 0x44400000);
+	nv_wo32(dev, ctx, 0x04fc/4, 0x4b800000);
+	for (i = 0x0510; i <= 0x051c; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x00030303);
+	for (i = 0x0530; i <= 0x053c; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x00080000);
+	for (i = 0x0548; i <= 0x0554; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x01012000);
+	for (i = 0x0558; i <= 0x0564; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x000105b8);
+	for (i = 0x0568; i <= 0x0574; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x00080008);
+	for (i = 0x0598; i <= 0x05d4; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x07ff0000);
+	nv_wo32(dev, ctx, 0x05e0/4, 0x4b7fffff);
+	nv_wo32(dev, ctx, 0x0620/4, 0x00000080);
+	nv_wo32(dev, ctx, 0x0624/4, 0x30201000);
+	nv_wo32(dev, ctx, 0x0628/4, 0x70605040);
+	nv_wo32(dev, ctx, 0x062c/4, 0xb0a09080);
+	nv_wo32(dev, ctx, 0x0630/4, 0xf0e0d0c0);
+	nv_wo32(dev, ctx, 0x0664/4, 0x00000001);
+	nv_wo32(dev, ctx, 0x066c/4, 0x00004000);
+	nv_wo32(dev, ctx, 0x0678/4, 0x00000001);
+	nv_wo32(dev, ctx, 0x0680/4, 0x00040000);
+	nv_wo32(dev, ctx, 0x0684/4, 0x00010000);
+	for (i = 0x1b04; i <= 0x2374; i += 16) {
+		nv_wo32(dev, ctx, (i + 0)/4, 0x10700ff9);
+		nv_wo32(dev, ctx, (i + 4)/4, 0x0436086c);
+		nv_wo32(dev, ctx, (i + 8)/4, 0x000c001b);
+	}
+	nv_wo32(dev, ctx, 0x2704/4, 0x3f800000);
+	nv_wo32(dev, ctx, 0x2718/4, 0x3f800000);
+	nv_wo32(dev, ctx, 0x2744/4, 0x40000000);
+	nv_wo32(dev, ctx, 0x2748/4, 0x3f800000);
+	nv_wo32(dev, ctx, 0x274c/4, 0x3f000000);
+	nv_wo32(dev, ctx, 0x2754/4, 0x40000000);
+	nv_wo32(dev, ctx, 0x2758/4, 0x3f800000);
+	nv_wo32(dev, ctx, 0x2760/4, 0xbf800000);
+	nv_wo32(dev, ctx, 0x2768/4, 0xbf800000);
+	nv_wo32(dev, ctx, 0x308c/4, 0x000fe000);
+	nv_wo32(dev, ctx, 0x3108/4, 0x000003f8);
+	nv_wo32(dev, ctx, 0x3468/4, 0x002fe000);
+	for (i = 0x3484; i <= 0x34a0; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x001c527c);
+}
+
+static void
+nv2a_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
+{
+	int i;
+
+	nv_wo32(dev, ctx, 0x033c/4, 0xffff0000);
+	nv_wo32(dev, ctx, 0x03a0/4, 0x0fff0000);
+	nv_wo32(dev, ctx, 0x03a4/4, 0x0fff0000);
+	nv_wo32(dev, ctx, 0x047c/4, 0x00000101);
+	nv_wo32(dev, ctx, 0x0490/4, 0x00000111);
+	nv_wo32(dev, ctx, 0x04a8/4, 0x44400000);
+	for (i = 0x04d4; i <= 0x04e0; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x00030303);
+	for (i = 0x04f4; i <= 0x0500; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x00080000);
+	for (i = 0x050c; i <= 0x0518; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x01012000);
+	for (i = 0x051c; i <= 0x0528; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x000105b8);
+	for (i = 0x052c; i <= 0x0538; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x00080008);
+	for (i = 0x055c; i <= 0x0598; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x07ff0000);
+	nv_wo32(dev, ctx, 0x05a4/4, 0x4b7fffff);
+	nv_wo32(dev, ctx, 0x05fc/4, 0x00000001);
+	nv_wo32(dev, ctx, 0x0604/4, 0x00004000);
+	nv_wo32(dev, ctx, 0x0610/4, 0x00000001);
+	nv_wo32(dev, ctx, 0x0618/4, 0x00040000);
+	nv_wo32(dev, ctx, 0x061c/4, 0x00010000);
+	for (i = 0x1a9c; i <= 0x22fc; i += 16) { /*XXX: check!! */
+		nv_wo32(dev, ctx, (i + 0)/4, 0x10700ff9);
+		nv_wo32(dev, ctx, (i + 4)/4, 0x0436086c);
+		nv_wo32(dev, ctx, (i + 8)/4, 0x000c001b);
+	}
+	nv_wo32(dev, ctx, 0x269c/4, 0x3f800000);
+	nv_wo32(dev, ctx, 0x26b0/4, 0x3f800000);
+	nv_wo32(dev, ctx, 0x26dc/4, 0x40000000);
+	nv_wo32(dev, ctx, 0x26e0/4, 0x3f800000);
+	nv_wo32(dev, ctx, 0x26e4/4, 0x3f000000);
+	nv_wo32(dev, ctx, 0x26ec/4, 0x40000000);
+	nv_wo32(dev, ctx, 0x26f0/4, 0x3f800000);
+	nv_wo32(dev, ctx, 0x26f8/4, 0xbf800000);
+	nv_wo32(dev, ctx, 0x2700/4, 0xbf800000);
+	nv_wo32(dev, ctx, 0x3024/4, 0x000fe000);
+	nv_wo32(dev, ctx, 0x30a0/4, 0x000003f8);
+	nv_wo32(dev, ctx, 0x33fc/4, 0x002fe000);
+	for (i = 0x341c; i <= 0x3438; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x001c527c);
+}
+
+static void
+nv30_31_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
+{
+	int i;
+
+	nv_wo32(dev, ctx, 0x0410/4, 0x00000101);
+	nv_wo32(dev, ctx, 0x0424/4, 0x00000111);
+	nv_wo32(dev, ctx, 0x0428/4, 0x00000060);
+	nv_wo32(dev, ctx, 0x0444/4, 0x00000080);
+	nv_wo32(dev, ctx, 0x0448/4, 0xffff0000);
+	nv_wo32(dev, ctx, 0x044c/4, 0x00000001);
+	nv_wo32(dev, ctx, 0x0460/4, 0x44400000);
+	nv_wo32(dev, ctx, 0x048c/4, 0xffff0000);
+	for (i = 0x04e0; i < 0x04e8; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x0fff0000);
+	nv_wo32(dev, ctx, 0x04ec/4, 0x00011100);
+	for (i = 0x0508; i < 0x0548; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x07ff0000);
+	nv_wo32(dev, ctx, 0x0550/4, 0x4b7fffff);
+	nv_wo32(dev, ctx, 0x058c/4, 0x00000080);
+	nv_wo32(dev, ctx, 0x0590/4, 0x30201000);
+	nv_wo32(dev, ctx, 0x0594/4, 0x70605040);
+	nv_wo32(dev, ctx, 0x0598/4, 0xb8a89888);
+	nv_wo32(dev, ctx, 0x059c/4, 0xf8e8d8c8);
+	nv_wo32(dev, ctx, 0x05b0/4, 0xb0000000);
+	for (i = 0x0600; i < 0x0640; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x00010588);
+	for (i = 0x0640; i < 0x0680; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x00030303);
+	for (i = 0x06c0; i < 0x0700; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x0008aae4);
+	for (i = 0x0700; i < 0x0740; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x01012000);
+	for (i = 0x0740; i < 0x0780; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x00080008);
+	nv_wo32(dev, ctx, 0x085c/4, 0x00040000);
+	nv_wo32(dev, ctx, 0x0860/4, 0x00010000);
+	for (i = 0x0864; i < 0x0874; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x00040004);
+	for (i = 0x1f18; i <= 0x3088 ; i += 16) {
+		nv_wo32(dev, ctx, i/4 + 0, 0x10700ff9);
+		nv_wo32(dev, ctx, i/4 + 1, 0x0436086c);
+		nv_wo32(dev, ctx, i/4 + 2, 0x000c001b);
+	}
+	for (i = 0x30b8; i < 0x30c8; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x0000ffff);
+	nv_wo32(dev, ctx, 0x344c/4, 0x3f800000);
+	nv_wo32(dev, ctx, 0x3808/4, 0x3f800000);
+	nv_wo32(dev, ctx, 0x381c/4, 0x3f800000);
+	nv_wo32(dev, ctx, 0x3848/4, 0x40000000);
+	nv_wo32(dev, ctx, 0x384c/4, 0x3f800000);
+	nv_wo32(dev, ctx, 0x3850/4, 0x3f000000);
+	nv_wo32(dev, ctx, 0x3858/4, 0x40000000);
+	nv_wo32(dev, ctx, 0x385c/4, 0x3f800000);
+	nv_wo32(dev, ctx, 0x3864/4, 0xbf800000);
+	nv_wo32(dev, ctx, 0x386c/4, 0xbf800000);
+}
+
+static void
+nv34_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
+{
+	int i;
+
+	nv_wo32(dev, ctx, 0x040c/4, 0x01000101);
+	nv_wo32(dev, ctx, 0x0420/4, 0x00000111);
+	nv_wo32(dev, ctx, 0x0424/4, 0x00000060);
+	nv_wo32(dev, ctx, 0x0440/4, 0x00000080);
+	nv_wo32(dev, ctx, 0x0444/4, 0xffff0000);
+	nv_wo32(dev, ctx, 0x0448/4, 0x00000001);
+	nv_wo32(dev, ctx, 0x045c/4, 0x44400000);
+	nv_wo32(dev, ctx, 0x0480/4, 0xffff0000);
+	for (i = 0x04d4; i < 0x04dc; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x0fff0000);
+	nv_wo32(dev, ctx, 0x04e0/4, 0x00011100);
+	for (i = 0x04fc; i < 0x053c; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x07ff0000);
+	nv_wo32(dev, ctx, 0x0544/4, 0x4b7fffff);
+	nv_wo32(dev, ctx, 0x057c/4, 0x00000080);
+	nv_wo32(dev, ctx, 0x0580/4, 0x30201000);
+	nv_wo32(dev, ctx, 0x0584/4, 0x70605040);
+	nv_wo32(dev, ctx, 0x0588/4, 0xb8a89888);
+	nv_wo32(dev, ctx, 0x058c/4, 0xf8e8d8c8);
+	nv_wo32(dev, ctx, 0x05a0/4, 0xb0000000);
+	for (i = 0x05f0; i < 0x0630; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x00010588);
+	for (i = 0x0630; i < 0x0670; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x00030303);
+	for (i = 0x06b0; i < 0x06f0; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x0008aae4);
+	for (i = 0x06f0; i < 0x0730; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x01012000);
+	for (i = 0x0730; i < 0x0770; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x00080008);
+	nv_wo32(dev, ctx, 0x0850/4, 0x00040000);
+	nv_wo32(dev, ctx, 0x0854/4, 0x00010000);
+	for (i = 0x0858; i < 0x0868; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x00040004);
+	for (i = 0x15ac; i <= 0x271c ; i += 16) {
+		nv_wo32(dev, ctx, i/4 + 0, 0x10700ff9);
+		nv_wo32(dev, ctx, i/4 + 1, 0x0436086c);
+		nv_wo32(dev, ctx, i/4 + 2, 0x000c001b);
+	}
+	for (i = 0x274c; i < 0x275c; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x0000ffff);
+	nv_wo32(dev, ctx, 0x2ae0/4, 0x3f800000);
+	nv_wo32(dev, ctx, 0x2e9c/4, 0x3f800000);
+	nv_wo32(dev, ctx, 0x2eb0/4, 0x3f800000);
+	nv_wo32(dev, ctx, 0x2edc/4, 0x40000000);
+	nv_wo32(dev, ctx, 0x2ee0/4, 0x3f800000);
+	nv_wo32(dev, ctx, 0x2ee4/4, 0x3f000000);
+	nv_wo32(dev, ctx, 0x2eec/4, 0x40000000);
+	nv_wo32(dev, ctx, 0x2ef0/4, 0x3f800000);
+	nv_wo32(dev, ctx, 0x2ef8/4, 0xbf800000);
+	nv_wo32(dev, ctx, 0x2f00/4, 0xbf800000);
+}
+
+static void
+nv35_36_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
+{
+	int i;
+
+	nv_wo32(dev, ctx, 0x040c/4, 0x00000101);
+	nv_wo32(dev, ctx, 0x0420/4, 0x00000111);
+	nv_wo32(dev, ctx, 0x0424/4, 0x00000060);
+	nv_wo32(dev, ctx, 0x0440/4, 0x00000080);
+	nv_wo32(dev, ctx, 0x0444/4, 0xffff0000);
+	nv_wo32(dev, ctx, 0x0448/4, 0x00000001);
+	nv_wo32(dev, ctx, 0x045c/4, 0x44400000);
+	nv_wo32(dev, ctx, 0x0488/4, 0xffff0000);
+	for (i = 0x04dc; i < 0x04e4; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x0fff0000);
+	nv_wo32(dev, ctx, 0x04e8/4, 0x00011100);
+	for (i = 0x0504; i < 0x0544; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x07ff0000);
+	nv_wo32(dev, ctx, 0x054c/4, 0x4b7fffff);
+	nv_wo32(dev, ctx, 0x0588/4, 0x00000080);
+	nv_wo32(dev, ctx, 0x058c/4, 0x30201000);
+	nv_wo32(dev, ctx, 0x0590/4, 0x70605040);
+	nv_wo32(dev, ctx, 0x0594/4, 0xb8a89888);
+	nv_wo32(dev, ctx, 0x0598/4, 0xf8e8d8c8);
+	nv_wo32(dev, ctx, 0x05ac/4, 0xb0000000);
+	for (i = 0x0604; i < 0x0644; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x00010588);
+	for (i = 0x0644; i < 0x0684; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x00030303);
+	for (i = 0x06c4; i < 0x0704; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x0008aae4);
+	for (i = 0x0704; i < 0x0744; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x01012000);
+	for (i = 0x0744; i < 0x0784; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x00080008);
+	nv_wo32(dev, ctx, 0x0860/4, 0x00040000);
+	nv_wo32(dev, ctx, 0x0864/4, 0x00010000);
+	for (i = 0x0868; i < 0x0878; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x00040004);
+	for (i = 0x1f1c; i <= 0x308c ; i += 16) {
+		nv_wo32(dev, ctx, i/4 + 0, 0x10700ff9);
+		nv_wo32(dev, ctx, i/4 + 1, 0x0436086c);
+		nv_wo32(dev, ctx, i/4 + 2, 0x000c001b);
+	}
+	for (i = 0x30bc; i < 0x30cc; i += 4)
+		nv_wo32(dev, ctx, i/4, 0x0000ffff);
+	nv_wo32(dev, ctx, 0x3450/4, 0x3f800000);
+	nv_wo32(dev, ctx, 0x380c/4, 0x3f800000);
+	nv_wo32(dev, ctx, 0x3820/4, 0x3f800000);
+	nv_wo32(dev, ctx, 0x384c/4, 0x40000000);
+	nv_wo32(dev, ctx, 0x3850/4, 0x3f800000);
+	nv_wo32(dev, ctx, 0x3854/4, 0x3f000000);
+	nv_wo32(dev, ctx, 0x385c/4, 0x40000000);
+	nv_wo32(dev, ctx, 0x3860/4, 0x3f800000);
+	nv_wo32(dev, ctx, 0x3868/4, 0xbf800000);
+	nv_wo32(dev, ctx, 0x3870/4, 0xbf800000);
+}
+
+int
+nv20_graph_create_context(struct nouveau_channel *chan)
+{
+	struct drm_device *dev = chan->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	void (*ctx_init)(struct drm_device *, struct nouveau_gpuobj *);
+	unsigned int ctx_size;
+	unsigned int idoffs = 0x28/4;
+	int ret;
+
+	switch (dev_priv->chipset) {
+	case 0x20:
+		ctx_size = NV20_GRCTX_SIZE;
+		ctx_init = nv20_graph_context_init;
+		idoffs = 0;
+		break;
+	case 0x25:
+	case 0x28:
+		ctx_size = NV25_GRCTX_SIZE;
+		ctx_init = nv25_graph_context_init;
+		break;
+	case 0x2a:
+		ctx_size = NV2A_GRCTX_SIZE;
+		ctx_init = nv2a_graph_context_init;
+		idoffs = 0;
+		break;
+	case 0x30:
+	case 0x31:
+		ctx_size = NV30_31_GRCTX_SIZE;
+		ctx_init = nv30_31_graph_context_init;
+		break;
+	case 0x34:
+		ctx_size = NV34_GRCTX_SIZE;
+		ctx_init = nv34_graph_context_init;
+		break;
+	case 0x35:
+	case 0x36:
+		ctx_size = NV35_36_GRCTX_SIZE;
+		ctx_init = nv35_36_graph_context_init;
+		break;
+	default:
+		ctx_size = 0;
+		ctx_init = nv35_36_graph_context_init;
+		NV_ERROR(dev, "Please contact the devs if you want your NV%x"
+			      " card to work\n", dev_priv->chipset);
+		return -ENOSYS;
+		break;
+	}
+
+	ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, ctx_size, 16,
+					  NVOBJ_FLAG_ZERO_ALLOC,
+					  &chan->ramin_grctx);
+	if (ret)
+		return ret;
+
+	/* Initialise default context values */
+	dev_priv->engine.instmem.prepare_access(dev, true);
+	ctx_init(dev, chan->ramin_grctx->gpuobj);
+
+	/* nv20: nv_wo32(dev, chan->ramin_grctx->gpuobj, 10, chan->id<<24); */
+	nv_wo32(dev, chan->ramin_grctx->gpuobj, idoffs,
+					(chan->id << 24) | 0x1); /* CTX_USER */
+
+	nv_wo32(dev, dev_priv->ctx_table->gpuobj, chan->id,
+			chan->ramin_grctx->instance >> 4);
+
+	dev_priv->engine.instmem.finish_access(dev);
+	return 0;
+}
+
+void
+nv20_graph_destroy_context(struct nouveau_channel *chan)
+{
+	struct drm_device *dev = chan->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	if (chan->ramin_grctx)
+		nouveau_gpuobj_ref_del(dev, &chan->ramin_grctx);
+
+	dev_priv->engine.instmem.prepare_access(dev, true);
+	nv_wo32(dev, dev_priv->ctx_table->gpuobj, chan->id, 0);
+	dev_priv->engine.instmem.finish_access(dev);
+}
+
+int
+nv20_graph_load_context(struct nouveau_channel *chan)
+{
+	struct drm_device *dev = chan->dev;
+	uint32_t inst;
+
+	if (!chan->ramin_grctx)
+		return -EINVAL;
+	inst = chan->ramin_grctx->instance >> 4;
+
+	nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst);
+	nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_XFER,
+		     NV20_PGRAPH_CHANNEL_CTX_XFER_LOAD);
+	nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10010100);
+
+	nouveau_wait_for_idle(dev);
+	return 0;
+}
+
+int
+nv20_graph_unload_context(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
+	struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
+	struct nouveau_channel *chan;
+	uint32_t inst, tmp;
+
+	chan = pgraph->channel(dev);
+	if (!chan)
+		return 0;
+	inst = chan->ramin_grctx->instance >> 4;
+
+	nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst);
+	nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_XFER,
+		     NV20_PGRAPH_CHANNEL_CTX_XFER_SAVE);
+
+	nouveau_wait_for_idle(dev);
+
+	nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000000);
+	tmp  = nv_rd32(dev, NV10_PGRAPH_CTX_USER) & 0x00ffffff;
+	tmp |= (pfifo->channels - 1) << 24;
+	nv_wr32(dev, NV10_PGRAPH_CTX_USER, tmp);
+	return 0;
+}
+
+static void
+nv20_graph_rdi(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	int i, writecount = 32;
+	uint32_t rdi_index = 0x2c80000;
+
+	if (dev_priv->chipset == 0x20) {
+		rdi_index = 0x3d0000;
+		writecount = 15;
+	}
+
+	nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, rdi_index);
+	for (i = 0; i < writecount; i++)
+		nv_wr32(dev, NV10_PGRAPH_RDI_DATA, 0);
+
+	nouveau_wait_for_idle(dev);
+}
+
+int
+nv20_graph_init(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv =
+		(struct drm_nouveau_private *)dev->dev_private;
+	uint32_t tmp, vramsz;
+	int ret, i;
+
+	nv_wr32(dev, NV03_PMC_ENABLE,
+		nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PGRAPH);
+	nv_wr32(dev, NV03_PMC_ENABLE,
+		nv_rd32(dev, NV03_PMC_ENABLE) |  NV_PMC_ENABLE_PGRAPH);
+
+	if (!dev_priv->ctx_table) {
+		/* Create Context Pointer Table */
+		dev_priv->ctx_table_size = 32 * 4;
+		ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0,
+						  dev_priv->ctx_table_size, 16,
+						  NVOBJ_FLAG_ZERO_ALLOC,
+						  &dev_priv->ctx_table);
+		if (ret)
+			return ret;
+	}
+
+	nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_TABLE,
+		 dev_priv->ctx_table->instance >> 4);
+
+	nv20_graph_rdi(dev);
+
+	nv_wr32(dev, NV03_PGRAPH_INTR   , 0xFFFFFFFF);
+	nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF);
+
+	nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0xFFFFFFFF);
+	nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x00000000);
+	nv_wr32(dev, NV04_PGRAPH_DEBUG_1, 0x00118700);
+	nv_wr32(dev, NV04_PGRAPH_DEBUG_3, 0xF3CE0475); /* 0x4 = auto ctx switch */
+	nv_wr32(dev, NV10_PGRAPH_DEBUG_4, 0x00000000);
+	nv_wr32(dev, 0x40009C           , 0x00000040);
+
+	if (dev_priv->chipset >= 0x25) {
+		nv_wr32(dev, 0x400890, 0x00080000);
+		nv_wr32(dev, 0x400610, 0x304B1FB6);
+		nv_wr32(dev, 0x400B80, 0x18B82880);
+		nv_wr32(dev, 0x400B84, 0x44000000);
+		nv_wr32(dev, 0x400098, 0x40000080);
+		nv_wr32(dev, 0x400B88, 0x000000ff);
+	} else {
+		nv_wr32(dev, 0x400880, 0x00080000); /* 0x0008c7df */
+		nv_wr32(dev, 0x400094, 0x00000005);
+		nv_wr32(dev, 0x400B80, 0x45CAA208); /* 0x45eae20e */
+		nv_wr32(dev, 0x400B84, 0x24000000);
+		nv_wr32(dev, 0x400098, 0x00000040);
+		nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00E00038);
+		nv_wr32(dev, NV10_PGRAPH_RDI_DATA , 0x00000030);
+		nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00E10038);
+		nv_wr32(dev, NV10_PGRAPH_RDI_DATA , 0x00000030);
+	}
+
+	/* copy tile info from PFB */
+	for (i = 0; i < NV10_PFB_TILE__SIZE; i++) {
+		nv_wr32(dev, 0x00400904 + i * 0x10,
+					nv_rd32(dev, NV10_PFB_TLIMIT(i)));
+			/* which is NV40_PGRAPH_TLIMIT0(i) ?? */
+		nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0030 + i * 4);
+		nv_wr32(dev, NV10_PGRAPH_RDI_DATA,
+					nv_rd32(dev, NV10_PFB_TLIMIT(i)));
+		nv_wr32(dev, 0x00400908 + i * 0x10,
+					nv_rd32(dev, NV10_PFB_TSIZE(i)));
+			/* which is NV40_PGRAPH_TSIZE0(i) ?? */
+		nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0050 + i * 4);
+		nv_wr32(dev, NV10_PGRAPH_RDI_DATA,
+					nv_rd32(dev, NV10_PFB_TSIZE(i)));
+		nv_wr32(dev, 0x00400900 + i * 0x10,
+					nv_rd32(dev, NV10_PFB_TILE(i)));
+			/* which is NV40_PGRAPH_TILE0(i) ?? */
+		nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0010 + i * 4);
+		nv_wr32(dev, NV10_PGRAPH_RDI_DATA,
+					nv_rd32(dev, NV10_PFB_TILE(i)));
+	}
+	for (i = 0; i < 8; i++) {
+		nv_wr32(dev, 0x400980 + i * 4, nv_rd32(dev, 0x100300 + i * 4));
+		nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0090 + i * 4);
+		nv_wr32(dev, NV10_PGRAPH_RDI_DATA,
+					nv_rd32(dev, 0x100300 + i * 4));
+	}
+	nv_wr32(dev, 0x4009a0, nv_rd32(dev, 0x100324));
+	nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA000C);
+	nv_wr32(dev, NV10_PGRAPH_RDI_DATA, nv_rd32(dev, 0x100324));
+
+	nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000100);
+	nv_wr32(dev, NV10_PGRAPH_STATE      , 0xFFFFFFFF);
+
+	tmp = nv_rd32(dev, NV10_PGRAPH_SURFACE) & 0x0007ff00;
+	nv_wr32(dev, NV10_PGRAPH_SURFACE, tmp);
+	tmp = nv_rd32(dev, NV10_PGRAPH_SURFACE) | 0x00020100;
+	nv_wr32(dev, NV10_PGRAPH_SURFACE, tmp);
+
+	/* begin RAM config */
+	vramsz = drm_get_resource_len(dev, 0) - 1;
+	nv_wr32(dev, 0x4009A4, nv_rd32(dev, NV04_PFB_CFG0));
+	nv_wr32(dev, 0x4009A8, nv_rd32(dev, NV04_PFB_CFG1));
+	nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0000);
+	nv_wr32(dev, NV10_PGRAPH_RDI_DATA , nv_rd32(dev, NV04_PFB_CFG0));
+	nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0004);
+	nv_wr32(dev, NV10_PGRAPH_RDI_DATA , nv_rd32(dev, NV04_PFB_CFG1));
+	nv_wr32(dev, 0x400820, 0);
+	nv_wr32(dev, 0x400824, 0);
+	nv_wr32(dev, 0x400864, vramsz - 1);
+	nv_wr32(dev, 0x400868, vramsz - 1);
+
+	/* interesting.. the below overwrites some of the tile setup above.. */
+	nv_wr32(dev, 0x400B20, 0x00000000);
+	nv_wr32(dev, 0x400B04, 0xFFFFFFFF);
+
+	nv_wr32(dev, NV03_PGRAPH_ABS_UCLIP_XMIN, 0);
+	nv_wr32(dev, NV03_PGRAPH_ABS_UCLIP_YMIN, 0);
+	nv_wr32(dev, NV03_PGRAPH_ABS_UCLIP_XMAX, 0x7fff);
+	nv_wr32(dev, NV03_PGRAPH_ABS_UCLIP_YMAX, 0x7fff);
+
+	return 0;
+}
+
+void
+nv20_graph_takedown(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	nouveau_gpuobj_ref_del(dev, &dev_priv->ctx_table);
+}
+
+int
+nv30_graph_init(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	int ret, i;
+
+	nv_wr32(dev, NV03_PMC_ENABLE,
+		nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PGRAPH);
+	nv_wr32(dev, NV03_PMC_ENABLE,
+		nv_rd32(dev, NV03_PMC_ENABLE) |  NV_PMC_ENABLE_PGRAPH);
+
+	if (!dev_priv->ctx_table) {
+		/* Create Context Pointer Table */
+		dev_priv->ctx_table_size = 32 * 4;
+		ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0,
+						  dev_priv->ctx_table_size, 16,
+						  NVOBJ_FLAG_ZERO_ALLOC,
+						  &dev_priv->ctx_table);
+		if (ret)
+			return ret;
+	}
+
+	nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_TABLE,
+			dev_priv->ctx_table->instance >> 4);
+
+	nv_wr32(dev, NV03_PGRAPH_INTR   , 0xFFFFFFFF);
+	nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF);
+
+	nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0xFFFFFFFF);
+	nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x00000000);
+	nv_wr32(dev, NV04_PGRAPH_DEBUG_1, 0x401287c0);
+	nv_wr32(dev, 0x400890, 0x01b463ff);
+	nv_wr32(dev, NV04_PGRAPH_DEBUG_3, 0xf2de0475);
+	nv_wr32(dev, NV10_PGRAPH_DEBUG_4, 0x00008000);
+	nv_wr32(dev, NV04_PGRAPH_LIMIT_VIOL_PIX, 0xf04bdff6);
+	nv_wr32(dev, 0x400B80, 0x1003d888);
+	nv_wr32(dev, 0x400B84, 0x0c000000);
+	nv_wr32(dev, 0x400098, 0x00000000);
+	nv_wr32(dev, 0x40009C, 0x0005ad00);
+	nv_wr32(dev, 0x400B88, 0x62ff00ff); /* suspiciously like PGRAPH_DEBUG_2 */
+	nv_wr32(dev, 0x4000a0, 0x00000000);
+	nv_wr32(dev, 0x4000a4, 0x00000008);
+	nv_wr32(dev, 0x4008a8, 0xb784a400);
+	nv_wr32(dev, 0x400ba0, 0x002f8685);
+	nv_wr32(dev, 0x400ba4, 0x00231f3f);
+	nv_wr32(dev, 0x4008a4, 0x40000020);
+
+	if (dev_priv->chipset == 0x34) {
+		nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0004);
+		nv_wr32(dev, NV10_PGRAPH_RDI_DATA , 0x00200201);
+		nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0008);
+		nv_wr32(dev, NV10_PGRAPH_RDI_DATA , 0x00000008);
+		nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0000);
+		nv_wr32(dev, NV10_PGRAPH_RDI_DATA , 0x00000032);
+		nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00E00004);
+		nv_wr32(dev, NV10_PGRAPH_RDI_DATA , 0x00000002);
+	}
+
+	nv_wr32(dev, 0x4000c0, 0x00000016);
+
+	/* copy tile info from PFB */
+	for (i = 0; i < NV10_PFB_TILE__SIZE; i++) {
+		nv_wr32(dev, 0x00400904 + i * 0x10,
+					nv_rd32(dev, NV10_PFB_TLIMIT(i)));
+			/* which is NV40_PGRAPH_TLIMIT0(i) ?? */
+		nv_wr32(dev, 0x00400908 + i * 0x10,
+					nv_rd32(dev, NV10_PFB_TSIZE(i)));
+			/* which is NV40_PGRAPH_TSIZE0(i) ?? */
+		nv_wr32(dev, 0x00400900 + i * 0x10,
+					nv_rd32(dev, NV10_PFB_TILE(i)));
+			/* which is NV40_PGRAPH_TILE0(i) ?? */
+	}
+
+	nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000100);
+	nv_wr32(dev, NV10_PGRAPH_STATE      , 0xFFFFFFFF);
+	nv_wr32(dev, 0x0040075c             , 0x00000001);
+
+	/* begin RAM config */
+	/* vramsz = drm_get_resource_len(dev, 0) - 1; */
+	nv_wr32(dev, 0x4009A4, nv_rd32(dev, NV04_PFB_CFG0));
+	nv_wr32(dev, 0x4009A8, nv_rd32(dev, NV04_PFB_CFG1));
+	if (dev_priv->chipset != 0x34) {
+		nv_wr32(dev, 0x400750, 0x00EA0000);
+		nv_wr32(dev, 0x400754, nv_rd32(dev, NV04_PFB_CFG0));
+		nv_wr32(dev, 0x400750, 0x00EA0004);
+		nv_wr32(dev, 0x400754, nv_rd32(dev, NV04_PFB_CFG1));
+	}
+
+	return 0;
+}
+
+struct nouveau_pgraph_object_class nv20_graph_grclass[] = {
+	{ 0x0030, false, NULL }, /* null */
+	{ 0x0039, false, NULL }, /* m2mf */
+	{ 0x004a, false, NULL }, /* gdirect */
+	{ 0x009f, false, NULL }, /* imageblit (nv12) */
+	{ 0x008a, false, NULL }, /* ifc */
+	{ 0x0089, false, NULL }, /* sifm */
+	{ 0x0062, false, NULL }, /* surf2d */
+	{ 0x0043, false, NULL }, /* rop */
+	{ 0x0012, false, NULL }, /* beta1 */
+	{ 0x0072, false, NULL }, /* beta4 */
+	{ 0x0019, false, NULL }, /* cliprect */
+	{ 0x0044, false, NULL }, /* pattern */
+	{ 0x009e, false, NULL }, /* swzsurf */
+	{ 0x0096, false, NULL }, /* celcius */
+	{ 0x0097, false, NULL }, /* kelvin (nv20) */
+	{ 0x0597, false, NULL }, /* kelvin (nv25) */
+	{}
+};
+
+struct nouveau_pgraph_object_class nv30_graph_grclass[] = {
+	{ 0x0030, false, NULL }, /* null */
+	{ 0x0039, false, NULL }, /* m2mf */
+	{ 0x004a, false, NULL }, /* gdirect */
+	{ 0x009f, false, NULL }, /* imageblit (nv12) */
+	{ 0x008a, false, NULL }, /* ifc */
+	{ 0x038a, false, NULL }, /* ifc (nv30) */
+	{ 0x0089, false, NULL }, /* sifm */
+	{ 0x0389, false, NULL }, /* sifm (nv30) */
+	{ 0x0062, false, NULL }, /* surf2d */
+	{ 0x0362, false, NULL }, /* surf2d (nv30) */
+	{ 0x0043, false, NULL }, /* rop */
+	{ 0x0012, false, NULL }, /* beta1 */
+	{ 0x0072, false, NULL }, /* beta4 */
+	{ 0x0019, false, NULL }, /* cliprect */
+	{ 0x0044, false, NULL }, /* pattern */
+	{ 0x039e, false, NULL }, /* swzsurf */
+	{ 0x0397, false, NULL }, /* rankine (nv30) */
+	{ 0x0497, false, NULL }, /* rankine (nv35) */
+	{ 0x0697, false, NULL }, /* rankine (nv34) */
+	{}
+};
+
diff --git a/drivers/gpu/drm/nouveau/nv40_fb.c b/drivers/gpu/drm/nouveau/nv40_fb.c
new file mode 100644
index 0000000..ca1d271
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv40_fb.c
@@ -0,0 +1,62 @@
+#include "drmP.h"
+#include "drm.h"
+#include "nouveau_drv.h"
+#include "nouveau_drm.h"
+
+int
+nv40_fb_init(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	uint32_t fb_bar_size, tmp;
+	int num_tiles;
+	int i;
+
+	/* This is strictly a NV4x register (don't know about NV5x). */
+	/* The blob sets these to all kinds of values, and they mess up our setup. */
+	/* I got value 0x52802 instead. For some cards the blob even sets it back to 0x1. */
+	/* Note: the blob doesn't read this value, so i'm pretty sure this is safe for all cards. */
+	/* Any idea what this is? */
+	nv_wr32(dev, NV40_PFB_UNK_800, 0x1);
+
+	switch (dev_priv->chipset) {
+	case 0x40:
+	case 0x45:
+		tmp = nv_rd32(dev, NV10_PFB_CLOSE_PAGE2);
+		nv_wr32(dev, NV10_PFB_CLOSE_PAGE2, tmp & ~(1 << 15));
+		num_tiles = NV10_PFB_TILE__SIZE;
+		break;
+	case 0x46: /* G72 */
+	case 0x47: /* G70 */
+	case 0x49: /* G71 */
+	case 0x4b: /* G73 */
+	case 0x4c: /* C51 (G7X version) */
+		num_tiles = NV40_PFB_TILE__SIZE_1;
+		break;
+	default:
+		num_tiles = NV40_PFB_TILE__SIZE_0;
+		break;
+	}
+
+	fb_bar_size = drm_get_resource_len(dev, 0) - 1;
+	switch (dev_priv->chipset) {
+	case 0x40:
+		for (i = 0; i < num_tiles; i++) {
+			nv_wr32(dev, NV10_PFB_TILE(i), 0);
+			nv_wr32(dev, NV10_PFB_TLIMIT(i), fb_bar_size);
+		}
+		break;
+	default:
+		for (i = 0; i < num_tiles; i++) {
+			nv_wr32(dev, NV40_PFB_TILE(i), 0);
+			nv_wr32(dev, NV40_PFB_TLIMIT(i), fb_bar_size);
+		}
+		break;
+	}
+
+	return 0;
+}
+
+void
+nv40_fb_takedown(struct drm_device *dev)
+{
+}
diff --git a/drivers/gpu/drm/nouveau/nv40_fifo.c b/drivers/gpu/drm/nouveau/nv40_fifo.c
new file mode 100644
index 0000000..b4f19cc
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv40_fifo.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2007 Ben Skeggs.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "drmP.h"
+#include "nouveau_drv.h"
+#include "nouveau_drm.h"
+
+#define NV40_RAMFC(c) (dev_priv->ramfc_offset + ((c) * NV40_RAMFC__SIZE))
+#define NV40_RAMFC__SIZE 128
+
+int
+nv40_fifo_create_context(struct nouveau_channel *chan)
+{
+	struct drm_device *dev = chan->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	uint32_t fc = NV40_RAMFC(chan->id);
+	int ret;
+
+	ret = nouveau_gpuobj_new_fake(dev, NV40_RAMFC(chan->id), ~0,
+				      NV40_RAMFC__SIZE, NVOBJ_FLAG_ZERO_ALLOC |
+				      NVOBJ_FLAG_ZERO_FREE, NULL, &chan->ramfc);
+	if (ret)
+		return ret;
+
+	dev_priv->engine.instmem.prepare_access(dev, true);
+	nv_wi32(dev, fc +  0, chan->pushbuf_base);
+	nv_wi32(dev, fc +  4, chan->pushbuf_base);
+	nv_wi32(dev, fc + 12, chan->pushbuf->instance >> 4);
+	nv_wi32(dev, fc + 24, NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES |
+			      NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES |
+			      NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8 |
+#ifdef __BIG_ENDIAN
+			      NV_PFIFO_CACHE1_BIG_ENDIAN |
+#endif
+			      0x30000000 /* no idea.. */);
+	nv_wi32(dev, fc + 56, chan->ramin_grctx->instance >> 4);
+	nv_wi32(dev, fc + 60, 0x0001FFFF);
+	dev_priv->engine.instmem.finish_access(dev);
+
+	/* enable the fifo dma operation */
+	nv_wr32(dev, NV04_PFIFO_MODE,
+		nv_rd32(dev, NV04_PFIFO_MODE) | (1 << chan->id));
+	return 0;
+}
+
+void
+nv40_fifo_destroy_context(struct nouveau_channel *chan)
+{
+	struct drm_device *dev = chan->dev;
+
+	nv_wr32(dev, NV04_PFIFO_MODE,
+		nv_rd32(dev, NV04_PFIFO_MODE) & ~(1 << chan->id));
+
+	if (chan->ramfc)
+		nouveau_gpuobj_ref_del(dev, &chan->ramfc);
+}
+
+static void
+nv40_fifo_do_load_context(struct drm_device *dev, int chid)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	uint32_t fc = NV40_RAMFC(chid), tmp, tmp2;
+
+	dev_priv->engine.instmem.prepare_access(dev, false);
+
+	nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUT, nv_ri32(dev, fc + 0));
+	nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_GET, nv_ri32(dev, fc + 4));
+	nv_wr32(dev, NV10_PFIFO_CACHE1_REF_CNT, nv_ri32(dev, fc + 8));
+	nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_INSTANCE, nv_ri32(dev, fc + 12));
+	nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_DCOUNT, nv_ri32(dev, fc + 16));
+	nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_STATE, nv_ri32(dev, fc + 20));
+
+	/* No idea what 0x2058 is.. */
+	tmp   = nv_ri32(dev, fc + 24);
+	tmp2  = nv_rd32(dev, 0x2058) & 0xFFF;
+	tmp2 |= (tmp & 0x30000000);
+	nv_wr32(dev, 0x2058, tmp2);
+	tmp  &= ~0x30000000;
+	nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_FETCH, tmp);
+
+	nv_wr32(dev, NV04_PFIFO_CACHE1_ENGINE, nv_ri32(dev, fc + 28));
+	nv_wr32(dev, NV04_PFIFO_CACHE1_PULL1, nv_ri32(dev, fc + 32));
+	nv_wr32(dev, NV10_PFIFO_CACHE1_ACQUIRE_VALUE, nv_ri32(dev, fc + 36));
+	tmp = nv_ri32(dev, fc + 40);
+	nv_wr32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMESTAMP, tmp);
+	nv_wr32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMEOUT, nv_ri32(dev, fc + 44));
+	nv_wr32(dev, NV10_PFIFO_CACHE1_SEMAPHORE, nv_ri32(dev, fc + 48));
+	nv_wr32(dev, NV10_PFIFO_CACHE1_DMA_SUBROUTINE, nv_ri32(dev, fc + 52));
+	nv_wr32(dev, NV40_PFIFO_GRCTX_INSTANCE, nv_ri32(dev, fc + 56));
+
+	/* Don't clobber the TIMEOUT_ENABLED flag when restoring from RAMFC */
+	tmp  = nv_rd32(dev, NV04_PFIFO_DMA_TIMESLICE) & ~0x1FFFF;
+	tmp |= nv_ri32(dev, fc + 60) & 0x1FFFF;
+	nv_wr32(dev, NV04_PFIFO_DMA_TIMESLICE, tmp);
+
+	nv_wr32(dev, 0x32e4, nv_ri32(dev, fc + 64));
+	/* NVIDIA does this next line twice... */
+	nv_wr32(dev, 0x32e8, nv_ri32(dev, fc + 68));
+	nv_wr32(dev, 0x2088, nv_ri32(dev, fc + 76));
+	nv_wr32(dev, 0x3300, nv_ri32(dev, fc + 80));
+
+	dev_priv->engine.instmem.finish_access(dev);
+
+	nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0);
+	nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, 0);
+}
+
+int
+nv40_fifo_load_context(struct nouveau_channel *chan)
+{
+	struct drm_device *dev = chan->dev;
+	uint32_t tmp;
+
+	nv40_fifo_do_load_context(dev, chan->id);
+
+	/* Set channel active, and in DMA mode */
+	nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1,
+		     NV40_PFIFO_CACHE1_PUSH1_DMA | chan->id);
+	nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 1);
+
+	/* Reset DMA_CTL_AT_INFO to INVALID */
+	tmp = nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_CTL) & ~(1 << 31);
+	nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_CTL, tmp);
+
+	return 0;
+}
+
+int
+nv40_fifo_unload_context(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
+	uint32_t fc, tmp;
+	int chid;
+
+	chid = pfifo->channel_id(dev);
+	if (chid < 0 || chid >= dev_priv->engine.fifo.channels)
+		return 0;
+	fc = NV40_RAMFC(chid);
+
+	dev_priv->engine.instmem.prepare_access(dev, true);
+	nv_wi32(dev, fc + 0, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUT));
+	nv_wi32(dev, fc + 4, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET));
+	nv_wi32(dev, fc + 8, nv_rd32(dev, NV10_PFIFO_CACHE1_REF_CNT));
+	nv_wi32(dev, fc + 12, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_INSTANCE));
+	nv_wi32(dev, fc + 16, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_DCOUNT));
+	nv_wi32(dev, fc + 20, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_STATE));
+	tmp  = nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_FETCH);
+	tmp |= nv_rd32(dev, 0x2058) & 0x30000000;
+	nv_wi32(dev, fc + 24, tmp);
+	nv_wi32(dev, fc + 28, nv_rd32(dev, NV04_PFIFO_CACHE1_ENGINE));
+	nv_wi32(dev, fc + 32, nv_rd32(dev, NV04_PFIFO_CACHE1_PULL1));
+	nv_wi32(dev, fc + 36, nv_rd32(dev, NV10_PFIFO_CACHE1_ACQUIRE_VALUE));
+	tmp = nv_rd32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMESTAMP);
+	nv_wi32(dev, fc + 40, tmp);
+	nv_wi32(dev, fc + 44, nv_rd32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMEOUT));
+	nv_wi32(dev, fc + 48, nv_rd32(dev, NV10_PFIFO_CACHE1_SEMAPHORE));
+	/* NVIDIA read 0x3228 first, then write DMA_GET here.. maybe something
+	 * more involved depending on the value of 0x3228?
+	 */
+	nv_wi32(dev, fc + 52, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET));
+	nv_wi32(dev, fc + 56, nv_rd32(dev, NV40_PFIFO_GRCTX_INSTANCE));
+	nv_wi32(dev, fc + 60, nv_rd32(dev, NV04_PFIFO_DMA_TIMESLICE) & 0x1ffff);
+	/* No idea what the below is for exactly, ripped from a mmio-trace */
+	nv_wi32(dev, fc + 64, nv_rd32(dev, NV40_PFIFO_UNK32E4));
+	/* NVIDIA do this next line twice.. bug? */
+	nv_wi32(dev, fc + 68, nv_rd32(dev, 0x32e8));
+	nv_wi32(dev, fc + 76, nv_rd32(dev, 0x2088));
+	nv_wi32(dev, fc + 80, nv_rd32(dev, 0x3300));
+#if 0 /* no real idea which is PUT/GET in UNK_48.. */
+	tmp  = nv_rd32(dev, NV04_PFIFO_CACHE1_GET);
+	tmp |= (nv_rd32(dev, NV04_PFIFO_CACHE1_PUT) << 16);
+	nv_wi32(dev, fc + 72, tmp);
+#endif
+	dev_priv->engine.instmem.finish_access(dev);
+
+	nv40_fifo_do_load_context(dev, pfifo->channels - 1);
+	nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1,
+		     NV40_PFIFO_CACHE1_PUSH1_DMA | (pfifo->channels - 1));
+	return 0;
+}
+
+static void
+nv40_fifo_init_reset(struct drm_device *dev)
+{
+	int i;
+
+	nv_wr32(dev, NV03_PMC_ENABLE,
+		nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PFIFO);
+	nv_wr32(dev, NV03_PMC_ENABLE,
+		nv_rd32(dev, NV03_PMC_ENABLE) |  NV_PMC_ENABLE_PFIFO);
+
+	nv_wr32(dev, 0x003224, 0x000f0078);
+	nv_wr32(dev, 0x003210, 0x00000000);
+	nv_wr32(dev, 0x003270, 0x00000000);
+	nv_wr32(dev, 0x003240, 0x00000000);
+	nv_wr32(dev, 0x003244, 0x00000000);
+	nv_wr32(dev, 0x003258, 0x00000000);
+	nv_wr32(dev, 0x002504, 0x00000000);
+	for (i = 0; i < 16; i++)
+		nv_wr32(dev, 0x002510 + (i * 4), 0x00000000);
+	nv_wr32(dev, 0x00250c, 0x0000ffff);
+	nv_wr32(dev, 0x002048, 0x00000000);
+	nv_wr32(dev, 0x003228, 0x00000000);
+	nv_wr32(dev, 0x0032e8, 0x00000000);
+	nv_wr32(dev, 0x002410, 0x00000000);
+	nv_wr32(dev, 0x002420, 0x00000000);
+	nv_wr32(dev, 0x002058, 0x00000001);
+	nv_wr32(dev, 0x00221c, 0x00000000);
+	/* something with 0x2084, read/modify/write, no change */
+	nv_wr32(dev, 0x002040, 0x000000ff);
+	nv_wr32(dev, 0x002500, 0x00000000);
+	nv_wr32(dev, 0x003200, 0x00000000);
+
+	nv_wr32(dev, NV04_PFIFO_DMA_TIMESLICE, 0x2101ffff);
+}
+
+static void
+nv40_fifo_init_ramxx(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	nv_wr32(dev, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ |
+				       ((dev_priv->ramht_bits - 9) << 16) |
+				       (dev_priv->ramht_offset >> 8));
+	nv_wr32(dev, NV03_PFIFO_RAMRO, dev_priv->ramro_offset>>8);
+
+	switch (dev_priv->chipset) {
+	case 0x47:
+	case 0x49:
+	case 0x4b:
+		nv_wr32(dev, 0x2230, 1);
+		break;
+	default:
+		break;
+	}
+
+	switch (dev_priv->chipset) {
+	case 0x40:
+	case 0x41:
+	case 0x42:
+	case 0x43:
+	case 0x45:
+	case 0x47:
+	case 0x48:
+	case 0x49:
+	case 0x4b:
+		nv_wr32(dev, NV40_PFIFO_RAMFC, 0x30002);
+		break;
+	default:
+		nv_wr32(dev, 0x2230, 0);
+		nv_wr32(dev, NV40_PFIFO_RAMFC,
+			((nouveau_mem_fb_amount(dev) - 512 * 1024 +
+			  dev_priv->ramfc_offset) >> 16) | (3 << 16));
+		break;
+	}
+}
+
+static void
+nv40_fifo_init_intr(struct drm_device *dev)
+{
+	nv_wr32(dev, 0x002100, 0xffffffff);
+	nv_wr32(dev, 0x002140, 0xffffffff);
+}
+
+int
+nv40_fifo_init(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
+	int i;
+
+	nv40_fifo_init_reset(dev);
+	nv40_fifo_init_ramxx(dev);
+
+	nv40_fifo_do_load_context(dev, pfifo->channels - 1);
+	nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, pfifo->channels - 1);
+
+	nv40_fifo_init_intr(dev);
+	pfifo->enable(dev);
+	pfifo->reassign(dev, true);
+
+	for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
+		if (dev_priv->fifos[i]) {
+			uint32_t mode = nv_rd32(dev, NV04_PFIFO_MODE);
+			nv_wr32(dev, NV04_PFIFO_MODE, mode | (1 << i));
+		}
+	}
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nv40_graph.c b/drivers/gpu/drm/nouveau/nv40_graph.c
new file mode 100644
index 0000000..d3e0a2a
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv40_graph.c
@@ -0,0 +1,560 @@
+/*
+ * Copyright (C) 2007 Ben Skeggs.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include <linux/firmware.h>
+
+#include "drmP.h"
+#include "drm.h"
+#include "nouveau_drv.h"
+
+MODULE_FIRMWARE("nouveau/nv40.ctxprog");
+MODULE_FIRMWARE("nouveau/nv40.ctxvals");
+MODULE_FIRMWARE("nouveau/nv41.ctxprog");
+MODULE_FIRMWARE("nouveau/nv41.ctxvals");
+MODULE_FIRMWARE("nouveau/nv42.ctxprog");
+MODULE_FIRMWARE("nouveau/nv42.ctxvals");
+MODULE_FIRMWARE("nouveau/nv43.ctxprog");
+MODULE_FIRMWARE("nouveau/nv43.ctxvals");
+MODULE_FIRMWARE("nouveau/nv44.ctxprog");
+MODULE_FIRMWARE("nouveau/nv44.ctxvals");
+MODULE_FIRMWARE("nouveau/nv46.ctxprog");
+MODULE_FIRMWARE("nouveau/nv46.ctxvals");
+MODULE_FIRMWARE("nouveau/nv47.ctxprog");
+MODULE_FIRMWARE("nouveau/nv47.ctxvals");
+MODULE_FIRMWARE("nouveau/nv49.ctxprog");
+MODULE_FIRMWARE("nouveau/nv49.ctxvals");
+MODULE_FIRMWARE("nouveau/nv4a.ctxprog");
+MODULE_FIRMWARE("nouveau/nv4a.ctxvals");
+MODULE_FIRMWARE("nouveau/nv4b.ctxprog");
+MODULE_FIRMWARE("nouveau/nv4b.ctxvals");
+MODULE_FIRMWARE("nouveau/nv4c.ctxprog");
+MODULE_FIRMWARE("nouveau/nv4c.ctxvals");
+MODULE_FIRMWARE("nouveau/nv4e.ctxprog");
+MODULE_FIRMWARE("nouveau/nv4e.ctxvals");
+
+struct nouveau_channel *
+nv40_graph_channel(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	uint32_t inst;
+	int i;
+
+	inst = nv_rd32(dev, NV40_PGRAPH_CTXCTL_CUR);
+	if (!(inst & NV40_PGRAPH_CTXCTL_CUR_LOADED))
+		return NULL;
+	inst = (inst & NV40_PGRAPH_CTXCTL_CUR_INSTANCE) << 4;
+
+	for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
+		struct nouveau_channel *chan = dev_priv->fifos[i];
+
+		if (chan && chan->ramin_grctx &&
+		    chan->ramin_grctx->instance == inst)
+			return chan;
+	}
+
+	return NULL;
+}
+
+int
+nv40_graph_create_context(struct nouveau_channel *chan)
+{
+	struct drm_device *dev = chan->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_gpuobj *ctx;
+	int ret;
+
+	/* Allocate a 175KiB block of PRAMIN to store the context.  This
+	 * is massive overkill for a lot of chipsets, but it should be safe
+	 * until we're able to implement this properly (will happen at more
+	 * or less the same time we're able to write our own context programs.
+	 */
+	ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, 175*1024, 16,
+					  NVOBJ_FLAG_ZERO_ALLOC,
+					  &chan->ramin_grctx);
+	if (ret)
+		return ret;
+	ctx = chan->ramin_grctx->gpuobj;
+
+	/* Initialise default context values */
+	dev_priv->engine.instmem.prepare_access(dev, true);
+	nv40_grctx_vals_load(dev, ctx);
+	nv_wo32(dev, ctx, 0, ctx->im_pramin->start);
+	dev_priv->engine.instmem.finish_access(dev);
+
+	return 0;
+}
+
+void
+nv40_graph_destroy_context(struct nouveau_channel *chan)
+{
+	nouveau_gpuobj_ref_del(chan->dev, &chan->ramin_grctx);
+}
+
+static int
+nv40_graph_transfer_context(struct drm_device *dev, uint32_t inst, int save)
+{
+	uint32_t old_cp, tv = 1000, tmp;
+	int i;
+
+	old_cp = nv_rd32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER);
+	nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst);
+
+	tmp  = nv_rd32(dev, NV40_PGRAPH_CTXCTL_0310);
+	tmp |= save ? NV40_PGRAPH_CTXCTL_0310_XFER_SAVE :
+		      NV40_PGRAPH_CTXCTL_0310_XFER_LOAD;
+	nv_wr32(dev, NV40_PGRAPH_CTXCTL_0310, tmp);
+
+	tmp  = nv_rd32(dev, NV40_PGRAPH_CTXCTL_0304);
+	tmp |= NV40_PGRAPH_CTXCTL_0304_XFER_CTX;
+	nv_wr32(dev, NV40_PGRAPH_CTXCTL_0304, tmp);
+
+	nouveau_wait_for_idle(dev);
+
+	for (i = 0; i < tv; i++) {
+		if (nv_rd32(dev, NV40_PGRAPH_CTXCTL_030C) == 0)
+			break;
+	}
+
+	nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, old_cp);
+
+	if (i == tv) {
+		uint32_t ucstat = nv_rd32(dev, NV40_PGRAPH_CTXCTL_UCODE_STAT);
+		NV_ERROR(dev, "Failed: Instance=0x%08x Save=%d\n", inst, save);
+		NV_ERROR(dev, "IP: 0x%02x, Opcode: 0x%08x\n",
+			 ucstat >> NV40_PGRAPH_CTXCTL_UCODE_STAT_IP_SHIFT,
+			 ucstat  & NV40_PGRAPH_CTXCTL_UCODE_STAT_OP_MASK);
+		NV_ERROR(dev, "0x40030C = 0x%08x\n",
+			 nv_rd32(dev, NV40_PGRAPH_CTXCTL_030C));
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+/* Restore the context for a specific channel into PGRAPH */
+int
+nv40_graph_load_context(struct nouveau_channel *chan)
+{
+	struct drm_device *dev = chan->dev;
+	uint32_t inst;
+	int ret;
+
+	if (!chan->ramin_grctx)
+		return -EINVAL;
+	inst = chan->ramin_grctx->instance >> 4;
+
+	ret = nv40_graph_transfer_context(dev, inst, 0);
+	if (ret)
+		return ret;
+
+	/* 0x40032C, no idea of it's exact function.  Could simply be a
+	 * record of the currently active PGRAPH context.  It's currently
+	 * unknown as to what bit 24 does.  The nv ddx has it set, so we will
+	 * set it here too.
+	 */
+	nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst);
+	nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR,
+		 (inst & NV40_PGRAPH_CTXCTL_CUR_INSTANCE) |
+		  NV40_PGRAPH_CTXCTL_CUR_LOADED);
+	/* 0x32E0 records the instance address of the active FIFO's PGRAPH
+	 * context.  If at any time this doesn't match 0x40032C, you will
+	 * recieve PGRAPH_INTR_CONTEXT_SWITCH
+	 */
+	nv_wr32(dev, NV40_PFIFO_GRCTX_INSTANCE, inst);
+	return 0;
+}
+
+int
+nv40_graph_unload_context(struct drm_device *dev)
+{
+	uint32_t inst;
+	int ret;
+
+	inst = nv_rd32(dev, NV40_PGRAPH_CTXCTL_CUR);
+	if (!(inst & NV40_PGRAPH_CTXCTL_CUR_LOADED))
+		return 0;
+	inst &= NV40_PGRAPH_CTXCTL_CUR_INSTANCE;
+
+	ret = nv40_graph_transfer_context(dev, inst, 1);
+
+	nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, inst);
+	return ret;
+}
+
+struct nouveau_ctxprog {
+	uint32_t signature;
+	uint8_t  version;
+	uint16_t length;
+	uint32_t data[];
+} __attribute__ ((packed));
+
+struct nouveau_ctxvals {
+	uint32_t signature;
+	uint8_t  version;
+	uint32_t length;
+	struct {
+		uint32_t offset;
+		uint32_t value;
+	} data[];
+} __attribute__ ((packed));
+
+int
+nv40_grctx_init(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
+	const int chipset = dev_priv->chipset;
+	const struct firmware *fw;
+	const struct nouveau_ctxprog *cp;
+	const struct nouveau_ctxvals *cv;
+	char name[32];
+	int ret, i;
+
+	pgraph->accel_blocked = true;
+
+	if (!pgraph->ctxprog) {
+		sprintf(name, "nouveau/nv%02x.ctxprog", chipset);
+		ret = request_firmware(&fw, name, &dev->pdev->dev);
+		if (ret) {
+			NV_ERROR(dev, "No ctxprog for NV%02x\n", chipset);
+			return ret;
+		}
+
+		pgraph->ctxprog = kmalloc(fw->size, GFP_KERNEL);
+		if (!pgraph->ctxprog) {
+			NV_ERROR(dev, "OOM copying ctxprog\n");
+			release_firmware(fw);
+			return -ENOMEM;
+		}
+		memcpy(pgraph->ctxprog, fw->data, fw->size);
+
+		cp = pgraph->ctxprog;
+		if (cp->signature != 0x5043564e || cp->version != 0 ||
+		    cp->length != ((fw->size - 7) / 4)) {
+			NV_ERROR(dev, "ctxprog invalid\n");
+			release_firmware(fw);
+			nv40_grctx_fini(dev);
+			return -EINVAL;
+		}
+		release_firmware(fw);
+	}
+
+	if (!pgraph->ctxvals) {
+		sprintf(name, "nouveau/nv%02x.ctxvals", chipset);
+		ret = request_firmware(&fw, name, &dev->pdev->dev);
+		if (ret) {
+			NV_ERROR(dev, "No ctxvals for NV%02x\n", chipset);
+			nv40_grctx_fini(dev);
+			return ret;
+		}
+
+		pgraph->ctxvals = kmalloc(fw->size, GFP_KERNEL);
+		if (!pgraph->ctxprog) {
+			NV_ERROR(dev, "OOM copying ctxprog\n");
+			release_firmware(fw);
+			nv40_grctx_fini(dev);
+			return -ENOMEM;
+		}
+		memcpy(pgraph->ctxvals, fw->data, fw->size);
+
+		cv = (void *)pgraph->ctxvals;
+		if (cv->signature != 0x5643564e || cv->version != 0 ||
+		    cv->length != ((fw->size - 9) / 8)) {
+			NV_ERROR(dev, "ctxvals invalid\n");
+			release_firmware(fw);
+			nv40_grctx_fini(dev);
+			return -EINVAL;
+		}
+		release_firmware(fw);
+	}
+
+	cp = pgraph->ctxprog;
+
+	nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_INDEX, 0);
+	for (i = 0; i < cp->length; i++)
+		nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_DATA, cp->data[i]);
+
+	pgraph->accel_blocked = false;
+	return 0;
+}
+
+void
+nv40_grctx_fini(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
+
+	if (pgraph->ctxprog) {
+		kfree(pgraph->ctxprog);
+		pgraph->ctxprog = NULL;
+	}
+
+	if (pgraph->ctxvals) {
+		kfree(pgraph->ctxprog);
+		pgraph->ctxvals = NULL;
+	}
+}
+
+void
+nv40_grctx_vals_load(struct drm_device *dev, struct nouveau_gpuobj *ctx)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
+	struct nouveau_ctxvals *cv = pgraph->ctxvals;
+	int i;
+
+	if (!cv)
+		return;
+
+	for (i = 0; i < cv->length; i++)
+		nv_wo32(dev, ctx, cv->data[i].offset, cv->data[i].value);
+}
+
+/*
+ * G70		0x47
+ * G71		0x49
+ * NV45		0x48
+ * G72[M]	0x46
+ * G73		0x4b
+ * C51_G7X	0x4c
+ * C51		0x4e
+ */
+int
+nv40_graph_init(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv =
+		(struct drm_nouveau_private *)dev->dev_private;
+	uint32_t vramsz, tmp;
+	int i, j;
+
+	nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) &
+			~NV_PMC_ENABLE_PGRAPH);
+	nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) |
+			 NV_PMC_ENABLE_PGRAPH);
+
+	nv40_grctx_init(dev);
+
+	/* No context present currently */
+	nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, 0x00000000);
+
+	nv_wr32(dev, NV03_PGRAPH_INTR   , 0xFFFFFFFF);
+	nv_wr32(dev, NV40_PGRAPH_INTR_EN, 0xFFFFFFFF);
+
+	nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0xFFFFFFFF);
+	nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x00000000);
+	nv_wr32(dev, NV04_PGRAPH_DEBUG_1, 0x401287c0);
+	nv_wr32(dev, NV04_PGRAPH_DEBUG_3, 0xe0de8055);
+	nv_wr32(dev, NV10_PGRAPH_DEBUG_4, 0x00008000);
+	nv_wr32(dev, NV04_PGRAPH_LIMIT_VIOL_PIX, 0x00be3c5f);
+
+	nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10010100);
+	nv_wr32(dev, NV10_PGRAPH_STATE      , 0xFFFFFFFF);
+
+	j = nv_rd32(dev, 0x1540) & 0xff;
+	if (j) {
+		for (i = 0; !(j & 1); j >>= 1, i++)
+			;
+		nv_wr32(dev, 0x405000, i);
+	}
+
+	if (dev_priv->chipset == 0x40) {
+		nv_wr32(dev, 0x4009b0, 0x83280fff);
+		nv_wr32(dev, 0x4009b4, 0x000000a0);
+	} else {
+		nv_wr32(dev, 0x400820, 0x83280eff);
+		nv_wr32(dev, 0x400824, 0x000000a0);
+	}
+
+	switch (dev_priv->chipset) {
+	case 0x40:
+	case 0x45:
+		nv_wr32(dev, 0x4009b8, 0x0078e366);
+		nv_wr32(dev, 0x4009bc, 0x0000014c);
+		break;
+	case 0x41:
+	case 0x42: /* pciid also 0x00Cx */
+	/* case 0x0120: XXX (pciid) */
+		nv_wr32(dev, 0x400828, 0x007596ff);
+		nv_wr32(dev, 0x40082c, 0x00000108);
+		break;
+	case 0x43:
+		nv_wr32(dev, 0x400828, 0x0072cb77);
+		nv_wr32(dev, 0x40082c, 0x00000108);
+		break;
+	case 0x44:
+	case 0x46: /* G72 */
+	case 0x4a:
+	case 0x4c: /* G7x-based C51 */
+	case 0x4e:
+		nv_wr32(dev, 0x400860, 0);
+		nv_wr32(dev, 0x400864, 0);
+		break;
+	case 0x47: /* G70 */
+	case 0x49: /* G71 */
+	case 0x4b: /* G73 */
+		nv_wr32(dev, 0x400828, 0x07830610);
+		nv_wr32(dev, 0x40082c, 0x0000016A);
+		break;
+	default:
+		break;
+	}
+
+	nv_wr32(dev, 0x400b38, 0x2ffff800);
+	nv_wr32(dev, 0x400b3c, 0x00006000);
+
+	/* copy tile info from PFB */
+	switch (dev_priv->chipset) {
+	case 0x40: /* vanilla NV40 */
+		for (i = 0; i < NV10_PFB_TILE__SIZE; i++) {
+			tmp = nv_rd32(dev, NV10_PFB_TILE(i));
+			nv_wr32(dev, NV40_PGRAPH_TILE0(i), tmp);
+			nv_wr32(dev, NV40_PGRAPH_TILE1(i), tmp);
+			tmp = nv_rd32(dev, NV10_PFB_TLIMIT(i));
+			nv_wr32(dev, NV40_PGRAPH_TLIMIT0(i), tmp);
+			nv_wr32(dev, NV40_PGRAPH_TLIMIT1(i), tmp);
+			tmp = nv_rd32(dev, NV10_PFB_TSIZE(i));
+			nv_wr32(dev, NV40_PGRAPH_TSIZE0(i), tmp);
+			nv_wr32(dev, NV40_PGRAPH_TSIZE1(i), tmp);
+			tmp = nv_rd32(dev, NV10_PFB_TSTATUS(i));
+			nv_wr32(dev, NV40_PGRAPH_TSTATUS0(i), tmp);
+			nv_wr32(dev, NV40_PGRAPH_TSTATUS1(i), tmp);
+		}
+		break;
+	case 0x44:
+	case 0x4a:
+	case 0x4e: /* NV44-based cores don't have 0x406900? */
+		for (i = 0; i < NV40_PFB_TILE__SIZE_0; i++) {
+			tmp = nv_rd32(dev, NV40_PFB_TILE(i));
+			nv_wr32(dev, NV40_PGRAPH_TILE0(i), tmp);
+			tmp = nv_rd32(dev, NV40_PFB_TLIMIT(i));
+			nv_wr32(dev, NV40_PGRAPH_TLIMIT0(i), tmp);
+			tmp = nv_rd32(dev, NV40_PFB_TSIZE(i));
+			nv_wr32(dev, NV40_PGRAPH_TSIZE0(i), tmp);
+			tmp = nv_rd32(dev, NV40_PFB_TSTATUS(i));
+			nv_wr32(dev, NV40_PGRAPH_TSTATUS0(i), tmp);
+		}
+		break;
+	case 0x46:
+	case 0x47:
+	case 0x49:
+	case 0x4b: /* G7X-based cores */
+		for (i = 0; i < NV40_PFB_TILE__SIZE_1; i++) {
+			tmp = nv_rd32(dev, NV40_PFB_TILE(i));
+			nv_wr32(dev, NV47_PGRAPH_TILE0(i), tmp);
+			nv_wr32(dev, NV40_PGRAPH_TILE1(i), tmp);
+			tmp = nv_rd32(dev, NV40_PFB_TLIMIT(i));
+			nv_wr32(dev, NV47_PGRAPH_TLIMIT0(i), tmp);
+			nv_wr32(dev, NV40_PGRAPH_TLIMIT1(i), tmp);
+			tmp = nv_rd32(dev, NV40_PFB_TSIZE(i));
+			nv_wr32(dev, NV47_PGRAPH_TSIZE0(i), tmp);
+			nv_wr32(dev, NV40_PGRAPH_TSIZE1(i), tmp);
+			tmp = nv_rd32(dev, NV40_PFB_TSTATUS(i));
+			nv_wr32(dev, NV47_PGRAPH_TSTATUS0(i), tmp);
+			nv_wr32(dev, NV40_PGRAPH_TSTATUS1(i), tmp);
+		}
+		break;
+	default: /* everything else */
+		for (i = 0; i < NV40_PFB_TILE__SIZE_0; i++) {
+			tmp = nv_rd32(dev, NV40_PFB_TILE(i));
+			nv_wr32(dev, NV40_PGRAPH_TILE0(i), tmp);
+			nv_wr32(dev, NV40_PGRAPH_TILE1(i), tmp);
+			tmp = nv_rd32(dev, NV40_PFB_TLIMIT(i));
+			nv_wr32(dev, NV40_PGRAPH_TLIMIT0(i), tmp);
+			nv_wr32(dev, NV40_PGRAPH_TLIMIT1(i), tmp);
+			tmp = nv_rd32(dev, NV40_PFB_TSIZE(i));
+			nv_wr32(dev, NV40_PGRAPH_TSIZE0(i), tmp);
+			nv_wr32(dev, NV40_PGRAPH_TSIZE1(i), tmp);
+			tmp = nv_rd32(dev, NV40_PFB_TSTATUS(i));
+			nv_wr32(dev, NV40_PGRAPH_TSTATUS0(i), tmp);
+			nv_wr32(dev, NV40_PGRAPH_TSTATUS1(i), tmp);
+		}
+		break;
+	}
+
+	/* begin RAM config */
+	vramsz = drm_get_resource_len(dev, 0) - 1;
+	switch (dev_priv->chipset) {
+	case 0x40:
+		nv_wr32(dev, 0x4009A4, nv_rd32(dev, NV04_PFB_CFG0));
+		nv_wr32(dev, 0x4009A8, nv_rd32(dev, NV04_PFB_CFG1));
+		nv_wr32(dev, 0x4069A4, nv_rd32(dev, NV04_PFB_CFG0));
+		nv_wr32(dev, 0x4069A8, nv_rd32(dev, NV04_PFB_CFG1));
+		nv_wr32(dev, 0x400820, 0);
+		nv_wr32(dev, 0x400824, 0);
+		nv_wr32(dev, 0x400864, vramsz);
+		nv_wr32(dev, 0x400868, vramsz);
+		break;
+	default:
+		switch (dev_priv->chipset) {
+		case 0x46:
+		case 0x47:
+		case 0x49:
+		case 0x4b:
+			nv_wr32(dev, 0x400DF0, nv_rd32(dev, NV04_PFB_CFG0));
+			nv_wr32(dev, 0x400DF4, nv_rd32(dev, NV04_PFB_CFG1));
+			break;
+		default:
+			nv_wr32(dev, 0x4009F0, nv_rd32(dev, NV04_PFB_CFG0));
+			nv_wr32(dev, 0x4009F4, nv_rd32(dev, NV04_PFB_CFG1));
+			break;
+		}
+		nv_wr32(dev, 0x4069F0, nv_rd32(dev, NV04_PFB_CFG0));
+		nv_wr32(dev, 0x4069F4, nv_rd32(dev, NV04_PFB_CFG1));
+		nv_wr32(dev, 0x400840, 0);
+		nv_wr32(dev, 0x400844, 0);
+		nv_wr32(dev, 0x4008A0, vramsz);
+		nv_wr32(dev, 0x4008A4, vramsz);
+		break;
+	}
+
+	return 0;
+}
+
+void nv40_graph_takedown(struct drm_device *dev)
+{
+}
+
+struct nouveau_pgraph_object_class nv40_graph_grclass[] = {
+	{ 0x0030, false, NULL }, /* null */
+	{ 0x0039, false, NULL }, /* m2mf */
+	{ 0x004a, false, NULL }, /* gdirect */
+	{ 0x009f, false, NULL }, /* imageblit (nv12) */
+	{ 0x008a, false, NULL }, /* ifc */
+	{ 0x0089, false, NULL }, /* sifm */
+	{ 0x3089, false, NULL }, /* sifm (nv40) */
+	{ 0x0062, false, NULL }, /* surf2d */
+	{ 0x3062, false, NULL }, /* surf2d (nv40) */
+	{ 0x0043, false, NULL }, /* rop */
+	{ 0x0012, false, NULL }, /* beta1 */
+	{ 0x0072, false, NULL }, /* beta4 */
+	{ 0x0019, false, NULL }, /* cliprect */
+	{ 0x0044, false, NULL }, /* pattern */
+	{ 0x309e, false, NULL }, /* swzsurf */
+	{ 0x4097, false, NULL }, /* curie (nv40) */
+	{ 0x4497, false, NULL }, /* curie (nv44) */
+	{}
+};
+
diff --git a/drivers/gpu/drm/nouveau/nv40_mc.c b/drivers/gpu/drm/nouveau/nv40_mc.c
new file mode 100644
index 0000000..2a3495e
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv40_mc.c
@@ -0,0 +1,38 @@
+#include "drmP.h"
+#include "drm.h"
+#include "nouveau_drv.h"
+#include "nouveau_drm.h"
+
+int
+nv40_mc_init(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	uint32_t tmp;
+
+	/* Power up everything, resetting each individual unit will
+	 * be done later if needed.
+	 */
+	nv_wr32(dev, NV03_PMC_ENABLE, 0xFFFFFFFF);
+
+	switch (dev_priv->chipset) {
+	case 0x44:
+	case 0x46: /* G72 */
+	case 0x4e:
+	case 0x4c: /* C51_G7X */
+		tmp = nv_rd32(dev, NV40_PFB_020C);
+		nv_wr32(dev, NV40_PMC_1700, tmp);
+		nv_wr32(dev, NV40_PMC_1704, 0);
+		nv_wr32(dev, NV40_PMC_1708, 0);
+		nv_wr32(dev, NV40_PMC_170C, tmp);
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+void
+nv40_mc_takedown(struct drm_device *dev)
+{
+}
diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c
new file mode 100644
index 0000000..f8e28a1
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv50_crtc.c
@@ -0,0 +1,769 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "drmP.h"
+#include "drm_mode.h"
+#include "drm_crtc_helper.h"
+
+#define NOUVEAU_DMA_DEBUG (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO)
+#include "nouveau_reg.h"
+#include "nouveau_drv.h"
+#include "nouveau_hw.h"
+#include "nouveau_encoder.h"
+#include "nouveau_crtc.h"
+#include "nouveau_fb.h"
+#include "nouveau_connector.h"
+#include "nv50_display.h"
+
+static void
+nv50_crtc_lut_load(struct drm_crtc *crtc)
+{
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+	void __iomem *lut = nvbo_kmap_obj_iovirtual(nv_crtc->lut.nvbo);
+	int i;
+
+	NV_DEBUG(crtc->dev, "\n");
+
+	for (i = 0; i < 256; i++) {
+		writew(nv_crtc->lut.r[i] >> 2, lut + 8*i + 0);
+		writew(nv_crtc->lut.g[i] >> 2, lut + 8*i + 2);
+		writew(nv_crtc->lut.b[i] >> 2, lut + 8*i + 4);
+	}
+
+	if (nv_crtc->lut.depth == 30) {
+		writew(nv_crtc->lut.r[i - 1] >> 2, lut + 8*i + 0);
+		writew(nv_crtc->lut.g[i - 1] >> 2, lut + 8*i + 2);
+		writew(nv_crtc->lut.b[i - 1] >> 2, lut + 8*i + 4);
+	}
+}
+
+int
+nv50_crtc_blank(struct nouveau_crtc *nv_crtc, bool blanked)
+{
+	struct drm_device *dev = nv_crtc->base.dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_channel *evo = dev_priv->evo;
+	int index = nv_crtc->index, ret;
+
+	NV_DEBUG(dev, "index %d\n", nv_crtc->index);
+	NV_DEBUG(dev, "%s\n", blanked ? "blanked" : "unblanked");
+
+	if (blanked) {
+		nv_crtc->cursor.hide(nv_crtc, false);
+
+		ret = RING_SPACE(evo, dev_priv->chipset != 0x50 ? 7 : 5);
+		if (ret) {
+			NV_ERROR(dev, "no space while blanking crtc\n");
+			return ret;
+		}
+		BEGIN_RING(evo, 0, NV50_EVO_CRTC(index, CLUT_MODE), 2);
+		OUT_RING(evo, NV50_EVO_CRTC_CLUT_MODE_BLANK);
+		OUT_RING(evo, 0);
+		if (dev_priv->chipset != 0x50) {
+			BEGIN_RING(evo, 0, NV84_EVO_CRTC(index, CLUT_DMA), 1);
+			OUT_RING(evo, NV84_EVO_CRTC_CLUT_DMA_HANDLE_NONE);
+		}
+
+		BEGIN_RING(evo, 0, NV50_EVO_CRTC(index, FB_DMA), 1);
+		OUT_RING(evo, NV50_EVO_CRTC_FB_DMA_HANDLE_NONE);
+	} else {
+		if (nv_crtc->cursor.visible)
+			nv_crtc->cursor.show(nv_crtc, false);
+		else
+			nv_crtc->cursor.hide(nv_crtc, false);
+
+		ret = RING_SPACE(evo, dev_priv->chipset != 0x50 ? 10 : 8);
+		if (ret) {
+			NV_ERROR(dev, "no space while unblanking crtc\n");
+			return ret;
+		}
+		BEGIN_RING(evo, 0, NV50_EVO_CRTC(index, CLUT_MODE), 2);
+		OUT_RING(evo, nv_crtc->lut.depth == 8 ?
+				NV50_EVO_CRTC_CLUT_MODE_OFF :
+				NV50_EVO_CRTC_CLUT_MODE_ON);
+		OUT_RING(evo, (nv_crtc->lut.nvbo->bo.mem.mm_node->start <<
+				 PAGE_SHIFT) >> 8);
+		if (dev_priv->chipset != 0x50) {
+			BEGIN_RING(evo, 0, NV84_EVO_CRTC(index, CLUT_DMA), 1);
+			OUT_RING(evo, NvEvoVRAM);
+		}
+
+		BEGIN_RING(evo, 0, NV50_EVO_CRTC(index, FB_OFFSET), 2);
+		OUT_RING(evo, nv_crtc->fb.offset >> 8);
+		OUT_RING(evo, 0);
+		BEGIN_RING(evo, 0, NV50_EVO_CRTC(index, FB_DMA), 1);
+		if (dev_priv->chipset != 0x50)
+			if (nv_crtc->fb.tile_flags == 0x7a00)
+				OUT_RING(evo, NvEvoFB32);
+			else
+			if (nv_crtc->fb.tile_flags == 0x7000)
+				OUT_RING(evo, NvEvoFB16);
+			else
+				OUT_RING(evo, NvEvoVRAM);
+		else
+			OUT_RING(evo, NvEvoVRAM);
+	}
+
+	nv_crtc->fb.blanked = blanked;
+	return 0;
+}
+
+static int
+nv50_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool on, bool update)
+{
+	struct drm_device *dev = nv_crtc->base.dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_channel *evo = dev_priv->evo;
+	int ret;
+
+	NV_DEBUG(dev, "\n");
+
+	ret = RING_SPACE(evo, 2 + (update ? 2 : 0));
+	if (ret) {
+		NV_ERROR(dev, "no space while setting dither\n");
+		return ret;
+	}
+
+	BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, DITHER_CTRL), 1);
+	if (on)
+		OUT_RING(evo, NV50_EVO_CRTC_DITHER_CTRL_ON);
+	else
+		OUT_RING(evo, NV50_EVO_CRTC_DITHER_CTRL_OFF);
+
+	if (update) {
+		BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
+		OUT_RING(evo, 0);
+		FIRE_RING(evo);
+	}
+
+	return 0;
+}
+
+struct nouveau_connector *
+nouveau_crtc_connector_get(struct nouveau_crtc *nv_crtc)
+{
+	struct drm_device *dev = nv_crtc->base.dev;
+	struct drm_connector *connector;
+	struct drm_crtc *crtc = to_drm_crtc(nv_crtc);
+
+	/* The safest approach is to find an encoder with the right crtc, that
+	 * is also linked to a connector. */
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+		if (connector->encoder)
+			if (connector->encoder->crtc == crtc)
+				return nouveau_connector(connector);
+	}
+
+	return NULL;
+}
+
+static int
+nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, int scaling_mode, bool update)
+{
+	struct nouveau_connector *nv_connector =
+		nouveau_crtc_connector_get(nv_crtc);
+	struct drm_device *dev = nv_crtc->base.dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_channel *evo = dev_priv->evo;
+	struct drm_display_mode *native_mode = NULL;
+	struct drm_display_mode *mode = &nv_crtc->base.mode;
+	uint32_t outX, outY, horiz, vert;
+	int ret;
+
+	NV_DEBUG(dev, "\n");
+
+	switch (scaling_mode) {
+	case DRM_MODE_SCALE_NONE:
+		break;
+	default:
+		if (!nv_connector || !nv_connector->native_mode) {
+			NV_ERROR(dev, "No native mode, forcing panel scaling\n");
+			scaling_mode = DRM_MODE_SCALE_NONE;
+		} else {
+			native_mode = nv_connector->native_mode;
+		}
+		break;
+	}
+
+	switch (scaling_mode) {
+	case DRM_MODE_SCALE_ASPECT:
+		horiz = (native_mode->hdisplay << 19) / mode->hdisplay;
+		vert = (native_mode->vdisplay << 19) / mode->vdisplay;
+
+		if (vert > horiz) {
+			outX = (mode->hdisplay * horiz) >> 19;
+			outY = (mode->vdisplay * horiz) >> 19;
+		} else {
+			outX = (mode->hdisplay * vert) >> 19;
+			outY = (mode->vdisplay * vert) >> 19;
+		}
+		break;
+	case DRM_MODE_SCALE_FULLSCREEN:
+		outX = native_mode->hdisplay;
+		outY = native_mode->vdisplay;
+		break;
+	case DRM_MODE_SCALE_CENTER:
+	case DRM_MODE_SCALE_NONE:
+	default:
+		outX = mode->hdisplay;
+		outY = mode->vdisplay;
+		break;
+	}
+
+	ret = RING_SPACE(evo, update ? 7 : 5);
+	if (ret)
+		return ret;
+
+	/* Got a better name for SCALER_ACTIVE? */
+	/* One day i've got to really figure out why this is needed. */
+	BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_CTRL), 1);
+	if ((mode->flags & DRM_MODE_FLAG_DBLSCAN) ||
+	    (mode->flags & DRM_MODE_FLAG_INTERLACE) ||
+	    mode->hdisplay != outX || mode->vdisplay != outY) {
+		OUT_RING(evo, NV50_EVO_CRTC_SCALE_CTRL_ACTIVE);
+	} else {
+		OUT_RING(evo, NV50_EVO_CRTC_SCALE_CTRL_INACTIVE);
+	}
+
+	BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_RES1), 2);
+	OUT_RING(evo, outY << 16 | outX);
+	OUT_RING(evo, outY << 16 | outX);
+
+	if (update) {
+		BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
+		OUT_RING(evo, 0);
+		FIRE_RING(evo);
+	}
+
+	return 0;
+}
+
+int
+nv50_crtc_set_clock(struct drm_device *dev, int head, int pclk)
+{
+	uint32_t pll_reg = NV50_PDISPLAY_CRTC_CLK_CTRL1(head);
+	struct nouveau_pll_vals pll;
+	struct pll_lims limits;
+	uint32_t reg1, reg2;
+	int ret;
+
+	ret = get_pll_limits(dev, pll_reg, &limits);
+	if (ret)
+		return ret;
+
+	ret = nouveau_calc_pll_mnp(dev, &limits, pclk, &pll);
+	if (ret <= 0)
+		return ret;
+
+	if (limits.vco2.maxfreq) {
+		reg1 = nv_rd32(dev, pll_reg + 4) & 0xff00ff00;
+		reg2 = nv_rd32(dev, pll_reg + 8) & 0x8000ff00;
+		nv_wr32(dev, pll_reg, 0x10000611);
+		nv_wr32(dev, pll_reg + 4, reg1 | (pll.M1 << 16) | pll.N1);
+		nv_wr32(dev, pll_reg + 8,
+			reg2 | (pll.log2P << 28) | (pll.M2 << 16) | pll.N2);
+	} else {
+		reg1 = nv_rd32(dev, pll_reg + 4) & 0xffc00000;
+		nv_wr32(dev, pll_reg, 0x50000610);
+		nv_wr32(dev, pll_reg + 4, reg1 |
+			(pll.log2P << 16) | (pll.M1 << 8) | pll.N1);
+	}
+
+	return 0;
+}
+
+static void
+nv50_crtc_destroy(struct drm_crtc *crtc)
+{
+	struct drm_device *dev = crtc->dev;
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+
+	NV_DEBUG(dev, "\n");
+
+	if (!crtc)
+		return;
+
+	drm_crtc_cleanup(&nv_crtc->base);
+
+	nv50_cursor_fini(nv_crtc);
+
+	nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo);
+	nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo);
+	kfree(nv_crtc->mode);
+	kfree(nv_crtc);
+}
+
+int
+nv50_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
+		     uint32_t buffer_handle, uint32_t width, uint32_t height)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+	struct nouveau_bo *cursor = NULL;
+	struct drm_gem_object *gem;
+	int ret = 0, i;
+
+	if (width != 64 || height != 64)
+		return -EINVAL;
+
+	if (!buffer_handle) {
+		nv_crtc->cursor.hide(nv_crtc, true);
+		return 0;
+	}
+
+	gem = drm_gem_object_lookup(dev, file_priv, buffer_handle);
+	if (!gem)
+		return -EINVAL;
+	cursor = nouveau_gem_object(gem);
+
+	ret = nouveau_bo_map(cursor);
+	if (ret)
+		goto out;
+
+	/* The simple will do for now. */
+	for (i = 0; i < 64 * 64; i++)
+		nouveau_bo_wr32(nv_crtc->cursor.nvbo, i, nouveau_bo_rd32(cursor, i));
+
+	nouveau_bo_unmap(cursor);
+
+	nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.nvbo->bo.offset -
+					    dev_priv->vm_vram_base);
+	nv_crtc->cursor.show(nv_crtc, true);
+
+out:
+	mutex_lock(&dev->struct_mutex);
+	drm_gem_object_unreference(gem);
+	mutex_unlock(&dev->struct_mutex);
+	return ret;
+}
+
+int
+nv50_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
+{
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+
+	nv_crtc->cursor.set_pos(nv_crtc, x, y);
+	return 0;
+}
+
+static void
+nv50_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b,
+		    uint32_t size)
+{
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+	int i;
+
+	if (size != 256)
+		return;
+
+	for (i = 0; i < 256; i++) {
+		nv_crtc->lut.r[i] = r[i];
+		nv_crtc->lut.g[i] = g[i];
+		nv_crtc->lut.b[i] = b[i];
+	}
+
+	/* We need to know the depth before we upload, but it's possible to
+	 * get called before a framebuffer is bound.  If this is the case,
+	 * mark the lut values as dirty by setting depth==0, and it'll be
+	 * uploaded on the first mode_set_base()
+	 */
+	if (!nv_crtc->base.fb) {
+		nv_crtc->lut.depth = 0;
+		return;
+	}
+
+	nv50_crtc_lut_load(crtc);
+}
+
+static void
+nv50_crtc_save(struct drm_crtc *crtc)
+{
+	NV_ERROR(crtc->dev, "!!\n");
+}
+
+static void
+nv50_crtc_restore(struct drm_crtc *crtc)
+{
+	NV_ERROR(crtc->dev, "!!\n");
+}
+
+static const struct drm_crtc_funcs nv50_crtc_funcs = {
+	.save = nv50_crtc_save,
+	.restore = nv50_crtc_restore,
+	.cursor_set = nv50_crtc_cursor_set,
+	.cursor_move = nv50_crtc_cursor_move,
+	.gamma_set = nv50_crtc_gamma_set,
+	.set_config = drm_crtc_helper_set_config,
+	.destroy = nv50_crtc_destroy,
+};
+
+static void
+nv50_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+}
+
+static void
+nv50_crtc_prepare(struct drm_crtc *crtc)
+{
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+	struct drm_device *dev = crtc->dev;
+	struct drm_encoder *encoder;
+
+	NV_DEBUG(dev, "index %d\n", nv_crtc->index);
+
+	/* Disconnect all unused encoders. */
+	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+		struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+
+		if (drm_helper_encoder_in_use(encoder))
+			continue;
+
+		nv_encoder->disconnect(nv_encoder);
+	}
+
+	nv50_crtc_blank(nv_crtc, true);
+}
+
+static void
+nv50_crtc_commit(struct drm_crtc *crtc)
+{
+	struct drm_crtc *crtc2;
+	struct drm_device *dev = crtc->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_channel *evo = dev_priv->evo;
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+	int ret;
+
+	NV_DEBUG(dev, "index %d\n", nv_crtc->index);
+
+	nv50_crtc_blank(nv_crtc, false);
+
+	/* Explicitly blank all unused crtc's. */
+	list_for_each_entry(crtc2, &dev->mode_config.crtc_list, head) {
+		if (!drm_helper_crtc_in_use(crtc2))
+			nv50_crtc_blank(nouveau_crtc(crtc2), true);
+	}
+
+	ret = RING_SPACE(evo, 2);
+	if (ret) {
+		NV_ERROR(dev, "no space while committing crtc\n");
+		return;
+	}
+	BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
+	OUT_RING(evo, 0);
+	FIRE_RING(evo);
+}
+
+static bool
+nv50_crtc_mode_fixup(struct drm_crtc *crtc, struct drm_display_mode *mode,
+		     struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static int
+nv50_crtc_do_mode_set_base(struct drm_crtc *crtc, int x, int y,
+			   struct drm_framebuffer *old_fb, bool update)
+{
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+	struct drm_device *dev = nv_crtc->base.dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_channel *evo = dev_priv->evo;
+	struct drm_framebuffer *drm_fb = nv_crtc->base.fb;
+	struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb);
+	int ret, format;
+
+	NV_DEBUG(dev, "index %d\n", nv_crtc->index);
+
+	switch (drm_fb->depth) {
+	case  8:
+		format = NV50_EVO_CRTC_FB_DEPTH_8;
+		break;
+	case 15:
+		format = NV50_EVO_CRTC_FB_DEPTH_15;
+		break;
+	case 16:
+		format = NV50_EVO_CRTC_FB_DEPTH_16;
+		break;
+	case 24:
+	case 32:
+		format = NV50_EVO_CRTC_FB_DEPTH_24;
+		break;
+	case 30:
+		format = NV50_EVO_CRTC_FB_DEPTH_30;
+		break;
+	default:
+		 NV_ERROR(dev, "unknown depth %d\n", drm_fb->depth);
+		 return -EINVAL;
+	}
+
+	ret = nouveau_bo_pin(fb->nvbo, TTM_PL_FLAG_VRAM);
+	if (ret)
+		return ret;
+
+	if (old_fb) {
+		struct nouveau_framebuffer *ofb = nouveau_framebuffer(old_fb);
+		nouveau_bo_unpin(ofb->nvbo);
+	}
+
+	nv_crtc->fb.offset = fb->nvbo->bo.offset - dev_priv->vm_vram_base;
+	nv_crtc->fb.tile_flags = fb->nvbo->tile_flags;
+	nv_crtc->fb.cpp = drm_fb->bits_per_pixel / 8;
+	if (!nv_crtc->fb.blanked && dev_priv->chipset != 0x50) {
+		ret = RING_SPACE(evo, 2);
+		if (ret)
+			return ret;
+
+		BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, FB_DMA), 1);
+		if (nv_crtc->fb.tile_flags == 0x7a00)
+			OUT_RING(evo, NvEvoFB32);
+		else
+		if (nv_crtc->fb.tile_flags == 0x7000)
+			OUT_RING(evo, NvEvoFB16);
+		else
+			OUT_RING(evo, NvEvoVRAM);
+	}
+
+	ret = RING_SPACE(evo, 12);
+	if (ret)
+		return ret;
+
+	BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, FB_OFFSET), 5);
+	OUT_RING(evo, nv_crtc->fb.offset >> 8);
+	OUT_RING(evo, 0);
+	OUT_RING(evo, (drm_fb->height << 16) | drm_fb->width);
+	if (!nv_crtc->fb.tile_flags) {
+		OUT_RING(evo, drm_fb->pitch | (1 << 20));
+	} else {
+		OUT_RING(evo, ((drm_fb->pitch / 4) << 4) |
+				  fb->nvbo->tile_mode);
+	}
+	if (dev_priv->chipset == 0x50)
+		OUT_RING(evo, (fb->nvbo->tile_flags << 8) | format);
+	else
+		OUT_RING(evo, format);
+
+	BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, CLUT_MODE), 1);
+	OUT_RING(evo, fb->base.depth == 8 ?
+		 NV50_EVO_CRTC_CLUT_MODE_OFF : NV50_EVO_CRTC_CLUT_MODE_ON);
+
+	BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, COLOR_CTRL), 1);
+	OUT_RING(evo, NV50_EVO_CRTC_COLOR_CTRL_COLOR);
+	BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, FB_POS), 1);
+	OUT_RING(evo, (y << 16) | x);
+
+	if (nv_crtc->lut.depth != fb->base.depth) {
+		nv_crtc->lut.depth = fb->base.depth;
+		nv50_crtc_lut_load(crtc);
+	}
+
+	if (update) {
+		ret = RING_SPACE(evo, 2);
+		if (ret)
+			return ret;
+		BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
+		OUT_RING(evo, 0);
+		FIRE_RING(evo);
+	}
+
+	return 0;
+}
+
+static int
+nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
+		   struct drm_display_mode *adjusted_mode, int x, int y,
+		   struct drm_framebuffer *old_fb)
+{
+	struct drm_device *dev = crtc->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_channel *evo = dev_priv->evo;
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+	struct nouveau_connector *nv_connector = NULL;
+	uint32_t hsync_dur,  vsync_dur, hsync_start_to_end, vsync_start_to_end;
+	uint32_t hunk1, vunk1, vunk2a, vunk2b;
+	int ret;
+
+	/* Find the connector attached to this CRTC */
+	nv_connector = nouveau_crtc_connector_get(nv_crtc);
+
+	*nv_crtc->mode = *adjusted_mode;
+
+	NV_DEBUG(dev, "index %d\n", nv_crtc->index);
+
+	hsync_dur = adjusted_mode->hsync_end - adjusted_mode->hsync_start;
+	vsync_dur = adjusted_mode->vsync_end - adjusted_mode->vsync_start;
+	hsync_start_to_end = adjusted_mode->htotal - adjusted_mode->hsync_start;
+	vsync_start_to_end = adjusted_mode->vtotal - adjusted_mode->vsync_start;
+	/* I can't give this a proper name, anyone else can? */
+	hunk1 = adjusted_mode->htotal -
+		adjusted_mode->hsync_start + adjusted_mode->hdisplay;
+	vunk1 = adjusted_mode->vtotal -
+		adjusted_mode->vsync_start + adjusted_mode->vdisplay;
+	/* Another strange value, this time only for interlaced adjusted_modes. */
+	vunk2a = 2 * adjusted_mode->vtotal -
+		 adjusted_mode->vsync_start + adjusted_mode->vdisplay;
+	vunk2b = adjusted_mode->vtotal -
+		 adjusted_mode->vsync_start + adjusted_mode->vtotal;
+
+	if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) {
+		vsync_dur /= 2;
+		vsync_start_to_end  /= 2;
+		vunk1 /= 2;
+		vunk2a /= 2;
+		vunk2b /= 2;
+		/* magic */
+		if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) {
+			vsync_start_to_end -= 1;
+			vunk1 -= 1;
+			vunk2a -= 1;
+			vunk2b -= 1;
+		}
+	}
+
+	ret = RING_SPACE(evo, 17);
+	if (ret)
+		return ret;
+
+	BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, CLOCK), 2);
+	OUT_RING(evo, adjusted_mode->clock | 0x800000);
+	OUT_RING(evo, (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) ? 2 : 0);
+
+	BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, DISPLAY_START), 5);
+	OUT_RING(evo, 0);
+	OUT_RING(evo, (adjusted_mode->vtotal << 16) | adjusted_mode->htotal);
+	OUT_RING(evo, (vsync_dur - 1) << 16 | (hsync_dur - 1));
+	OUT_RING(evo, (vsync_start_to_end - 1) << 16 |
+			(hsync_start_to_end - 1));
+	OUT_RING(evo, (vunk1 - 1) << 16 | (hunk1 - 1));
+
+	if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) {
+		BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, UNK0824), 1);
+		OUT_RING(evo, (vunk2b - 1) << 16 | (vunk2a - 1));
+	} else {
+		OUT_RING(evo, 0);
+		OUT_RING(evo, 0);
+	}
+
+	BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, UNK082C), 1);
+	OUT_RING(evo, 0);
+
+	/* This is the actual resolution of the mode. */
+	BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, REAL_RES), 1);
+	OUT_RING(evo, (mode->vdisplay << 16) | mode->hdisplay);
+	BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_CENTER_OFFSET), 1);
+	OUT_RING(evo, NV50_EVO_CRTC_SCALE_CENTER_OFFSET_VAL(0, 0));
+
+	nv_crtc->set_dither(nv_crtc, nv_connector->use_dithering, false);
+	nv_crtc->set_scale(nv_crtc, nv_connector->scaling_mode, false);
+
+	return nv50_crtc_do_mode_set_base(crtc, x, y, old_fb, false);
+}
+
+static int
+nv50_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+			struct drm_framebuffer *old_fb)
+{
+	return nv50_crtc_do_mode_set_base(crtc, x, y, old_fb, true);
+}
+
+static const struct drm_crtc_helper_funcs nv50_crtc_helper_funcs = {
+	.dpms = nv50_crtc_dpms,
+	.prepare = nv50_crtc_prepare,
+	.commit = nv50_crtc_commit,
+	.mode_fixup = nv50_crtc_mode_fixup,
+	.mode_set = nv50_crtc_mode_set,
+	.mode_set_base = nv50_crtc_mode_set_base,
+	.load_lut = nv50_crtc_lut_load,
+};
+
+int
+nv50_crtc_create(struct drm_device *dev, int index)
+{
+	struct nouveau_crtc *nv_crtc = NULL;
+	int ret, i;
+
+	NV_DEBUG(dev, "\n");
+
+	nv_crtc = kzalloc(sizeof(*nv_crtc), GFP_KERNEL);
+	if (!nv_crtc)
+		return -ENOMEM;
+
+	nv_crtc->mode = kzalloc(sizeof(*nv_crtc->mode), GFP_KERNEL);
+	if (!nv_crtc->mode) {
+		kfree(nv_crtc);
+		return -ENOMEM;
+	}
+
+	/* Default CLUT parameters, will be activated on the hw upon
+	 * first mode set.
+	 */
+	for (i = 0; i < 256; i++) {
+		nv_crtc->lut.r[i] = i << 8;
+		nv_crtc->lut.g[i] = i << 8;
+		nv_crtc->lut.b[i] = i << 8;
+	}
+	nv_crtc->lut.depth = 0;
+
+	ret = nouveau_bo_new(dev, NULL, 4096, 0x100, TTM_PL_FLAG_VRAM,
+			     0, 0x0000, false, true, &nv_crtc->lut.nvbo);
+	if (!ret) {
+		ret = nouveau_bo_pin(nv_crtc->lut.nvbo, TTM_PL_FLAG_VRAM);
+		if (!ret)
+			ret = nouveau_bo_map(nv_crtc->lut.nvbo);
+		if (ret)
+			nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo);
+	}
+
+	if (ret) {
+		kfree(nv_crtc->mode);
+		kfree(nv_crtc);
+		return ret;
+	}
+
+	nv_crtc->index = index;
+
+	/* set function pointers */
+	nv_crtc->set_dither = nv50_crtc_set_dither;
+	nv_crtc->set_scale = nv50_crtc_set_scale;
+
+	drm_crtc_init(dev, &nv_crtc->base, &nv50_crtc_funcs);
+	drm_crtc_helper_add(&nv_crtc->base, &nv50_crtc_helper_funcs);
+	drm_mode_crtc_set_gamma_size(&nv_crtc->base, 256);
+
+	ret = nouveau_bo_new(dev, NULL, 64*64*4, 0x100, TTM_PL_FLAG_VRAM,
+			     0, 0x0000, false, true, &nv_crtc->cursor.nvbo);
+	if (!ret) {
+		ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM);
+		if (!ret)
+			ret = nouveau_bo_map(nv_crtc->cursor.nvbo);
+		if (ret)
+			nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo);
+	}
+
+	nv50_cursor_init(nv_crtc);
+	return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nv50_cursor.c b/drivers/gpu/drm/nouveau/nv50_cursor.c
new file mode 100644
index 0000000..e2e79a8
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv50_cursor.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "drmP.h"
+#include "drm_mode.h"
+
+#define NOUVEAU_DMA_DEBUG (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO)
+#include "nouveau_reg.h"
+#include "nouveau_drv.h"
+#include "nouveau_crtc.h"
+#include "nv50_display.h"
+
+static void
+nv50_cursor_show(struct nouveau_crtc *nv_crtc, bool update)
+{
+	struct drm_nouveau_private *dev_priv = nv_crtc->base.dev->dev_private;
+	struct nouveau_channel *evo = dev_priv->evo;
+	struct drm_device *dev = nv_crtc->base.dev;
+	int ret;
+
+	NV_DEBUG(dev, "\n");
+
+	if (update && nv_crtc->cursor.visible)
+		return;
+
+	ret = RING_SPACE(evo, (dev_priv->chipset != 0x50 ? 5 : 3) + update * 2);
+	if (ret) {
+		NV_ERROR(dev, "no space while unhiding cursor\n");
+		return;
+	}
+
+	if (dev_priv->chipset != 0x50) {
+		BEGIN_RING(evo, 0, NV84_EVO_CRTC(nv_crtc->index, CURSOR_DMA), 1);
+		OUT_RING(evo, NvEvoVRAM);
+	}
+	BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, CURSOR_CTRL), 2);
+	OUT_RING(evo, NV50_EVO_CRTC_CURSOR_CTRL_SHOW);
+	OUT_RING(evo, nv_crtc->cursor.offset >> 8);
+
+	if (update) {
+		BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
+		OUT_RING(evo, 0);
+		FIRE_RING(evo);
+		nv_crtc->cursor.visible = true;
+	}
+}
+
+static void
+nv50_cursor_hide(struct nouveau_crtc *nv_crtc, bool update)
+{
+	struct drm_nouveau_private *dev_priv = nv_crtc->base.dev->dev_private;
+	struct nouveau_channel *evo = dev_priv->evo;
+	struct drm_device *dev = nv_crtc->base.dev;
+	int ret;
+
+	NV_DEBUG(dev, "\n");
+
+	if (update && !nv_crtc->cursor.visible)
+		return;
+
+	ret = RING_SPACE(evo, (dev_priv->chipset != 0x50 ? 5 : 3) + update * 2);
+	if (ret) {
+		NV_ERROR(dev, "no space while hiding cursor\n");
+		return;
+	}
+	BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, CURSOR_CTRL), 2);
+	OUT_RING(evo, NV50_EVO_CRTC_CURSOR_CTRL_HIDE);
+	OUT_RING(evo, 0);
+	if (dev_priv->chipset != 0x50) {
+		BEGIN_RING(evo, 0, NV84_EVO_CRTC(nv_crtc->index, CURSOR_DMA), 1);
+		OUT_RING(evo, NV84_EVO_CRTC_CURSOR_DMA_HANDLE_NONE);
+	}
+
+	if (update) {
+		BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
+		OUT_RING(evo, 0);
+		FIRE_RING(evo);
+		nv_crtc->cursor.visible = false;
+	}
+}
+
+static void
+nv50_cursor_set_pos(struct nouveau_crtc *nv_crtc, int x, int y)
+{
+	struct drm_device *dev = nv_crtc->base.dev;
+
+	nv_wr32(dev, NV50_PDISPLAY_CURSOR_USER_POS(nv_crtc->index),
+		((y & 0xFFFF) << 16) | (x & 0xFFFF));
+	/* Needed to make the cursor move. */
+	nv_wr32(dev, NV50_PDISPLAY_CURSOR_USER_POS_CTRL(nv_crtc->index), 0);
+}
+
+static void
+nv50_cursor_set_offset(struct nouveau_crtc *nv_crtc, uint32_t offset)
+{
+	NV_DEBUG(nv_crtc->base.dev, "\n");
+	if (offset == nv_crtc->cursor.offset)
+		return;
+
+	nv_crtc->cursor.offset = offset;
+	if (nv_crtc->cursor.visible) {
+		nv_crtc->cursor.visible = false;
+		nv_crtc->cursor.show(nv_crtc, true);
+	}
+}
+
+int
+nv50_cursor_init(struct nouveau_crtc *nv_crtc)
+{
+	nv_crtc->cursor.set_offset = nv50_cursor_set_offset;
+	nv_crtc->cursor.set_pos = nv50_cursor_set_pos;
+	nv_crtc->cursor.hide = nv50_cursor_hide;
+	nv_crtc->cursor.show = nv50_cursor_show;
+	return 0;
+}
+
+void
+nv50_cursor_fini(struct nouveau_crtc *nv_crtc)
+{
+	struct drm_device *dev = nv_crtc->base.dev;
+	int idx = nv_crtc->index;
+
+	NV_DEBUG(dev, "\n");
+
+	nv_wr32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(idx), 0);
+	if (!nv_wait(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(idx),
+		     NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS, 0)) {
+		NV_ERROR(dev, "timeout: CURSOR_CTRL2_STATUS == 0\n");
+		NV_ERROR(dev, "CURSOR_CTRL2 = 0x%08x\n",
+			 nv_rd32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(idx)));
+	}
+}
+
diff --git a/drivers/gpu/drm/nouveau/nv50_dac.c b/drivers/gpu/drm/nouveau/nv50_dac.c
new file mode 100644
index 0000000..fb5838e
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv50_dac.c
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "drmP.h"
+#include "drm_crtc_helper.h"
+
+#define NOUVEAU_DMA_DEBUG (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO)
+#include "nouveau_reg.h"
+#include "nouveau_drv.h"
+#include "nouveau_dma.h"
+#include "nouveau_encoder.h"
+#include "nouveau_connector.h"
+#include "nouveau_crtc.h"
+#include "nv50_display.h"
+
+static void
+nv50_dac_disconnect(struct nouveau_encoder *nv_encoder)
+{
+	struct drm_device *dev = to_drm_encoder(nv_encoder)->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_channel *evo = dev_priv->evo;
+	int ret;
+
+	NV_DEBUG(dev, "Disconnecting DAC %d\n", nv_encoder->or);
+
+	ret = RING_SPACE(evo, 2);
+	if (ret) {
+		NV_ERROR(dev, "no space while disconnecting DAC\n");
+		return;
+	}
+	BEGIN_RING(evo, 0, NV50_EVO_DAC(nv_encoder->or, MODE_CTRL), 1);
+	OUT_RING(evo, 0);
+}
+
+static enum drm_connector_status
+nv50_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct drm_device *dev = encoder->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	enum drm_connector_status status = connector_status_disconnected;
+	uint32_t dpms_state, load_pattern, load_state;
+	int or = nv_encoder->or;
+
+	nv_wr32(dev, NV50_PDISPLAY_DAC_CLK_CTRL1(or), 0x00000001);
+	dpms_state = nv_rd32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or));
+
+	nv_wr32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or),
+		0x00150000 | NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING);
+	if (!nv_wait(NV50_PDISPLAY_DAC_DPMS_CTRL(or),
+		     NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING, 0)) {
+		NV_ERROR(dev, "timeout: DAC_DPMS_CTRL_PENDING(%d) == 0\n", or);
+		NV_ERROR(dev, "DAC_DPMS_CTRL(%d) = 0x%08x\n", or,
+			  nv_rd32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or)));
+		return status;
+	}
+
+	/* Use bios provided value if possible. */
+	if (dev_priv->vbios->dactestval) {
+		load_pattern = dev_priv->vbios->dactestval;
+		NV_DEBUG(dev, "Using bios provided load_pattern of %d\n",
+			  load_pattern);
+	} else {
+		load_pattern = 340;
+		NV_DEBUG(dev, "Using default load_pattern of %d\n",
+			 load_pattern);
+	}
+
+	nv_wr32(dev, NV50_PDISPLAY_DAC_LOAD_CTRL(or),
+		NV50_PDISPLAY_DAC_LOAD_CTRL_ACTIVE | load_pattern);
+	mdelay(45); /* give it some time to process */
+	load_state = nv_rd32(dev, NV50_PDISPLAY_DAC_LOAD_CTRL(or));
+
+	nv_wr32(dev, NV50_PDISPLAY_DAC_LOAD_CTRL(or), 0);
+	nv_wr32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or), dpms_state |
+		NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING);
+
+	if ((load_state & NV50_PDISPLAY_DAC_LOAD_CTRL_PRESENT) ==
+			  NV50_PDISPLAY_DAC_LOAD_CTRL_PRESENT)
+		status = connector_status_connected;
+
+	if (status == connector_status_connected)
+		NV_DEBUG(dev, "Load was detected on output with or %d\n", or);
+	else
+		NV_DEBUG(dev, "Load was not detected on output with or %d\n", or);
+
+	return status;
+}
+
+static void
+nv50_dac_dpms(struct drm_encoder *encoder, int mode)
+{
+	struct drm_device *dev = encoder->dev;
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	uint32_t val;
+	int or = nv_encoder->or;
+
+	NV_DEBUG(dev, "or %d mode %d\n", or, mode);
+
+	/* wait for it to be done */
+	if (!nv_wait(NV50_PDISPLAY_DAC_DPMS_CTRL(or),
+		     NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING, 0)) {
+		NV_ERROR(dev, "timeout: DAC_DPMS_CTRL_PENDING(%d) == 0\n", or);
+		NV_ERROR(dev, "DAC_DPMS_CTRL(%d) = 0x%08x\n", or,
+			 nv_rd32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or)));
+		return;
+	}
+
+	val = nv_rd32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or)) & ~0x7F;
+
+	if (mode != DRM_MODE_DPMS_ON)
+		val |= NV50_PDISPLAY_DAC_DPMS_CTRL_BLANKED;
+
+	switch (mode) {
+	case DRM_MODE_DPMS_STANDBY:
+		val |= NV50_PDISPLAY_DAC_DPMS_CTRL_HSYNC_OFF;
+		break;
+	case DRM_MODE_DPMS_SUSPEND:
+		val |= NV50_PDISPLAY_DAC_DPMS_CTRL_VSYNC_OFF;
+		break;
+	case DRM_MODE_DPMS_OFF:
+		val |= NV50_PDISPLAY_DAC_DPMS_CTRL_OFF;
+		val |= NV50_PDISPLAY_DAC_DPMS_CTRL_HSYNC_OFF;
+		val |= NV50_PDISPLAY_DAC_DPMS_CTRL_VSYNC_OFF;
+		break;
+	default:
+		break;
+	}
+
+	nv_wr32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or), val |
+		NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING);
+}
+
+static void
+nv50_dac_save(struct drm_encoder *encoder)
+{
+	NV_ERROR(encoder->dev, "!!\n");
+}
+
+static void
+nv50_dac_restore(struct drm_encoder *encoder)
+{
+	NV_ERROR(encoder->dev, "!!\n");
+}
+
+static bool
+nv50_dac_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
+		    struct drm_display_mode *adjusted_mode)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct nouveau_connector *connector;
+
+	NV_DEBUG(encoder->dev, "or %d\n", nv_encoder->or);
+
+	connector = nouveau_encoder_connector_get(nv_encoder);
+	if (!connector) {
+		NV_ERROR(encoder->dev, "Encoder has no connector\n");
+		return false;
+	}
+
+	if (connector->scaling_mode != DRM_MODE_SCALE_NONE &&
+	     connector->native_mode) {
+		int id = adjusted_mode->base.id;
+		*adjusted_mode = *connector->native_mode;
+		adjusted_mode->base.id = id;
+	}
+
+	return true;
+}
+
+static void
+nv50_dac_prepare(struct drm_encoder *encoder)
+{
+}
+
+static void
+nv50_dac_commit(struct drm_encoder *encoder)
+{
+}
+
+static void
+nv50_dac_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
+		  struct drm_display_mode *adjusted_mode)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct drm_device *dev = encoder->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_channel *evo = dev_priv->evo;
+	struct nouveau_crtc *crtc = nouveau_crtc(encoder->crtc);
+	uint32_t mode_ctl = 0, mode_ctl2 = 0;
+	int ret;
+
+	NV_DEBUG(dev, "or %d\n", nv_encoder->or);
+
+	nv50_dac_dpms(encoder, DRM_MODE_DPMS_ON);
+
+	if (crtc->index == 1)
+		mode_ctl |= NV50_EVO_DAC_MODE_CTRL_CRTC1;
+	else
+		mode_ctl |= NV50_EVO_DAC_MODE_CTRL_CRTC0;
+
+	/* Lacking a working tv-out, this is not a 100% sure. */
+	if (nv_encoder->dcb->type == OUTPUT_ANALOG)
+		mode_ctl |= 0x40;
+	else
+	if (nv_encoder->dcb->type == OUTPUT_TV)
+		mode_ctl |= 0x100;
+
+	if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC)
+		mode_ctl2 |= NV50_EVO_DAC_MODE_CTRL2_NHSYNC;
+
+	if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC)
+		mode_ctl2 |= NV50_EVO_DAC_MODE_CTRL2_NVSYNC;
+
+	ret = RING_SPACE(evo, 3);
+	if (ret) {
+		NV_ERROR(dev, "no space while connecting DAC\n");
+		return;
+	}
+	BEGIN_RING(evo, 0, NV50_EVO_DAC(nv_encoder->or, MODE_CTRL), 2);
+	OUT_RING(evo, mode_ctl);
+	OUT_RING(evo, mode_ctl2);
+}
+
+static const struct drm_encoder_helper_funcs nv50_dac_helper_funcs = {
+	.dpms = nv50_dac_dpms,
+	.save = nv50_dac_save,
+	.restore = nv50_dac_restore,
+	.mode_fixup = nv50_dac_mode_fixup,
+	.prepare = nv50_dac_prepare,
+	.commit = nv50_dac_commit,
+	.mode_set = nv50_dac_mode_set,
+	.detect = nv50_dac_detect
+};
+
+static void
+nv50_dac_destroy(struct drm_encoder *encoder)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+
+	if (!encoder)
+		return;
+
+	NV_DEBUG(encoder->dev, "\n");
+
+	drm_encoder_cleanup(encoder);
+	kfree(nv_encoder);
+}
+
+static const struct drm_encoder_funcs nv50_dac_encoder_funcs = {
+	.destroy = nv50_dac_destroy,
+};
+
+int
+nv50_dac_create(struct drm_device *dev, struct dcb_entry *entry)
+{
+	struct nouveau_encoder *nv_encoder;
+	struct drm_encoder *encoder;
+
+	NV_DEBUG(dev, "\n");
+	NV_INFO(dev, "Detected a DAC output\n");
+
+	nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
+	if (!nv_encoder)
+		return -ENOMEM;
+	encoder = to_drm_encoder(nv_encoder);
+
+	nv_encoder->dcb = entry;
+	nv_encoder->or = ffs(entry->or) - 1;
+
+	nv_encoder->disconnect = nv50_dac_disconnect;
+
+	drm_encoder_init(dev, encoder, &nv50_dac_encoder_funcs,
+			 DRM_MODE_ENCODER_DAC);
+	drm_encoder_helper_add(encoder, &nv50_dac_helper_funcs);
+
+	encoder->possible_crtcs = entry->heads;
+	encoder->possible_clones = 0;
+	return 0;
+}
+
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c
new file mode 100644
index 0000000..12c5ee6
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv50_display.c
@@ -0,0 +1,1015 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "nv50_display.h"
+#include "nouveau_crtc.h"
+#include "nouveau_encoder.h"
+#include "nouveau_connector.h"
+#include "nouveau_fb.h"
+#include "drm_crtc_helper.h"
+
+static void
+nv50_evo_channel_del(struct nouveau_channel **pchan)
+{
+	struct nouveau_channel *chan = *pchan;
+
+	if (!chan)
+		return;
+	*pchan = NULL;
+
+	nouveau_gpuobj_channel_takedown(chan);
+	nouveau_bo_ref(NULL, &chan->pushbuf_bo);
+
+	if (chan->user)
+		iounmap(chan->user);
+
+	kfree(chan);
+}
+
+static int
+nv50_evo_dmaobj_new(struct nouveau_channel *evo, uint32_t class, uint32_t name,
+		    uint32_t tile_flags, uint32_t magic_flags,
+		    uint32_t offset, uint32_t limit)
+{
+	struct drm_nouveau_private *dev_priv = evo->dev->dev_private;
+	struct drm_device *dev = evo->dev;
+	struct nouveau_gpuobj *obj = NULL;
+	int ret;
+
+	ret = nouveau_gpuobj_new(dev, evo, 6*4, 32, 0, &obj);
+	if (ret)
+		return ret;
+	obj->engine = NVOBJ_ENGINE_DISPLAY;
+
+	ret = nouveau_gpuobj_ref_add(dev, evo, name, obj, NULL);
+	if (ret) {
+		nouveau_gpuobj_del(dev, &obj);
+		return ret;
+	}
+
+	dev_priv->engine.instmem.prepare_access(dev, true);
+	nv_wo32(dev, obj, 0, (tile_flags << 22) | (magic_flags << 16) | class);
+	nv_wo32(dev, obj, 1, limit);
+	nv_wo32(dev, obj, 2, offset);
+	nv_wo32(dev, obj, 3, 0x00000000);
+	nv_wo32(dev, obj, 4, 0x00000000);
+	nv_wo32(dev, obj, 5, 0x00010000);
+	dev_priv->engine.instmem.finish_access(dev);
+
+	return 0;
+}
+
+static int
+nv50_evo_channel_new(struct drm_device *dev, struct nouveau_channel **pchan)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_channel *chan;
+	int ret;
+
+	chan = kzalloc(sizeof(struct nouveau_channel), GFP_KERNEL);
+	if (!chan)
+		return -ENOMEM;
+	*pchan = chan;
+
+	chan->id = -1;
+	chan->dev = dev;
+	chan->user_get = 4;
+	chan->user_put = 0;
+
+	INIT_LIST_HEAD(&chan->ramht_refs);
+
+	ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, 32768, 0x1000,
+				     NVOBJ_FLAG_ZERO_ALLOC, &chan->ramin);
+	if (ret) {
+		NV_ERROR(dev, "Error allocating EVO channel memory: %d\n", ret);
+		nv50_evo_channel_del(pchan);
+		return ret;
+	}
+
+	ret = nouveau_mem_init_heap(&chan->ramin_heap, chan->ramin->gpuobj->
+				    im_pramin->start, 32768);
+	if (ret) {
+		NV_ERROR(dev, "Error initialising EVO PRAMIN heap: %d\n", ret);
+		nv50_evo_channel_del(pchan);
+		return ret;
+	}
+
+	ret = nouveau_gpuobj_new_ref(dev, chan, chan, 0, 4096, 16,
+				     0, &chan->ramht);
+	if (ret) {
+		NV_ERROR(dev, "Unable to allocate EVO RAMHT: %d\n", ret);
+		nv50_evo_channel_del(pchan);
+		return ret;
+	}
+
+	if (dev_priv->chipset != 0x50) {
+		ret = nv50_evo_dmaobj_new(chan, 0x3d, NvEvoFB16, 0x70, 0x19,
+					  0, 0xffffffff);
+		if (ret) {
+			nv50_evo_channel_del(pchan);
+			return ret;
+		}
+
+
+		ret = nv50_evo_dmaobj_new(chan, 0x3d, NvEvoFB32, 0x7a, 0x19,
+					  0, 0xffffffff);
+		if (ret) {
+			nv50_evo_channel_del(pchan);
+			return ret;
+		}
+	}
+
+	ret = nv50_evo_dmaobj_new(chan, 0x3d, NvEvoVRAM, 0, 0x19,
+				  0, nouveau_mem_fb_amount(dev));
+	if (ret) {
+		nv50_evo_channel_del(pchan);
+		return ret;
+	}
+
+	ret = nouveau_bo_new(dev, NULL, 4096, 0, TTM_PL_FLAG_VRAM, 0, 0,
+			     false, true, &chan->pushbuf_bo);
+	if (ret == 0)
+		ret = nouveau_bo_pin(chan->pushbuf_bo, TTM_PL_FLAG_VRAM);
+	if (ret) {
+		NV_ERROR(dev, "Error creating EVO DMA push buffer: %d\n", ret);
+		nv50_evo_channel_del(pchan);
+		return ret;
+	}
+
+	ret = nouveau_bo_map(chan->pushbuf_bo);
+	if (ret) {
+		NV_ERROR(dev, "Error mapping EVO DMA push buffer: %d\n", ret);
+		nv50_evo_channel_del(pchan);
+		return ret;
+	}
+
+	chan->user = ioremap(pci_resource_start(dev->pdev, 0) +
+					NV50_PDISPLAY_USER(0), PAGE_SIZE);
+	if (!chan->user) {
+		NV_ERROR(dev, "Error mapping EVO control regs.\n");
+		nv50_evo_channel_del(pchan);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+int
+nv50_display_init(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer;
+	struct nouveau_channel *evo = dev_priv->evo;
+	struct drm_connector *connector;
+	uint32_t val, ram_amount, hpd_en[2];
+	uint64_t start;
+	int ret, i;
+
+	NV_DEBUG(dev, "\n");
+
+	nv_wr32(dev, 0x00610184, nv_rd32(dev, 0x00614004));
+	/*
+	 * I think the 0x006101XX range is some kind of main control area
+	 * that enables things.
+	 */
+	/* CRTC? */
+	for (i = 0; i < 2; i++) {
+		val = nv_rd32(dev, 0x00616100 + (i * 0x800));
+		nv_wr32(dev, 0x00610190 + (i * 0x10), val);
+		val = nv_rd32(dev, 0x00616104 + (i * 0x800));
+		nv_wr32(dev, 0x00610194 + (i * 0x10), val);
+		val = nv_rd32(dev, 0x00616108 + (i * 0x800));
+		nv_wr32(dev, 0x00610198 + (i * 0x10), val);
+		val = nv_rd32(dev, 0x0061610c + (i * 0x800));
+		nv_wr32(dev, 0x0061019c + (i * 0x10), val);
+	}
+	/* DAC */
+	for (i = 0; i < 3; i++) {
+		val = nv_rd32(dev, 0x0061a000 + (i * 0x800));
+		nv_wr32(dev, 0x006101d0 + (i * 0x04), val);
+	}
+	/* SOR */
+	for (i = 0; i < 4; i++) {
+		val = nv_rd32(dev, 0x0061c000 + (i * 0x800));
+		nv_wr32(dev, 0x006101e0 + (i * 0x04), val);
+	}
+	/* Something not yet in use, tv-out maybe. */
+	for (i = 0; i < 3; i++) {
+		val = nv_rd32(dev, 0x0061e000 + (i * 0x800));
+		nv_wr32(dev, 0x006101f0 + (i * 0x04), val);
+	}
+
+	for (i = 0; i < 3; i++) {
+		nv_wr32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(i), 0x00550000 |
+			NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING);
+		nv_wr32(dev, NV50_PDISPLAY_DAC_CLK_CTRL1(i), 0x00000001);
+	}
+
+	/* This used to be in crtc unblank, but seems out of place there. */
+	nv_wr32(dev, NV50_PDISPLAY_UNK_380, 0);
+	/* RAM is clamped to 256 MiB. */
+	ram_amount = nouveau_mem_fb_amount(dev);
+	NV_DEBUG(dev, "ram_amount %d\n", ram_amount);
+	if (ram_amount > 256*1024*1024)
+		ram_amount = 256*1024*1024;
+	nv_wr32(dev, NV50_PDISPLAY_RAM_AMOUNT, ram_amount - 1);
+	nv_wr32(dev, NV50_PDISPLAY_UNK_388, 0x150000);
+	nv_wr32(dev, NV50_PDISPLAY_UNK_38C, 0);
+
+	/* The precise purpose is unknown, i suspect it has something to do
+	 * with text mode.
+	 */
+	if (nv_rd32(dev, NV50_PDISPLAY_INTR_1) & 0x100) {
+		nv_wr32(dev, NV50_PDISPLAY_INTR_1, 0x100);
+		nv_wr32(dev, 0x006194e8, nv_rd32(dev, 0x006194e8) & ~1);
+		if (!nv_wait(0x006194e8, 2, 0)) {
+			NV_ERROR(dev, "timeout: (0x6194e8 & 2) != 0\n");
+			NV_ERROR(dev, "0x6194e8 = 0x%08x\n",
+						nv_rd32(dev, 0x6194e8));
+			return -EBUSY;
+		}
+	}
+
+	/* taken from nv bug #12637, attempts to un-wedge the hw if it's
+	 * stuck in some unspecified state
+	 */
+	start = ptimer->read(dev);
+	nv_wr32(dev, NV50_PDISPLAY_CHANNEL_STAT(0), 0x2b00);
+	while ((val = nv_rd32(dev, NV50_PDISPLAY_CHANNEL_STAT(0))) & 0x1e0000) {
+		if ((val & 0x9f0000) == 0x20000)
+			nv_wr32(dev, NV50_PDISPLAY_CHANNEL_STAT(0),
+							val | 0x800000);
+
+		if ((val & 0x3f0000) == 0x30000)
+			nv_wr32(dev, NV50_PDISPLAY_CHANNEL_STAT(0),
+							val | 0x200000);
+
+		if (ptimer->read(dev) - start > 1000000000ULL) {
+			NV_ERROR(dev, "timeout: (0x610200 & 0x1e0000) != 0\n");
+			NV_ERROR(dev, "0x610200 = 0x%08x\n", val);
+			return -EBUSY;
+		}
+	}
+
+	nv_wr32(dev, NV50_PDISPLAY_CTRL_STATE, NV50_PDISPLAY_CTRL_STATE_ENABLE);
+	nv_wr32(dev, NV50_PDISPLAY_CHANNEL_STAT(0), 0x1000b03);
+	if (!nv_wait(NV50_PDISPLAY_CHANNEL_STAT(0), 0x40000000, 0x40000000)) {
+		NV_ERROR(dev, "timeout: (0x610200 & 0x40000000) == 0x40000000\n");
+		NV_ERROR(dev, "0x610200 = 0x%08x\n",
+			  nv_rd32(dev, NV50_PDISPLAY_CHANNEL_STAT(0)));
+		return -EBUSY;
+	}
+
+	for (i = 0; i < 2; i++) {
+		nv_wr32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), 0x2000);
+		if (!nv_wait(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i),
+			     NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS, 0)) {
+			NV_ERROR(dev, "timeout: CURSOR_CTRL2_STATUS == 0\n");
+			NV_ERROR(dev, "CURSOR_CTRL2 = 0x%08x\n",
+				 nv_rd32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i)));
+			return -EBUSY;
+		}
+
+		nv_wr32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i),
+			NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_ON);
+		if (!nv_wait(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i),
+			     NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS,
+			     NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_ACTIVE)) {
+			NV_ERROR(dev, "timeout: "
+				      "CURSOR_CTRL2_STATUS_ACTIVE(%d)\n", i);
+			NV_ERROR(dev, "CURSOR_CTRL2(%d) = 0x%08x\n", i,
+				 nv_rd32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i)));
+			return -EBUSY;
+		}
+	}
+
+	nv_wr32(dev, NV50_PDISPLAY_OBJECTS, (evo->ramin->instance >> 8) | 9);
+
+	/* initialise fifo */
+	nv_wr32(dev, NV50_PDISPLAY_CHANNEL_DMA_CB(0),
+		((evo->pushbuf_bo->bo.mem.mm_node->start << PAGE_SHIFT) >> 8) |
+		NV50_PDISPLAY_CHANNEL_DMA_CB_LOCATION_VRAM |
+		NV50_PDISPLAY_CHANNEL_DMA_CB_VALID);
+	nv_wr32(dev, NV50_PDISPLAY_CHANNEL_UNK2(0), 0x00010000);
+	nv_wr32(dev, NV50_PDISPLAY_CHANNEL_UNK3(0), 0x00000002);
+	if (!nv_wait(0x610200, 0x80000000, 0x00000000)) {
+		NV_ERROR(dev, "timeout: (0x610200 & 0x80000000) == 0\n");
+		NV_ERROR(dev, "0x610200 = 0x%08x\n", nv_rd32(dev, 0x610200));
+		return -EBUSY;
+	}
+	nv_wr32(dev, NV50_PDISPLAY_CHANNEL_STAT(0),
+		(nv_rd32(dev, NV50_PDISPLAY_CHANNEL_STAT(0)) & ~0x00000003) |
+		 NV50_PDISPLAY_CHANNEL_STAT_DMA_ENABLED);
+	nv_wr32(dev, NV50_PDISPLAY_USER_PUT(0), 0);
+	nv_wr32(dev, NV50_PDISPLAY_CHANNEL_STAT(0), 0x01000003 |
+		NV50_PDISPLAY_CHANNEL_STAT_DMA_ENABLED);
+	nv_wr32(dev, 0x610300, nv_rd32(dev, 0x610300) & ~1);
+
+	evo->dma.max = (4096/4) - 2;
+	evo->dma.put = 0;
+	evo->dma.cur = evo->dma.put;
+	evo->dma.free = evo->dma.max - evo->dma.cur;
+
+	ret = RING_SPACE(evo, NOUVEAU_DMA_SKIPS);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < NOUVEAU_DMA_SKIPS; i++)
+		OUT_RING(evo, 0);
+
+	ret = RING_SPACE(evo, 11);
+	if (ret)
+		return ret;
+	BEGIN_RING(evo, 0, NV50_EVO_UNK84, 2);
+	OUT_RING(evo, NV50_EVO_UNK84_NOTIFY_DISABLED);
+	OUT_RING(evo, NV50_EVO_DMA_NOTIFY_HANDLE_NONE);
+	BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, FB_DMA), 1);
+	OUT_RING(evo, NV50_EVO_CRTC_FB_DMA_HANDLE_NONE);
+	BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, UNK0800), 1);
+	OUT_RING(evo, 0);
+	BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, DISPLAY_START), 1);
+	OUT_RING(evo, 0);
+	BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, UNK082C), 1);
+	OUT_RING(evo, 0);
+	FIRE_RING(evo);
+	if (!nv_wait(0x640004, 0xffffffff, evo->dma.put << 2))
+		NV_ERROR(dev, "evo pushbuf stalled\n");
+
+	/* enable clock change interrupts. */
+	nv_wr32(dev, 0x610028, 0x00010001);
+	nv_wr32(dev, NV50_PDISPLAY_INTR_EN, (NV50_PDISPLAY_INTR_EN_CLK_UNK10 |
+					     NV50_PDISPLAY_INTR_EN_CLK_UNK20 |
+					     NV50_PDISPLAY_INTR_EN_CLK_UNK40));
+
+	/* enable hotplug interrupts */
+	hpd_en[0] = hpd_en[1] = 0;
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+		struct nouveau_connector *conn = nouveau_connector(connector);
+		struct dcb_gpio_entry *gpio;
+
+		if (connector->connector_type != DRM_MODE_CONNECTOR_DVII &&
+		    connector->connector_type != DRM_MODE_CONNECTOR_DVID &&
+		    connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort)
+			continue;
+
+		gpio = nouveau_bios_gpio_entry(dev, conn->dcb->gpio_tag);
+		if (!gpio)
+			continue;
+
+		hpd_en[gpio->line >> 4] |= (0x00010001 << (gpio->line & 0xf));
+	}
+
+	nv_wr32(dev, 0xe054, 0xffffffff);
+	nv_wr32(dev, 0xe050, hpd_en[0]);
+	if (dev_priv->chipset >= 0x90) {
+		nv_wr32(dev, 0xe074, 0xffffffff);
+		nv_wr32(dev, 0xe070, hpd_en[1]);
+	}
+
+	return 0;
+}
+
+static int nv50_display_disable(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct drm_crtc *drm_crtc;
+	int ret, i;
+
+	NV_DEBUG(dev, "\n");
+
+	list_for_each_entry(drm_crtc, &dev->mode_config.crtc_list, head) {
+		struct nouveau_crtc *crtc = nouveau_crtc(drm_crtc);
+
+		nv50_crtc_blank(crtc, true);
+	}
+
+	ret = RING_SPACE(dev_priv->evo, 2);
+	if (ret == 0) {
+		BEGIN_RING(dev_priv->evo, 0, NV50_EVO_UPDATE, 1);
+		OUT_RING(dev_priv->evo, 0);
+	}
+	FIRE_RING(dev_priv->evo);
+
+	/* Almost like ack'ing a vblank interrupt, maybe in the spirit of
+	 * cleaning up?
+	 */
+	list_for_each_entry(drm_crtc, &dev->mode_config.crtc_list, head) {
+		struct nouveau_crtc *crtc = nouveau_crtc(drm_crtc);
+		uint32_t mask = NV50_PDISPLAY_INTR_1_VBLANK_CRTC_(crtc->index);
+
+		if (!crtc->base.enabled)
+			continue;
+
+		nv_wr32(dev, NV50_PDISPLAY_INTR_1, mask);
+		if (!nv_wait(NV50_PDISPLAY_INTR_1, mask, mask)) {
+			NV_ERROR(dev, "timeout: (0x610024 & 0x%08x) == "
+				      "0x%08x\n", mask, mask);
+			NV_ERROR(dev, "0x610024 = 0x%08x\n",
+				 nv_rd32(dev, NV50_PDISPLAY_INTR_1));
+		}
+	}
+
+	nv_wr32(dev, NV50_PDISPLAY_CHANNEL_STAT(0), 0);
+	nv_wr32(dev, NV50_PDISPLAY_CTRL_STATE, 0);
+	if (!nv_wait(NV50_PDISPLAY_CHANNEL_STAT(0), 0x1e0000, 0)) {
+		NV_ERROR(dev, "timeout: (0x610200 & 0x1e0000) == 0\n");
+		NV_ERROR(dev, "0x610200 = 0x%08x\n",
+			  nv_rd32(dev, NV50_PDISPLAY_CHANNEL_STAT(0)));
+	}
+
+	for (i = 0; i < 3; i++) {
+		if (!nv_wait(NV50_PDISPLAY_SOR_DPMS_STATE(i),
+			     NV50_PDISPLAY_SOR_DPMS_STATE_WAIT, 0)) {
+			NV_ERROR(dev, "timeout: SOR_DPMS_STATE_WAIT(%d) == 0\n", i);
+			NV_ERROR(dev, "SOR_DPMS_STATE(%d) = 0x%08x\n", i,
+				  nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_STATE(i)));
+		}
+	}
+
+	/* disable interrupts. */
+	nv_wr32(dev, NV50_PDISPLAY_INTR_EN, 0x00000000);
+
+	/* disable hotplug interrupts */
+	nv_wr32(dev, 0xe054, 0xffffffff);
+	nv_wr32(dev, 0xe050, 0x00000000);
+	if (dev_priv->chipset >= 0x90) {
+		nv_wr32(dev, 0xe074, 0xffffffff);
+		nv_wr32(dev, 0xe070, 0x00000000);
+	}
+	return 0;
+}
+
+int nv50_display_create(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct parsed_dcb *dcb = dev_priv->vbios->dcb;
+	uint32_t connector[16] = {};
+	int ret, i;
+
+	NV_DEBUG(dev, "\n");
+
+	/* init basic kernel modesetting */
+	drm_mode_config_init(dev);
+
+	/* Initialise some optional connector properties. */
+	drm_mode_create_scaling_mode_property(dev);
+	drm_mode_create_dithering_property(dev);
+
+	dev->mode_config.min_width = 0;
+	dev->mode_config.min_height = 0;
+
+	dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs;
+
+	dev->mode_config.max_width = 8192;
+	dev->mode_config.max_height = 8192;
+
+	dev->mode_config.fb_base = dev_priv->fb_phys;
+
+	/* Create EVO channel */
+	ret = nv50_evo_channel_new(dev, &dev_priv->evo);
+	if (ret) {
+		NV_ERROR(dev, "Error creating EVO channel: %d\n", ret);
+		return ret;
+	}
+
+	/* Create CRTC objects */
+	for (i = 0; i < 2; i++)
+		nv50_crtc_create(dev, i);
+
+	/* We setup the encoders from the BIOS table */
+	for (i = 0 ; i < dcb->entries; i++) {
+		struct dcb_entry *entry = &dcb->entry[i];
+
+		if (entry->location != DCB_LOC_ON_CHIP) {
+			NV_WARN(dev, "Off-chip encoder %d/%d unsupported\n",
+				entry->type, ffs(entry->or) - 1);
+			continue;
+		}
+
+		switch (entry->type) {
+		case OUTPUT_TMDS:
+		case OUTPUT_LVDS:
+		case OUTPUT_DP:
+			nv50_sor_create(dev, entry);
+			break;
+		case OUTPUT_ANALOG:
+			nv50_dac_create(dev, entry);
+			break;
+		default:
+			NV_WARN(dev, "DCB encoder %d unknown\n", entry->type);
+			continue;
+		}
+
+		connector[entry->connector] |= (1 << entry->type);
+	}
+
+	/* It appears that DCB 3.0+ VBIOS has a connector table, however,
+	 * I'm not 100% certain how to decode it correctly yet so just
+	 * look at what encoders are present on each connector index and
+	 * attempt to derive the connector type from that.
+	 */
+	for (i = 0 ; i < dcb->entries; i++) {
+		struct dcb_entry *entry = &dcb->entry[i];
+		uint16_t encoders;
+		int type;
+
+		encoders = connector[entry->connector];
+		if (!(encoders & (1 << entry->type)))
+			continue;
+		connector[entry->connector] = 0;
+
+		if (encoders & (1 << OUTPUT_DP)) {
+			type = DRM_MODE_CONNECTOR_DisplayPort;
+		} else if (encoders & (1 << OUTPUT_TMDS)) {
+			if (encoders & (1 << OUTPUT_ANALOG))
+				type = DRM_MODE_CONNECTOR_DVII;
+			else
+				type = DRM_MODE_CONNECTOR_DVID;
+		} else if (encoders & (1 << OUTPUT_ANALOG)) {
+			type = DRM_MODE_CONNECTOR_VGA;
+		} else if (encoders & (1 << OUTPUT_LVDS)) {
+			type = DRM_MODE_CONNECTOR_LVDS;
+		} else {
+			type = DRM_MODE_CONNECTOR_Unknown;
+		}
+
+		if (type == DRM_MODE_CONNECTOR_Unknown)
+			continue;
+
+		nouveau_connector_create(dev, entry->connector, type);
+	}
+
+	ret = nv50_display_init(dev);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+int nv50_display_destroy(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	NV_DEBUG(dev, "\n");
+
+	drm_mode_config_cleanup(dev);
+
+	nv50_display_disable(dev);
+	nv50_evo_channel_del(&dev_priv->evo);
+
+	return 0;
+}
+
+static inline uint32_t
+nv50_display_mode_ctrl(struct drm_device *dev, bool sor, int or)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	uint32_t mc;
+
+	if (sor) {
+		if (dev_priv->chipset < 0x90 ||
+		    dev_priv->chipset == 0x92 || dev_priv->chipset == 0xa0)
+			mc = nv_rd32(dev, NV50_PDISPLAY_SOR_MODE_CTRL_P(or));
+		else
+			mc = nv_rd32(dev, NV90_PDISPLAY_SOR_MODE_CTRL_P(or));
+	} else {
+		mc = nv_rd32(dev, NV50_PDISPLAY_DAC_MODE_CTRL_P(or));
+	}
+
+	return mc;
+}
+
+static int
+nv50_display_irq_head(struct drm_device *dev, int *phead,
+		      struct dcb_entry **pdcbent)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	uint32_t unk30 = nv_rd32(dev, NV50_PDISPLAY_UNK30_CTRL);
+	uint32_t dac = 0, sor = 0;
+	int head, i, or = 0, type = OUTPUT_ANY;
+
+	/* We're assuming that head 0 *or* head 1 will be active here,
+	 * and not both.  I'm not sure if the hw will even signal both
+	 * ever, but it definitely shouldn't for us as we commit each
+	 * CRTC separately, and submission will be blocked by the GPU
+	 * until we handle each in turn.
+	 */
+	NV_DEBUG(dev, "0x610030: 0x%08x\n", unk30);
+	head = ffs((unk30 >> 9) & 3) - 1;
+	if (head < 0)
+		return -EINVAL;
+
+	/* This assumes CRTCs are never bound to multiple encoders, which
+	 * should be the case.
+	 */
+	for (i = 0; i < 3 && type == OUTPUT_ANY; i++) {
+		uint32_t mc = nv50_display_mode_ctrl(dev, false, i);
+		if (!(mc & (1 << head)))
+			continue;
+
+		switch ((mc >> 8) & 0xf) {
+		case 0: type = OUTPUT_ANALOG; break;
+		case 1: type = OUTPUT_TV; break;
+		default:
+			NV_ERROR(dev, "unknown dac mode_ctrl: 0x%08x\n", dac);
+			return -1;
+		}
+
+		or = i;
+	}
+
+	for (i = 0; i < 4 && type == OUTPUT_ANY; i++) {
+		uint32_t mc = nv50_display_mode_ctrl(dev, true, i);
+		if (!(mc & (1 << head)))
+			continue;
+
+		switch ((mc >> 8) & 0xf) {
+		case 0: type = OUTPUT_LVDS; break;
+		case 1: type = OUTPUT_TMDS; break;
+		case 2: type = OUTPUT_TMDS; break;
+		case 5: type = OUTPUT_TMDS; break;
+		case 8: type = OUTPUT_DP; break;
+		case 9: type = OUTPUT_DP; break;
+		default:
+			NV_ERROR(dev, "unknown sor mode_ctrl: 0x%08x\n", sor);
+			return -1;
+		}
+
+		or = i;
+	}
+
+	NV_DEBUG(dev, "type %d, or %d\n", type, or);
+	if (type == OUTPUT_ANY) {
+		NV_ERROR(dev, "unknown encoder!!\n");
+		return -1;
+	}
+
+	for (i = 0; i < dev_priv->vbios->dcb->entries; i++) {
+		struct dcb_entry *dcbent = &dev_priv->vbios->dcb->entry[i];
+
+		if (dcbent->type != type)
+			continue;
+
+		if (!(dcbent->or & (1 << or)))
+			continue;
+
+		*phead = head;
+		*pdcbent = dcbent;
+		return 0;
+	}
+
+	NV_ERROR(dev, "no DCB entry for %d %d\n", dac != 0, or);
+	return 0;
+}
+
+static uint32_t
+nv50_display_script_select(struct drm_device *dev, struct dcb_entry *dcbent,
+			   int pxclk)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nvbios *bios = &dev_priv->VBIOS;
+	uint32_t mc, script = 0, or;
+
+	or = ffs(dcbent->or) - 1;
+	mc = nv50_display_mode_ctrl(dev, dcbent->type != OUTPUT_ANALOG, or);
+	switch (dcbent->type) {
+	case OUTPUT_LVDS:
+		script = (mc >> 8) & 0xf;
+		if (bios->pub.fp_no_ddc) {
+			if (bios->fp.dual_link)
+				script |= 0x0100;
+			if (bios->fp.if_is_24bit)
+				script |= 0x0200;
+		} else {
+			if (pxclk >= bios->fp.duallink_transition_clk) {
+				script |= 0x0100;
+				if (bios->fp.strapless_is_24bit & 2)
+					script |= 0x0200;
+			} else
+			if (bios->fp.strapless_is_24bit & 1)
+				script |= 0x0200;
+		}
+
+		if (nouveau_uscript_lvds >= 0) {
+			NV_INFO(dev, "override script 0x%04x with 0x%04x "
+				     "for output LVDS-%d\n", script,
+				     nouveau_uscript_lvds, or);
+			script = nouveau_uscript_lvds;
+		}
+		break;
+	case OUTPUT_TMDS:
+		script = (mc >> 8) & 0xf;
+		if (pxclk >= 165000)
+			script |= 0x0100;
+
+		if (nouveau_uscript_tmds >= 0) {
+			NV_INFO(dev, "override script 0x%04x with 0x%04x "
+				     "for output TMDS-%d\n", script,
+				     nouveau_uscript_tmds, or);
+			script = nouveau_uscript_tmds;
+		}
+		break;
+	case OUTPUT_DP:
+		script = (mc >> 8) & 0xf;
+		break;
+	case OUTPUT_ANALOG:
+		script = 0xff;
+		break;
+	default:
+		NV_ERROR(dev, "modeset on unsupported output type!\n");
+		break;
+	}
+
+	return script;
+}
+
+static void
+nv50_display_vblank_crtc_handler(struct drm_device *dev, int crtc)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_channel *chan;
+	struct list_head *entry, *tmp;
+
+	list_for_each_safe(entry, tmp, &dev_priv->vbl_waiting) {
+		chan = list_entry(entry, struct nouveau_channel, nvsw.vbl_wait);
+
+		nouveau_bo_wr32(chan->notifier_bo, chan->nvsw.vblsem_offset,
+						chan->nvsw.vblsem_rval);
+		list_del(&chan->nvsw.vbl_wait);
+	}
+}
+
+static void
+nv50_display_vblank_handler(struct drm_device *dev, uint32_t intr)
+{
+	intr &= NV50_PDISPLAY_INTR_1_VBLANK_CRTC;
+
+	if (intr & NV50_PDISPLAY_INTR_1_VBLANK_CRTC_0)
+		nv50_display_vblank_crtc_handler(dev, 0);
+
+	if (intr & NV50_PDISPLAY_INTR_1_VBLANK_CRTC_1)
+		nv50_display_vblank_crtc_handler(dev, 1);
+
+	nv_wr32(dev, NV50_PDISPLAY_INTR_EN, nv_rd32(dev,
+		     NV50_PDISPLAY_INTR_EN) & ~intr);
+	nv_wr32(dev, NV50_PDISPLAY_INTR_1, intr);
+}
+
+static void
+nv50_display_unk10_handler(struct drm_device *dev)
+{
+	struct dcb_entry *dcbent;
+	int head, ret;
+
+	ret = nv50_display_irq_head(dev, &head, &dcbent);
+	if (ret)
+		goto ack;
+
+	nv_wr32(dev, 0x619494, nv_rd32(dev, 0x619494) & ~8);
+
+	nouveau_bios_run_display_table(dev, dcbent, 0, -1);
+
+ack:
+	nv_wr32(dev, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK10);
+	nv_wr32(dev, 0x610030, 0x80000000);
+}
+
+static void
+nv50_display_unk20_handler(struct drm_device *dev)
+{
+	struct dcb_entry *dcbent;
+	uint32_t tmp, pclk, script;
+	int head, or, ret;
+
+	ret = nv50_display_irq_head(dev, &head, &dcbent);
+	if (ret)
+		goto ack;
+	or = ffs(dcbent->or) - 1;
+	pclk = nv_rd32(dev, NV50_PDISPLAY_CRTC_P(head, CLOCK)) & 0x3fffff;
+	script = nv50_display_script_select(dev, dcbent, pclk);
+
+	NV_DEBUG(dev, "head %d pxclk: %dKHz\n", head, pclk);
+
+	if (dcbent->type != OUTPUT_DP)
+		nouveau_bios_run_display_table(dev, dcbent, 0, -2);
+
+	nv50_crtc_set_clock(dev, head, pclk);
+
+	nouveau_bios_run_display_table(dev, dcbent, script, pclk);
+
+	tmp = nv_rd32(dev, NV50_PDISPLAY_CRTC_CLK_CTRL2(head));
+	tmp &= ~0x000000f;
+	nv_wr32(dev, NV50_PDISPLAY_CRTC_CLK_CTRL2(head), tmp);
+
+	if (dcbent->type != OUTPUT_ANALOG) {
+		tmp = nv_rd32(dev, NV50_PDISPLAY_SOR_CLK_CTRL2(or));
+		tmp &= ~0x00000f0f;
+		if (script & 0x0100)
+			tmp |= 0x00000101;
+		nv_wr32(dev, NV50_PDISPLAY_SOR_CLK_CTRL2(or), tmp);
+	} else {
+		nv_wr32(dev, NV50_PDISPLAY_DAC_CLK_CTRL2(or), 0);
+	}
+
+ack:
+	nv_wr32(dev, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK20);
+	nv_wr32(dev, 0x610030, 0x80000000);
+}
+
+static void
+nv50_display_unk40_handler(struct drm_device *dev)
+{
+	struct dcb_entry *dcbent;
+	int head, pclk, script, ret;
+
+	ret = nv50_display_irq_head(dev, &head, &dcbent);
+	if (ret)
+		goto ack;
+	pclk = nv_rd32(dev, NV50_PDISPLAY_CRTC_P(head, CLOCK)) & 0x3fffff;
+	script = nv50_display_script_select(dev, dcbent, pclk);
+
+	nouveau_bios_run_display_table(dev, dcbent, script, -pclk);
+
+ack:
+	nv_wr32(dev, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK40);
+	nv_wr32(dev, 0x610030, 0x80000000);
+	nv_wr32(dev, 0x619494, nv_rd32(dev, 0x619494) | 8);
+}
+
+void
+nv50_display_irq_handler_bh(struct work_struct *work)
+{
+	struct drm_nouveau_private *dev_priv =
+		container_of(work, struct drm_nouveau_private, irq_work);
+	struct drm_device *dev = dev_priv->dev;
+
+	for (;;) {
+		uint32_t intr0 = nv_rd32(dev, NV50_PDISPLAY_INTR_0);
+		uint32_t intr1 = nv_rd32(dev, NV50_PDISPLAY_INTR_1);
+
+		NV_DEBUG(dev, "PDISPLAY_INTR_BH 0x%08x 0x%08x\n", intr0, intr1);
+
+		if (intr1 & NV50_PDISPLAY_INTR_1_CLK_UNK10)
+			nv50_display_unk10_handler(dev);
+		else
+		if (intr1 & NV50_PDISPLAY_INTR_1_CLK_UNK20)
+			nv50_display_unk20_handler(dev);
+		else
+		if (intr1 & NV50_PDISPLAY_INTR_1_CLK_UNK40)
+			nv50_display_unk40_handler(dev);
+		else
+			break;
+	}
+
+	nv_wr32(dev, NV03_PMC_INTR_EN_0, 1);
+}
+
+static void
+nv50_display_error_handler(struct drm_device *dev)
+{
+	uint32_t addr, data;
+
+	nv_wr32(dev, NV50_PDISPLAY_INTR_0, 0x00010000);
+	addr = nv_rd32(dev, NV50_PDISPLAY_TRAPPED_ADDR);
+	data = nv_rd32(dev, NV50_PDISPLAY_TRAPPED_DATA);
+
+	NV_ERROR(dev, "EvoCh %d Mthd 0x%04x Data 0x%08x (0x%04x 0x%02x)\n",
+		 0, addr & 0xffc, data, addr >> 16, (addr >> 12) & 0xf);
+
+	nv_wr32(dev, NV50_PDISPLAY_TRAPPED_ADDR, 0x90000000);
+}
+
+static void
+nv50_display_irq_hotplug(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct drm_connector *connector;
+	const uint32_t gpio_reg[4] = { 0xe104, 0xe108, 0xe280, 0xe284 };
+	uint32_t unplug_mask, plug_mask, change_mask;
+	uint32_t hpd0, hpd1 = 0;
+
+	hpd0 = nv_rd32(dev, 0xe054) & nv_rd32(dev, 0xe050);
+	if (dev_priv->chipset >= 0x90)
+		hpd1 = nv_rd32(dev, 0xe074) & nv_rd32(dev, 0xe070);
+
+	plug_mask   = (hpd0 & 0x0000ffff) | (hpd1 << 16);
+	unplug_mask = (hpd0 >> 16) | (hpd1 & 0xffff0000);
+	change_mask = plug_mask | unplug_mask;
+
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+		struct drm_encoder_helper_funcs *helper;
+		struct nouveau_connector *nv_connector =
+			nouveau_connector(connector);
+		struct nouveau_encoder *nv_encoder;
+		struct dcb_gpio_entry *gpio;
+		uint32_t reg;
+		bool plugged;
+
+		if (!nv_connector->dcb)
+			continue;
+
+		gpio = nouveau_bios_gpio_entry(dev, nv_connector->dcb->gpio_tag);
+		if (!gpio || !(change_mask & (1 << gpio->line)))
+			continue;
+
+		reg = nv_rd32(dev, gpio_reg[gpio->line >> 3]);
+		plugged = !!(reg & (4 << ((gpio->line & 7) << 2)));
+		NV_INFO(dev, "%splugged %s\n", plugged ? "" : "un",
+			drm_get_connector_name(connector)) ;
+
+		if (!connector->encoder || !connector->encoder->crtc ||
+		    !connector->encoder->crtc->enabled)
+			continue;
+		nv_encoder = nouveau_encoder(connector->encoder);
+		helper = connector->encoder->helper_private;
+
+		if (nv_encoder->dcb->type != OUTPUT_DP)
+			continue;
+
+		if (plugged)
+			helper->dpms(connector->encoder, DRM_MODE_DPMS_ON);
+		else
+			helper->dpms(connector->encoder, DRM_MODE_DPMS_OFF);
+	}
+
+	nv_wr32(dev, 0xe054, nv_rd32(dev, 0xe054));
+	if (dev_priv->chipset >= 0x90)
+		nv_wr32(dev, 0xe074, nv_rd32(dev, 0xe074));
+}
+
+void
+nv50_display_irq_handler(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	uint32_t delayed = 0;
+
+	while (nv_rd32(dev, NV50_PMC_INTR_0) & NV50_PMC_INTR_0_HOTPLUG)
+		nv50_display_irq_hotplug(dev);
+
+	while (nv_rd32(dev, NV50_PMC_INTR_0) & NV50_PMC_INTR_0_DISPLAY) {
+		uint32_t intr0 = nv_rd32(dev, NV50_PDISPLAY_INTR_0);
+		uint32_t intr1 = nv_rd32(dev, NV50_PDISPLAY_INTR_1);
+		uint32_t clock;
+
+		NV_DEBUG(dev, "PDISPLAY_INTR 0x%08x 0x%08x\n", intr0, intr1);
+
+		if (!intr0 && !(intr1 & ~delayed))
+			break;
+
+		if (intr0 & 0x00010000) {
+			nv50_display_error_handler(dev);
+			intr0 &= ~0x00010000;
+		}
+
+		if (intr1 & NV50_PDISPLAY_INTR_1_VBLANK_CRTC) {
+			nv50_display_vblank_handler(dev, intr1);
+			intr1 &= ~NV50_PDISPLAY_INTR_1_VBLANK_CRTC;
+		}
+
+		clock = (intr1 & (NV50_PDISPLAY_INTR_1_CLK_UNK10 |
+				  NV50_PDISPLAY_INTR_1_CLK_UNK20 |
+				  NV50_PDISPLAY_INTR_1_CLK_UNK40));
+		if (clock) {
+			nv_wr32(dev, NV03_PMC_INTR_EN_0, 0);
+			if (!work_pending(&dev_priv->irq_work))
+				queue_work(dev_priv->wq, &dev_priv->irq_work);
+			delayed |= clock;
+			intr1 &= ~clock;
+		}
+
+		if (intr0) {
+			NV_ERROR(dev, "unknown PDISPLAY_INTR_0: 0x%08x\n", intr0);
+			nv_wr32(dev, NV50_PDISPLAY_INTR_0, intr0);
+		}
+
+		if (intr1) {
+			NV_ERROR(dev,
+				 "unknown PDISPLAY_INTR_1: 0x%08x\n", intr1);
+			nv_wr32(dev, NV50_PDISPLAY_INTR_1, intr1);
+		}
+	}
+}
+
diff --git a/drivers/gpu/drm/nouveau/nv50_display.h b/drivers/gpu/drm/nouveau/nv50_display.h
new file mode 100644
index 0000000..3ae8d07
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv50_display.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef __NV50_DISPLAY_H__
+#define __NV50_DISPLAY_H__
+
+#include "drmP.h"
+#include "drm.h"
+#include "nouveau_drv.h"
+#include "nouveau_dma.h"
+#include "nouveau_reg.h"
+#include "nouveau_crtc.h"
+#include "nv50_evo.h"
+
+void nv50_display_irq_handler(struct drm_device *dev);
+void nv50_display_irq_handler_bh(struct work_struct *work);
+int nv50_display_init(struct drm_device *dev);
+int nv50_display_create(struct drm_device *dev);
+int nv50_display_destroy(struct drm_device *dev);
+int nv50_crtc_blank(struct nouveau_crtc *, bool blank);
+int nv50_crtc_set_clock(struct drm_device *, int head, int pclk);
+
+#endif /* __NV50_DISPLAY_H__ */
diff --git a/drivers/gpu/drm/nouveau/nv50_evo.h b/drivers/gpu/drm/nouveau/nv50_evo.h
new file mode 100644
index 0000000..aae1334
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv50_evo.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#define NV50_EVO_UPDATE                                              0x00000080
+#define NV50_EVO_UNK84                                               0x00000084
+#define NV50_EVO_UNK84_NOTIFY                                        0x40000000
+#define NV50_EVO_UNK84_NOTIFY_DISABLED                               0x00000000
+#define NV50_EVO_UNK84_NOTIFY_ENABLED                                0x40000000
+#define NV50_EVO_DMA_NOTIFY                                          0x00000088
+#define NV50_EVO_DMA_NOTIFY_HANDLE                                   0xffffffff
+#define NV50_EVO_DMA_NOTIFY_HANDLE_NONE                              0x00000000
+#define NV50_EVO_UNK8C                                               0x0000008C
+
+#define NV50_EVO_DAC(n, r)                       ((n) * 0x80 + NV50_EVO_DAC_##r)
+#define NV50_EVO_DAC_MODE_CTRL                                       0x00000400
+#define NV50_EVO_DAC_MODE_CTRL_CRTC0                                 0x00000001
+#define NV50_EVO_DAC_MODE_CTRL_CRTC1                                 0x00000002
+#define NV50_EVO_DAC_MODE_CTRL2                                      0x00000404
+#define NV50_EVO_DAC_MODE_CTRL2_NHSYNC                               0x00000001
+#define NV50_EVO_DAC_MODE_CTRL2_NVSYNC                               0x00000002
+
+#define NV50_EVO_SOR(n, r)                       ((n) * 0x40 + NV50_EVO_SOR_##r)
+#define NV50_EVO_SOR_MODE_CTRL                                       0x00000600
+#define NV50_EVO_SOR_MODE_CTRL_CRTC0                                 0x00000001
+#define NV50_EVO_SOR_MODE_CTRL_CRTC1                                 0x00000002
+#define NV50_EVO_SOR_MODE_CTRL_TMDS                                  0x00000100
+#define NV50_EVO_SOR_MODE_CTRL_TMDS_DUAL_LINK                        0x00000400
+#define NV50_EVO_SOR_MODE_CTRL_NHSYNC                                0x00001000
+#define NV50_EVO_SOR_MODE_CTRL_NVSYNC                                0x00002000
+
+#define NV50_EVO_CRTC(n, r)                    ((n) * 0x400 + NV50_EVO_CRTC_##r)
+#define NV84_EVO_CRTC(n, r)                    ((n) * 0x400 + NV84_EVO_CRTC_##r)
+#define NV50_EVO_CRTC_UNK0800                                        0x00000800
+#define NV50_EVO_CRTC_CLOCK                                          0x00000804
+#define NV50_EVO_CRTC_INTERLACE                                      0x00000808
+#define NV50_EVO_CRTC_DISPLAY_START                                  0x00000810
+#define NV50_EVO_CRTC_DISPLAY_TOTAL                                  0x00000814
+#define NV50_EVO_CRTC_SYNC_DURATION                                  0x00000818
+#define NV50_EVO_CRTC_SYNC_START_TO_BLANK_END                        0x0000081c
+#define NV50_EVO_CRTC_UNK0820                                        0x00000820
+#define NV50_EVO_CRTC_UNK0824                                        0x00000824
+#define NV50_EVO_CRTC_UNK082C                                        0x0000082c
+#define NV50_EVO_CRTC_CLUT_MODE                                      0x00000840
+/* You can't have a palette in 8 bit mode (=OFF) */
+#define NV50_EVO_CRTC_CLUT_MODE_BLANK                                0x00000000
+#define NV50_EVO_CRTC_CLUT_MODE_OFF                                  0x80000000
+#define NV50_EVO_CRTC_CLUT_MODE_ON                                   0xC0000000
+#define NV50_EVO_CRTC_CLUT_OFFSET                                    0x00000844
+#define NV84_EVO_CRTC_CLUT_DMA                                       0x0000085C
+#define NV84_EVO_CRTC_CLUT_DMA_HANDLE                                0xffffffff
+#define NV84_EVO_CRTC_CLUT_DMA_HANDLE_NONE                           0x00000000
+#define NV50_EVO_CRTC_FB_OFFSET                                      0x00000860
+#define NV50_EVO_CRTC_FB_SIZE                                        0x00000868
+#define NV50_EVO_CRTC_FB_CONFIG                                      0x0000086c
+#define NV50_EVO_CRTC_FB_CONFIG_MODE                                 0x00100000
+#define NV50_EVO_CRTC_FB_CONFIG_MODE_TILE                            0x00000000
+#define NV50_EVO_CRTC_FB_CONFIG_MODE_PITCH                           0x00100000
+#define NV50_EVO_CRTC_FB_DEPTH                                       0x00000870
+#define NV50_EVO_CRTC_FB_DEPTH_8                                     0x00001e00
+#define NV50_EVO_CRTC_FB_DEPTH_15                                    0x0000e900
+#define NV50_EVO_CRTC_FB_DEPTH_16                                    0x0000e800
+#define NV50_EVO_CRTC_FB_DEPTH_24                                    0x0000cf00
+#define NV50_EVO_CRTC_FB_DEPTH_30                                    0x0000d100
+#define NV50_EVO_CRTC_FB_DMA                                         0x00000874
+#define NV50_EVO_CRTC_FB_DMA_HANDLE                                  0xffffffff
+#define NV50_EVO_CRTC_FB_DMA_HANDLE_NONE                             0x00000000
+#define NV50_EVO_CRTC_CURSOR_CTRL                                    0x00000880
+#define NV50_EVO_CRTC_CURSOR_CTRL_HIDE                               0x05000000
+#define NV50_EVO_CRTC_CURSOR_CTRL_SHOW                               0x85000000
+#define NV50_EVO_CRTC_CURSOR_OFFSET                                  0x00000884
+#define NV84_EVO_CRTC_CURSOR_DMA                                     0x0000089c
+#define NV84_EVO_CRTC_CURSOR_DMA_HANDLE                              0xffffffff
+#define NV84_EVO_CRTC_CURSOR_DMA_HANDLE_NONE                         0x00000000
+#define NV50_EVO_CRTC_DITHER_CTRL                                    0x000008a0
+#define NV50_EVO_CRTC_DITHER_CTRL_OFF                                0x00000000
+#define NV50_EVO_CRTC_DITHER_CTRL_ON                                 0x00000011
+#define NV50_EVO_CRTC_SCALE_CTRL                                     0x000008a4
+#define NV50_EVO_CRTC_SCALE_CTRL_INACTIVE                            0x00000000
+#define NV50_EVO_CRTC_SCALE_CTRL_ACTIVE                              0x00000009
+#define NV50_EVO_CRTC_COLOR_CTRL                                     0x000008a8
+#define NV50_EVO_CRTC_COLOR_CTRL_COLOR                               0x00040000
+#define NV50_EVO_CRTC_FB_POS                                         0x000008c0
+#define NV50_EVO_CRTC_REAL_RES                                       0x000008c8
+#define NV50_EVO_CRTC_SCALE_CENTER_OFFSET                            0x000008d4
+#define NV50_EVO_CRTC_SCALE_CENTER_OFFSET_VAL(x, y) \
+	((((unsigned)y << 16) & 0xFFFF0000) | (((unsigned)x) & 0x0000FFFF))
+/* Both of these are needed, otherwise nothing happens. */
+#define NV50_EVO_CRTC_SCALE_RES1                                     0x000008d8
+#define NV50_EVO_CRTC_SCALE_RES2                                     0x000008dc
+
diff --git a/drivers/gpu/drm/nouveau/nv50_fbcon.c b/drivers/gpu/drm/nouveau/nv50_fbcon.c
new file mode 100644
index 0000000..6bcc6d3
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv50_fbcon.c
@@ -0,0 +1,273 @@
+#include "drmP.h"
+#include "nouveau_drv.h"
+#include "nouveau_dma.h"
+#include "nouveau_fbcon.h"
+
+static void
+nv50_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
+{
+	struct nouveau_fbcon_par *par = info->par;
+	struct drm_device *dev = par->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_channel *chan = dev_priv->channel;
+
+	if (info->state != FBINFO_STATE_RUNNING)
+		return;
+
+	if (!(info->flags & FBINFO_HWACCEL_DISABLED) &&
+	     RING_SPACE(chan, rect->rop == ROP_COPY ? 7 : 11)) {
+		NV_ERROR(dev, "GPU lockup - switching to software fbcon\n");
+
+		info->flags |= FBINFO_HWACCEL_DISABLED;
+	}
+
+	if (info->flags & FBINFO_HWACCEL_DISABLED) {
+		cfb_fillrect(info, rect);
+		return;
+	}
+
+	if (rect->rop != ROP_COPY) {
+		BEGIN_RING(chan, NvSub2D, 0x02ac, 1);
+		OUT_RING(chan, 1);
+	}
+	BEGIN_RING(chan, NvSub2D, 0x0588, 1);
+	OUT_RING(chan, rect->color);
+	BEGIN_RING(chan, NvSub2D, 0x0600, 4);
+	OUT_RING(chan, rect->dx);
+	OUT_RING(chan, rect->dy);
+	OUT_RING(chan, rect->dx + rect->width);
+	OUT_RING(chan, rect->dy + rect->height);
+	if (rect->rop != ROP_COPY) {
+		BEGIN_RING(chan, NvSub2D, 0x02ac, 1);
+		OUT_RING(chan, 3);
+	}
+	FIRE_RING(chan);
+}
+
+static void
+nv50_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region)
+{
+	struct nouveau_fbcon_par *par = info->par;
+	struct drm_device *dev = par->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_channel *chan = dev_priv->channel;
+
+	if (info->state != FBINFO_STATE_RUNNING)
+		return;
+
+	if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 12)) {
+		NV_ERROR(dev, "GPU lockup - switching to software fbcon\n");
+
+		info->flags |= FBINFO_HWACCEL_DISABLED;
+	}
+
+	if (info->flags & FBINFO_HWACCEL_DISABLED) {
+		cfb_copyarea(info, region);
+		return;
+	}
+
+	BEGIN_RING(chan, NvSub2D, 0x0110, 1);
+	OUT_RING(chan, 0);
+	BEGIN_RING(chan, NvSub2D, 0x08b0, 4);
+	OUT_RING(chan, region->dx);
+	OUT_RING(chan, region->dy);
+	OUT_RING(chan, region->width);
+	OUT_RING(chan, region->height);
+	BEGIN_RING(chan, NvSub2D, 0x08d0, 4);
+	OUT_RING(chan, 0);
+	OUT_RING(chan, region->sx);
+	OUT_RING(chan, 0);
+	OUT_RING(chan, region->sy);
+	FIRE_RING(chan);
+}
+
+static void
+nv50_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
+{
+	struct nouveau_fbcon_par *par = info->par;
+	struct drm_device *dev = par->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_channel *chan = dev_priv->channel;
+	uint32_t width, dwords, *data = (uint32_t *)image->data;
+	uint32_t mask = ~(~0 >> (32 - info->var.bits_per_pixel));
+	uint32_t *palette = info->pseudo_palette;
+
+	if (info->state != FBINFO_STATE_RUNNING)
+		return;
+
+	if (image->depth != 1) {
+		cfb_imageblit(info, image);
+		return;
+	}
+
+	if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 11)) {
+		NV_ERROR(dev, "GPU lockup - switching to software fbcon\n");
+		info->flags |= FBINFO_HWACCEL_DISABLED;
+	}
+
+	if (info->flags & FBINFO_HWACCEL_DISABLED) {
+		cfb_imageblit(info, image);
+		return;
+	}
+
+	width = (image->width + 31) & ~31;
+	dwords = (width * image->height) >> 5;
+
+	BEGIN_RING(chan, NvSub2D, 0x0814, 2);
+	if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
+	    info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
+		OUT_RING(chan, palette[image->bg_color] | mask);
+		OUT_RING(chan, palette[image->fg_color] | mask);
+	} else {
+		OUT_RING(chan, image->bg_color);
+		OUT_RING(chan, image->fg_color);
+	}
+	BEGIN_RING(chan, NvSub2D, 0x0838, 2);
+	OUT_RING(chan, image->width);
+	OUT_RING(chan, image->height);
+	BEGIN_RING(chan, NvSub2D, 0x0850, 4);
+	OUT_RING(chan, 0);
+	OUT_RING(chan, image->dx);
+	OUT_RING(chan, 0);
+	OUT_RING(chan, image->dy);
+
+	while (dwords) {
+		int push = dwords > 2047 ? 2047 : dwords;
+
+		if (RING_SPACE(chan, push + 1)) {
+			NV_ERROR(dev,
+				 "GPU lockup - switching to software fbcon\n");
+			info->flags |= FBINFO_HWACCEL_DISABLED;
+			cfb_imageblit(info, image);
+			return;
+		}
+
+		dwords -= push;
+
+		BEGIN_RING(chan, NvSub2D, 0x40000860, push);
+		OUT_RINGp(chan, data, push);
+		data += push;
+	}
+
+	FIRE_RING(chan);
+}
+
+int
+nv50_fbcon_accel_init(struct fb_info *info)
+{
+	struct nouveau_fbcon_par *par = info->par;
+	struct drm_device *dev = par->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_channel *chan = dev_priv->channel;
+	struct nouveau_gpuobj *eng2d = NULL;
+	int ret, format;
+
+	switch (info->var.bits_per_pixel) {
+	case 8:
+		format = 0xf3;
+		break;
+	case 15:
+		format = 0xf8;
+		break;
+	case 16:
+		format = 0xe8;
+		break;
+	case 32:
+		switch (info->var.transp.length) {
+		case 0: /* depth 24 */
+		case 8: /* depth 32, just use 24.. */
+			format = 0xe6;
+			break;
+		case 2: /* depth 30 */
+			format = 0xd1;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = nouveau_gpuobj_gr_new(dev_priv->channel, 0x502d, &eng2d);
+	if (ret)
+		return ret;
+
+	ret = nouveau_gpuobj_ref_add(dev, dev_priv->channel, Nv2D, eng2d, NULL);
+	if (ret)
+		return ret;
+
+	ret = RING_SPACE(chan, 59);
+	if (ret) {
+		NV_ERROR(dev, "GPU lockup - switching to software fbcon\n");
+		return ret;
+	}
+
+	BEGIN_RING(chan, NvSub2D, 0x0000, 1);
+	OUT_RING(chan, Nv2D);
+	BEGIN_RING(chan, NvSub2D, 0x0180, 4);
+	OUT_RING(chan, NvNotify0);
+	OUT_RING(chan, chan->vram_handle);
+	OUT_RING(chan, chan->vram_handle);
+	OUT_RING(chan, chan->vram_handle);
+	BEGIN_RING(chan, NvSub2D, 0x0290, 1);
+	OUT_RING(chan, 0);
+	BEGIN_RING(chan, NvSub2D, 0x0888, 1);
+	OUT_RING(chan, 1);
+	BEGIN_RING(chan, NvSub2D, 0x02ac, 1);
+	OUT_RING(chan, 3);
+	BEGIN_RING(chan, NvSub2D, 0x02a0, 1);
+	OUT_RING(chan, 0x55);
+	BEGIN_RING(chan, NvSub2D, 0x08c0, 4);
+	OUT_RING(chan, 0);
+	OUT_RING(chan, 1);
+	OUT_RING(chan, 0);
+	OUT_RING(chan, 1);
+	BEGIN_RING(chan, NvSub2D, 0x0580, 2);
+	OUT_RING(chan, 4);
+	OUT_RING(chan, format);
+	BEGIN_RING(chan, NvSub2D, 0x02e8, 2);
+	OUT_RING(chan, 2);
+	OUT_RING(chan, 1);
+	BEGIN_RING(chan, NvSub2D, 0x0804, 1);
+	OUT_RING(chan, format);
+	BEGIN_RING(chan, NvSub2D, 0x0800, 1);
+	OUT_RING(chan, 1);
+	BEGIN_RING(chan, NvSub2D, 0x0808, 3);
+	OUT_RING(chan, 0);
+	OUT_RING(chan, 0);
+	OUT_RING(chan, 0);
+	BEGIN_RING(chan, NvSub2D, 0x081c, 1);
+	OUT_RING(chan, 1);
+	BEGIN_RING(chan, NvSub2D, 0x0840, 4);
+	OUT_RING(chan, 0);
+	OUT_RING(chan, 1);
+	OUT_RING(chan, 0);
+	OUT_RING(chan, 1);
+	BEGIN_RING(chan, NvSub2D, 0x0200, 2);
+	OUT_RING(chan, format);
+	OUT_RING(chan, 1);
+	BEGIN_RING(chan, NvSub2D, 0x0214, 5);
+	OUT_RING(chan, info->fix.line_length);
+	OUT_RING(chan, info->var.xres_virtual);
+	OUT_RING(chan, info->var.yres_virtual);
+	OUT_RING(chan, 0);
+	OUT_RING(chan, info->fix.smem_start - dev_priv->fb_phys +
+			 dev_priv->vm_vram_base);
+	BEGIN_RING(chan, NvSub2D, 0x0230, 2);
+	OUT_RING(chan, format);
+	OUT_RING(chan, 1);
+	BEGIN_RING(chan, NvSub2D, 0x0244, 5);
+	OUT_RING(chan, info->fix.line_length);
+	OUT_RING(chan, info->var.xres_virtual);
+	OUT_RING(chan, info->var.yres_virtual);
+	OUT_RING(chan, 0);
+	OUT_RING(chan, info->fix.smem_start - dev_priv->fb_phys +
+			 dev_priv->vm_vram_base);
+
+	info->fbops->fb_fillrect = nv50_fbcon_fillrect;
+	info->fbops->fb_copyarea = nv50_fbcon_copyarea;
+	info->fbops->fb_imageblit = nv50_fbcon_imageblit;
+	return 0;
+}
+
diff --git a/drivers/gpu/drm/nouveau/nv50_fifo.c b/drivers/gpu/drm/nouveau/nv50_fifo.c
new file mode 100644
index 0000000..77ae1aa
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv50_fifo.c
@@ -0,0 +1,494 @@
+/*
+ * Copyright (C) 2007 Ben Skeggs.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "drmP.h"
+#include "drm.h"
+#include "nouveau_drv.h"
+
+struct nv50_fifo_priv {
+	struct nouveau_gpuobj_ref *thingo[2];
+	int cur_thingo;
+};
+
+#define IS_G80 ((dev_priv->chipset & 0xf0) == 0x50)
+
+static void
+nv50_fifo_init_thingo(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nv50_fifo_priv *priv = dev_priv->engine.fifo.priv;
+	struct nouveau_gpuobj_ref *cur;
+	int i, nr;
+
+	NV_DEBUG(dev, "\n");
+
+	cur = priv->thingo[priv->cur_thingo];
+	priv->cur_thingo = !priv->cur_thingo;
+
+	/* We never schedule channel 0 or 127 */
+	dev_priv->engine.instmem.prepare_access(dev, true);
+	for (i = 1, nr = 0; i < 127; i++) {
+		if (dev_priv->fifos[i] && dev_priv->fifos[i]->ramfc)
+			nv_wo32(dev, cur->gpuobj, nr++, i);
+	}
+	dev_priv->engine.instmem.finish_access(dev);
+
+	nv_wr32(dev, 0x32f4, cur->instance >> 12);
+	nv_wr32(dev, 0x32ec, nr);
+	nv_wr32(dev, 0x2500, 0x101);
+}
+
+static int
+nv50_fifo_channel_enable(struct drm_device *dev, int channel, bool nt)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_channel *chan = dev_priv->fifos[channel];
+	uint32_t inst;
+
+	NV_DEBUG(dev, "ch%d\n", channel);
+
+	if (!chan->ramfc)
+		return -EINVAL;
+
+	if (IS_G80)
+		inst = chan->ramfc->instance >> 12;
+	else
+		inst = chan->ramfc->instance >> 8;
+	nv_wr32(dev, NV50_PFIFO_CTX_TABLE(channel),
+		 inst | NV50_PFIFO_CTX_TABLE_CHANNEL_ENABLED);
+
+	if (!nt)
+		nv50_fifo_init_thingo(dev);
+	return 0;
+}
+
+static void
+nv50_fifo_channel_disable(struct drm_device *dev, int channel, bool nt)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	uint32_t inst;
+
+	NV_DEBUG(dev, "ch%d, nt=%d\n", channel, nt);
+
+	if (IS_G80)
+		inst = NV50_PFIFO_CTX_TABLE_INSTANCE_MASK_G80;
+	else
+		inst = NV50_PFIFO_CTX_TABLE_INSTANCE_MASK_G84;
+	nv_wr32(dev, NV50_PFIFO_CTX_TABLE(channel), inst);
+
+	if (!nt)
+		nv50_fifo_init_thingo(dev);
+}
+
+static void
+nv50_fifo_init_reset(struct drm_device *dev)
+{
+	uint32_t pmc_e = NV_PMC_ENABLE_PFIFO;
+
+	NV_DEBUG(dev, "\n");
+
+	nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) & ~pmc_e);
+	nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) |  pmc_e);
+}
+
+static void
+nv50_fifo_init_intr(struct drm_device *dev)
+{
+	NV_DEBUG(dev, "\n");
+
+	nv_wr32(dev, NV03_PFIFO_INTR_0, 0xFFFFFFFF);
+	nv_wr32(dev, NV03_PFIFO_INTR_EN_0, 0xFFFFFFFF);
+}
+
+static void
+nv50_fifo_init_context_table(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	int i;
+
+	NV_DEBUG(dev, "\n");
+
+	for (i = 0; i < NV50_PFIFO_CTX_TABLE__SIZE; i++) {
+		if (dev_priv->fifos[i])
+			nv50_fifo_channel_enable(dev, i, true);
+		else
+			nv50_fifo_channel_disable(dev, i, true);
+	}
+
+	nv50_fifo_init_thingo(dev);
+}
+
+static void
+nv50_fifo_init_regs__nv(struct drm_device *dev)
+{
+	NV_DEBUG(dev, "\n");
+
+	nv_wr32(dev, 0x250c, 0x6f3cfc34);
+}
+
+static void
+nv50_fifo_init_regs(struct drm_device *dev)
+{
+	NV_DEBUG(dev, "\n");
+
+	nv_wr32(dev, 0x2500, 0);
+	nv_wr32(dev, 0x3250, 0);
+	nv_wr32(dev, 0x3220, 0);
+	nv_wr32(dev, 0x3204, 0);
+	nv_wr32(dev, 0x3210, 0);
+	nv_wr32(dev, 0x3270, 0);
+
+	/* Enable dummy channels setup by nv50_instmem.c */
+	nv50_fifo_channel_enable(dev, 0, true);
+	nv50_fifo_channel_enable(dev, 127, true);
+}
+
+int
+nv50_fifo_init(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nv50_fifo_priv *priv;
+	int ret;
+
+	NV_DEBUG(dev, "\n");
+
+	priv = dev_priv->engine.fifo.priv;
+	if (priv) {
+		priv->cur_thingo = !priv->cur_thingo;
+		goto just_reset;
+	}
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+	dev_priv->engine.fifo.priv = priv;
+
+	ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, 128*4, 0x1000,
+				     NVOBJ_FLAG_ZERO_ALLOC, &priv->thingo[0]);
+	if (ret) {
+		NV_ERROR(dev, "error creating thingo0: %d\n", ret);
+		return ret;
+	}
+
+	ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, 128*4, 0x1000,
+				     NVOBJ_FLAG_ZERO_ALLOC, &priv->thingo[1]);
+	if (ret) {
+		NV_ERROR(dev, "error creating thingo1: %d\n", ret);
+		return ret;
+	}
+
+just_reset:
+	nv50_fifo_init_reset(dev);
+	nv50_fifo_init_intr(dev);
+	nv50_fifo_init_context_table(dev);
+	nv50_fifo_init_regs__nv(dev);
+	nv50_fifo_init_regs(dev);
+	dev_priv->engine.fifo.enable(dev);
+	dev_priv->engine.fifo.reassign(dev, true);
+
+	return 0;
+}
+
+void
+nv50_fifo_takedown(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nv50_fifo_priv *priv = dev_priv->engine.fifo.priv;
+
+	NV_DEBUG(dev, "\n");
+
+	if (!priv)
+		return;
+
+	nouveau_gpuobj_ref_del(dev, &priv->thingo[0]);
+	nouveau_gpuobj_ref_del(dev, &priv->thingo[1]);
+
+	dev_priv->engine.fifo.priv = NULL;
+	kfree(priv);
+}
+
+int
+nv50_fifo_channel_id(struct drm_device *dev)
+{
+	return nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH1) &
+			NV50_PFIFO_CACHE1_PUSH1_CHID_MASK;
+}
+
+int
+nv50_fifo_create_context(struct nouveau_channel *chan)
+{
+	struct drm_device *dev = chan->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_gpuobj *ramfc = NULL;
+	int ret;
+
+	NV_DEBUG(dev, "ch%d\n", chan->id);
+
+	if (IS_G80) {
+		uint32_t ramin_poffset = chan->ramin->gpuobj->im_pramin->start;
+		uint32_t ramin_voffset = chan->ramin->gpuobj->im_backing_start;
+
+		ret = nouveau_gpuobj_new_fake(dev, ramin_poffset, ramin_voffset,
+					      0x100, NVOBJ_FLAG_ZERO_ALLOC |
+					      NVOBJ_FLAG_ZERO_FREE, &ramfc,
+					      &chan->ramfc);
+		if (ret)
+			return ret;
+
+		ret = nouveau_gpuobj_new_fake(dev, ramin_poffset + 0x0400,
+					      ramin_voffset + 0x0400, 4096,
+					      0, NULL, &chan->cache);
+		if (ret)
+			return ret;
+	} else {
+		ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, 0x100, 256,
+					     NVOBJ_FLAG_ZERO_ALLOC |
+					     NVOBJ_FLAG_ZERO_FREE,
+					     &chan->ramfc);
+		if (ret)
+			return ret;
+		ramfc = chan->ramfc->gpuobj;
+
+		ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, 4096, 256,
+					     0, &chan->cache);
+		if (ret)
+			return ret;
+	}
+
+	dev_priv->engine.instmem.prepare_access(dev, true);
+
+	nv_wo32(dev, ramfc, 0x08/4, chan->pushbuf_base);
+	nv_wo32(dev, ramfc, 0x10/4, chan->pushbuf_base);
+	nv_wo32(dev, ramfc, 0x48/4, chan->pushbuf->instance >> 4);
+	nv_wo32(dev, ramfc, 0x80/4, (0xc << 24) | (chan->ramht->instance >> 4));
+	nv_wo32(dev, ramfc, 0x3c/4, 0x00086078);
+	nv_wo32(dev, ramfc, 0x44/4, 0x2101ffff);
+	nv_wo32(dev, ramfc, 0x60/4, 0x7fffffff);
+	nv_wo32(dev, ramfc, 0x40/4, 0x00000000);
+	nv_wo32(dev, ramfc, 0x7c/4, 0x30000001);
+	nv_wo32(dev, ramfc, 0x78/4, 0x00000000);
+	nv_wo32(dev, ramfc, 0x4c/4, 0xffffffff);
+
+	if (!IS_G80) {
+		nv_wo32(dev, chan->ramin->gpuobj, 0, chan->id);
+		nv_wo32(dev, chan->ramin->gpuobj, 1,
+						chan->ramfc->instance >> 8);
+
+		nv_wo32(dev, ramfc, 0x88/4, chan->cache->instance >> 10);
+		nv_wo32(dev, ramfc, 0x98/4, chan->ramin->instance >> 12);
+	}
+
+	dev_priv->engine.instmem.finish_access(dev);
+
+	ret = nv50_fifo_channel_enable(dev, chan->id, false);
+	if (ret) {
+		NV_ERROR(dev, "error enabling ch%d: %d\n", chan->id, ret);
+		nouveau_gpuobj_ref_del(dev, &chan->ramfc);
+		return ret;
+	}
+
+	return 0;
+}
+
+void
+nv50_fifo_destroy_context(struct nouveau_channel *chan)
+{
+	struct drm_device *dev = chan->dev;
+
+	NV_DEBUG(dev, "ch%d\n", chan->id);
+
+	nouveau_gpuobj_ref_del(dev, &chan->ramfc);
+	nouveau_gpuobj_ref_del(dev, &chan->cache);
+
+	nv50_fifo_channel_disable(dev, chan->id, false);
+
+	/* Dummy channel, also used on ch 127 */
+	if (chan->id == 0)
+		nv50_fifo_channel_disable(dev, 127, false);
+}
+
+int
+nv50_fifo_load_context(struct nouveau_channel *chan)
+{
+	struct drm_device *dev = chan->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_gpuobj *ramfc = chan->ramfc->gpuobj;
+	struct nouveau_gpuobj *cache = chan->cache->gpuobj;
+	int ptr, cnt;
+
+	NV_DEBUG(dev, "ch%d\n", chan->id);
+
+	dev_priv->engine.instmem.prepare_access(dev, false);
+
+	nv_wr32(dev, 0x3330, nv_ro32(dev, ramfc, 0x00/4));
+	nv_wr32(dev, 0x3334, nv_ro32(dev, ramfc, 0x04/4));
+	nv_wr32(dev, 0x3240, nv_ro32(dev, ramfc, 0x08/4));
+	nv_wr32(dev, 0x3320, nv_ro32(dev, ramfc, 0x0c/4));
+	nv_wr32(dev, 0x3244, nv_ro32(dev, ramfc, 0x10/4));
+	nv_wr32(dev, 0x3328, nv_ro32(dev, ramfc, 0x14/4));
+	nv_wr32(dev, 0x3368, nv_ro32(dev, ramfc, 0x18/4));
+	nv_wr32(dev, 0x336c, nv_ro32(dev, ramfc, 0x1c/4));
+	nv_wr32(dev, 0x3370, nv_ro32(dev, ramfc, 0x20/4));
+	nv_wr32(dev, 0x3374, nv_ro32(dev, ramfc, 0x24/4));
+	nv_wr32(dev, 0x3378, nv_ro32(dev, ramfc, 0x28/4));
+	nv_wr32(dev, 0x337c, nv_ro32(dev, ramfc, 0x2c/4));
+	nv_wr32(dev, 0x3228, nv_ro32(dev, ramfc, 0x30/4));
+	nv_wr32(dev, 0x3364, nv_ro32(dev, ramfc, 0x34/4));
+	nv_wr32(dev, 0x32a0, nv_ro32(dev, ramfc, 0x38/4));
+	nv_wr32(dev, 0x3224, nv_ro32(dev, ramfc, 0x3c/4));
+	nv_wr32(dev, 0x324c, nv_ro32(dev, ramfc, 0x40/4));
+	nv_wr32(dev, 0x2044, nv_ro32(dev, ramfc, 0x44/4));
+	nv_wr32(dev, 0x322c, nv_ro32(dev, ramfc, 0x48/4));
+	nv_wr32(dev, 0x3234, nv_ro32(dev, ramfc, 0x4c/4));
+	nv_wr32(dev, 0x3340, nv_ro32(dev, ramfc, 0x50/4));
+	nv_wr32(dev, 0x3344, nv_ro32(dev, ramfc, 0x54/4));
+	nv_wr32(dev, 0x3280, nv_ro32(dev, ramfc, 0x58/4));
+	nv_wr32(dev, 0x3254, nv_ro32(dev, ramfc, 0x5c/4));
+	nv_wr32(dev, 0x3260, nv_ro32(dev, ramfc, 0x60/4));
+	nv_wr32(dev, 0x3264, nv_ro32(dev, ramfc, 0x64/4));
+	nv_wr32(dev, 0x3268, nv_ro32(dev, ramfc, 0x68/4));
+	nv_wr32(dev, 0x326c, nv_ro32(dev, ramfc, 0x6c/4));
+	nv_wr32(dev, 0x32e4, nv_ro32(dev, ramfc, 0x70/4));
+	nv_wr32(dev, 0x3248, nv_ro32(dev, ramfc, 0x74/4));
+	nv_wr32(dev, 0x2088, nv_ro32(dev, ramfc, 0x78/4));
+	nv_wr32(dev, 0x2058, nv_ro32(dev, ramfc, 0x7c/4));
+	nv_wr32(dev, 0x2210, nv_ro32(dev, ramfc, 0x80/4));
+
+	cnt = nv_ro32(dev, ramfc, 0x84/4);
+	for (ptr = 0; ptr < cnt; ptr++) {
+		nv_wr32(dev, NV40_PFIFO_CACHE1_METHOD(ptr),
+			nv_ro32(dev, cache, (ptr * 2) + 0));
+		nv_wr32(dev, NV40_PFIFO_CACHE1_DATA(ptr),
+			nv_ro32(dev, cache, (ptr * 2) + 1));
+	}
+	nv_wr32(dev, 0x3210, cnt << 2);
+	nv_wr32(dev, 0x3270, 0);
+
+	/* guessing that all the 0x34xx regs aren't on NV50 */
+	if (!IS_G80) {
+		nv_wr32(dev, 0x340c, nv_ro32(dev, ramfc, 0x88/4));
+		nv_wr32(dev, 0x3400, nv_ro32(dev, ramfc, 0x8c/4));
+		nv_wr32(dev, 0x3404, nv_ro32(dev, ramfc, 0x90/4));
+		nv_wr32(dev, 0x3408, nv_ro32(dev, ramfc, 0x94/4));
+		nv_wr32(dev, 0x3410, nv_ro32(dev, ramfc, 0x98/4));
+	}
+
+	dev_priv->engine.instmem.finish_access(dev);
+
+	nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0);
+	nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, 0);
+	nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, chan->id | (1<<16));
+	return 0;
+}
+
+int
+nv50_fifo_unload_context(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
+	struct nouveau_gpuobj *ramfc, *cache;
+	struct nouveau_channel *chan = NULL;
+	int chid, get, put, ptr;
+
+	NV_DEBUG(dev, "\n");
+
+	chid = pfifo->channel_id(dev);
+	if (chid < 0 || chid >= dev_priv->engine.fifo.channels)
+		return 0;
+
+	chan = dev_priv->fifos[chid];
+	if (!chan) {
+		NV_ERROR(dev, "Inactive channel on PFIFO: %d\n", chid);
+		return -EINVAL;
+	}
+	NV_DEBUG(dev, "ch%d\n", chan->id);
+	ramfc = chan->ramfc->gpuobj;
+	cache = chan->cache->gpuobj;
+
+	dev_priv->engine.instmem.prepare_access(dev, true);
+
+	nv_wo32(dev, ramfc, 0x00/4, nv_rd32(dev, 0x3330));
+	nv_wo32(dev, ramfc, 0x04/4, nv_rd32(dev, 0x3334));
+	nv_wo32(dev, ramfc, 0x08/4, nv_rd32(dev, 0x3240));
+	nv_wo32(dev, ramfc, 0x0c/4, nv_rd32(dev, 0x3320));
+	nv_wo32(dev, ramfc, 0x10/4, nv_rd32(dev, 0x3244));
+	nv_wo32(dev, ramfc, 0x14/4, nv_rd32(dev, 0x3328));
+	nv_wo32(dev, ramfc, 0x18/4, nv_rd32(dev, 0x3368));
+	nv_wo32(dev, ramfc, 0x1c/4, nv_rd32(dev, 0x336c));
+	nv_wo32(dev, ramfc, 0x20/4, nv_rd32(dev, 0x3370));
+	nv_wo32(dev, ramfc, 0x24/4, nv_rd32(dev, 0x3374));
+	nv_wo32(dev, ramfc, 0x28/4, nv_rd32(dev, 0x3378));
+	nv_wo32(dev, ramfc, 0x2c/4, nv_rd32(dev, 0x337c));
+	nv_wo32(dev, ramfc, 0x30/4, nv_rd32(dev, 0x3228));
+	nv_wo32(dev, ramfc, 0x34/4, nv_rd32(dev, 0x3364));
+	nv_wo32(dev, ramfc, 0x38/4, nv_rd32(dev, 0x32a0));
+	nv_wo32(dev, ramfc, 0x3c/4, nv_rd32(dev, 0x3224));
+	nv_wo32(dev, ramfc, 0x40/4, nv_rd32(dev, 0x324c));
+	nv_wo32(dev, ramfc, 0x44/4, nv_rd32(dev, 0x2044));
+	nv_wo32(dev, ramfc, 0x48/4, nv_rd32(dev, 0x322c));
+	nv_wo32(dev, ramfc, 0x4c/4, nv_rd32(dev, 0x3234));
+	nv_wo32(dev, ramfc, 0x50/4, nv_rd32(dev, 0x3340));
+	nv_wo32(dev, ramfc, 0x54/4, nv_rd32(dev, 0x3344));
+	nv_wo32(dev, ramfc, 0x58/4, nv_rd32(dev, 0x3280));
+	nv_wo32(dev, ramfc, 0x5c/4, nv_rd32(dev, 0x3254));
+	nv_wo32(dev, ramfc, 0x60/4, nv_rd32(dev, 0x3260));
+	nv_wo32(dev, ramfc, 0x64/4, nv_rd32(dev, 0x3264));
+	nv_wo32(dev, ramfc, 0x68/4, nv_rd32(dev, 0x3268));
+	nv_wo32(dev, ramfc, 0x6c/4, nv_rd32(dev, 0x326c));
+	nv_wo32(dev, ramfc, 0x70/4, nv_rd32(dev, 0x32e4));
+	nv_wo32(dev, ramfc, 0x74/4, nv_rd32(dev, 0x3248));
+	nv_wo32(dev, ramfc, 0x78/4, nv_rd32(dev, 0x2088));
+	nv_wo32(dev, ramfc, 0x7c/4, nv_rd32(dev, 0x2058));
+	nv_wo32(dev, ramfc, 0x80/4, nv_rd32(dev, 0x2210));
+
+	put = (nv_rd32(dev, NV03_PFIFO_CACHE1_PUT) & 0x7ff) >> 2;
+	get = (nv_rd32(dev, NV03_PFIFO_CACHE1_GET) & 0x7ff) >> 2;
+	ptr = 0;
+	while (put != get) {
+		nv_wo32(dev, cache, ptr++,
+			    nv_rd32(dev, NV40_PFIFO_CACHE1_METHOD(get)));
+		nv_wo32(dev, cache, ptr++,
+			    nv_rd32(dev, NV40_PFIFO_CACHE1_DATA(get)));
+		get = (get + 1) & 0x1ff;
+	}
+
+	/* guessing that all the 0x34xx regs aren't on NV50 */
+	if (!IS_G80) {
+		nv_wo32(dev, ramfc, 0x84/4, ptr >> 1);
+		nv_wo32(dev, ramfc, 0x88/4, nv_rd32(dev, 0x340c));
+		nv_wo32(dev, ramfc, 0x8c/4, nv_rd32(dev, 0x3400));
+		nv_wo32(dev, ramfc, 0x90/4, nv_rd32(dev, 0x3404));
+		nv_wo32(dev, ramfc, 0x94/4, nv_rd32(dev, 0x3408));
+		nv_wo32(dev, ramfc, 0x98/4, nv_rd32(dev, 0x3410));
+	}
+
+	dev_priv->engine.instmem.finish_access(dev);
+
+	/*XXX: probably reload ch127 (NULL) state back too */
+	nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, 127);
+	return 0;
+}
+
diff --git a/drivers/gpu/drm/nouveau/nv50_graph.c b/drivers/gpu/drm/nouveau/nv50_graph.c
new file mode 100644
index 0000000..177d822
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv50_graph.c
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2007 Ben Skeggs.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "drmP.h"
+#include "drm.h"
+#include "nouveau_drv.h"
+
+MODULE_FIRMWARE("nouveau/nv50.ctxprog");
+MODULE_FIRMWARE("nouveau/nv50.ctxvals");
+MODULE_FIRMWARE("nouveau/nv84.ctxprog");
+MODULE_FIRMWARE("nouveau/nv84.ctxvals");
+MODULE_FIRMWARE("nouveau/nv86.ctxprog");
+MODULE_FIRMWARE("nouveau/nv86.ctxvals");
+MODULE_FIRMWARE("nouveau/nv92.ctxprog");
+MODULE_FIRMWARE("nouveau/nv92.ctxvals");
+MODULE_FIRMWARE("nouveau/nv94.ctxprog");
+MODULE_FIRMWARE("nouveau/nv94.ctxvals");
+MODULE_FIRMWARE("nouveau/nv96.ctxprog");
+MODULE_FIRMWARE("nouveau/nv96.ctxvals");
+MODULE_FIRMWARE("nouveau/nv98.ctxprog");
+MODULE_FIRMWARE("nouveau/nv98.ctxvals");
+MODULE_FIRMWARE("nouveau/nva0.ctxprog");
+MODULE_FIRMWARE("nouveau/nva0.ctxvals");
+MODULE_FIRMWARE("nouveau/nva5.ctxprog");
+MODULE_FIRMWARE("nouveau/nva5.ctxvals");
+MODULE_FIRMWARE("nouveau/nva8.ctxprog");
+MODULE_FIRMWARE("nouveau/nva8.ctxvals");
+MODULE_FIRMWARE("nouveau/nvaa.ctxprog");
+MODULE_FIRMWARE("nouveau/nvaa.ctxvals");
+MODULE_FIRMWARE("nouveau/nvac.ctxprog");
+MODULE_FIRMWARE("nouveau/nvac.ctxvals");
+
+#define IS_G80 ((dev_priv->chipset & 0xf0) == 0x50)
+
+static void
+nv50_graph_init_reset(struct drm_device *dev)
+{
+	uint32_t pmc_e = NV_PMC_ENABLE_PGRAPH | (1 << 21);
+
+	NV_DEBUG(dev, "\n");
+
+	nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) & ~pmc_e);
+	nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) |  pmc_e);
+}
+
+static void
+nv50_graph_init_intr(struct drm_device *dev)
+{
+	NV_DEBUG(dev, "\n");
+
+	nv_wr32(dev, NV03_PGRAPH_INTR, 0xffffffff);
+	nv_wr32(dev, 0x400138, 0xffffffff);
+	nv_wr32(dev, NV40_PGRAPH_INTR_EN, 0xffffffff);
+}
+
+static void
+nv50_graph_init_regs__nv(struct drm_device *dev)
+{
+	NV_DEBUG(dev, "\n");
+
+	nv_wr32(dev, 0x400804, 0xc0000000);
+	nv_wr32(dev, 0x406800, 0xc0000000);
+	nv_wr32(dev, 0x400c04, 0xc0000000);
+	nv_wr32(dev, 0x401804, 0xc0000000);
+	nv_wr32(dev, 0x405018, 0xc0000000);
+	nv_wr32(dev, 0x402000, 0xc0000000);
+
+	nv_wr32(dev, 0x400108, 0xffffffff);
+
+	nv_wr32(dev, 0x400824, 0x00004000);
+	nv_wr32(dev, 0x400500, 0x00010001);
+}
+
+static void
+nv50_graph_init_regs(struct drm_device *dev)
+{
+	NV_DEBUG(dev, "\n");
+
+	nv_wr32(dev, NV04_PGRAPH_DEBUG_3,
+				(1 << 2) /* HW_CONTEXT_SWITCH_ENABLED */);
+	nv_wr32(dev, 0x402ca8, 0x800);
+}
+
+static int
+nv50_graph_init_ctxctl(struct drm_device *dev)
+{
+	NV_DEBUG(dev, "\n");
+
+	nv40_grctx_init(dev);
+
+	nv_wr32(dev, 0x400320, 4);
+	nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, 0);
+	nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, 0);
+	return 0;
+}
+
+int
+nv50_graph_init(struct drm_device *dev)
+{
+	int ret;
+
+	NV_DEBUG(dev, "\n");
+
+	nv50_graph_init_reset(dev);
+	nv50_graph_init_regs__nv(dev);
+	nv50_graph_init_regs(dev);
+	nv50_graph_init_intr(dev);
+
+	ret = nv50_graph_init_ctxctl(dev);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+void
+nv50_graph_takedown(struct drm_device *dev)
+{
+	NV_DEBUG(dev, "\n");
+	nv40_grctx_fini(dev);
+}
+
+void
+nv50_graph_fifo_access(struct drm_device *dev, bool enabled)
+{
+	const uint32_t mask = 0x00010001;
+
+	if (enabled)
+		nv_wr32(dev, 0x400500, nv_rd32(dev, 0x400500) | mask);
+	else
+		nv_wr32(dev, 0x400500, nv_rd32(dev, 0x400500) & ~mask);
+}
+
+struct nouveau_channel *
+nv50_graph_channel(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	uint32_t inst;
+	int i;
+
+	inst = nv_rd32(dev, NV50_PGRAPH_CTXCTL_CUR);
+	if (!(inst & NV50_PGRAPH_CTXCTL_CUR_LOADED))
+		return NULL;
+	inst = (inst & NV50_PGRAPH_CTXCTL_CUR_INSTANCE) << 12;
+
+	for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
+		struct nouveau_channel *chan = dev_priv->fifos[i];
+
+		if (chan && chan->ramin && chan->ramin->instance == inst)
+			return chan;
+	}
+
+	return NULL;
+}
+
+int
+nv50_graph_create_context(struct nouveau_channel *chan)
+{
+	struct drm_device *dev = chan->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_gpuobj *ramin = chan->ramin->gpuobj;
+	struct nouveau_gpuobj *ctx;
+	uint32_t grctx_size = 0x70000;
+	int hdr, ret;
+
+	NV_DEBUG(dev, "ch%d\n", chan->id);
+
+	ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, grctx_size, 0x1000,
+				     NVOBJ_FLAG_ZERO_ALLOC |
+				     NVOBJ_FLAG_ZERO_FREE, &chan->ramin_grctx);
+	if (ret)
+		return ret;
+	ctx = chan->ramin_grctx->gpuobj;
+
+	hdr = IS_G80 ? 0x200 : 0x20;
+	dev_priv->engine.instmem.prepare_access(dev, true);
+	nv_wo32(dev, ramin, (hdr + 0x00)/4, 0x00190002);
+	nv_wo32(dev, ramin, (hdr + 0x04)/4, chan->ramin_grctx->instance +
+					   grctx_size - 1);
+	nv_wo32(dev, ramin, (hdr + 0x08)/4, chan->ramin_grctx->instance);
+	nv_wo32(dev, ramin, (hdr + 0x0c)/4, 0);
+	nv_wo32(dev, ramin, (hdr + 0x10)/4, 0);
+	nv_wo32(dev, ramin, (hdr + 0x14)/4, 0x00010000);
+	dev_priv->engine.instmem.finish_access(dev);
+
+	dev_priv->engine.instmem.prepare_access(dev, true);
+	nv40_grctx_vals_load(dev, ctx);
+	nv_wo32(dev, ctx, 0x00000/4, chan->ramin->instance >> 12);
+	if ((dev_priv->chipset & 0xf0) == 0xa0)
+		nv_wo32(dev, ctx, 0x00004/4, 0x00000000);
+	else
+		nv_wo32(dev, ctx, 0x0011c/4, 0x00000000);
+	dev_priv->engine.instmem.finish_access(dev);
+
+	return 0;
+}
+
+void
+nv50_graph_destroy_context(struct nouveau_channel *chan)
+{
+	struct drm_device *dev = chan->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	int i, hdr = IS_G80 ? 0x200 : 0x20;
+
+	NV_DEBUG(dev, "ch%d\n", chan->id);
+
+	if (!chan->ramin || !chan->ramin->gpuobj)
+		return;
+
+	dev_priv->engine.instmem.prepare_access(dev, true);
+	for (i = hdr; i < hdr + 24; i += 4)
+		nv_wo32(dev, chan->ramin->gpuobj, i/4, 0);
+	dev_priv->engine.instmem.finish_access(dev);
+
+	nouveau_gpuobj_ref_del(dev, &chan->ramin_grctx);
+}
+
+static int
+nv50_graph_do_load_context(struct drm_device *dev, uint32_t inst)
+{
+	uint32_t fifo = nv_rd32(dev, 0x400500);
+
+	nv_wr32(dev, 0x400500, fifo & ~1);
+	nv_wr32(dev, 0x400784, inst);
+	nv_wr32(dev, 0x400824, nv_rd32(dev, 0x400824) | 0x40);
+	nv_wr32(dev, 0x400320, nv_rd32(dev, 0x400320) | 0x11);
+	nv_wr32(dev, 0x400040, 0xffffffff);
+	(void)nv_rd32(dev, 0x400040);
+	nv_wr32(dev, 0x400040, 0x00000000);
+	nv_wr32(dev, 0x400304, nv_rd32(dev, 0x400304) | 1);
+
+	if (nouveau_wait_for_idle(dev))
+		nv_wr32(dev, 0x40032c, inst | (1<<31));
+	nv_wr32(dev, 0x400500, fifo);
+
+	return 0;
+}
+
+int
+nv50_graph_load_context(struct nouveau_channel *chan)
+{
+	uint32_t inst = chan->ramin->instance >> 12;
+
+	NV_DEBUG(chan->dev, "ch%d\n", chan->id);
+	return nv50_graph_do_load_context(chan->dev, inst);
+}
+
+int
+nv50_graph_unload_context(struct drm_device *dev)
+{
+	uint32_t inst, fifo = nv_rd32(dev, 0x400500);
+
+	inst  = nv_rd32(dev, NV50_PGRAPH_CTXCTL_CUR);
+	if (!(inst & NV50_PGRAPH_CTXCTL_CUR_LOADED))
+		return 0;
+	inst &= NV50_PGRAPH_CTXCTL_CUR_INSTANCE;
+
+	nv_wr32(dev, 0x400500, fifo & ~1);
+	nv_wr32(dev, 0x400784, inst);
+	nv_wr32(dev, 0x400824, nv_rd32(dev, 0x400824) | 0x20);
+	nv_wr32(dev, 0x400304, nv_rd32(dev, 0x400304) | 0x01);
+	nouveau_wait_for_idle(dev);
+	nv_wr32(dev, 0x400500, fifo);
+
+	nv_wr32(dev, NV50_PGRAPH_CTXCTL_CUR, inst);
+	return 0;
+}
+
+void
+nv50_graph_context_switch(struct drm_device *dev)
+{
+	uint32_t inst;
+
+	nv50_graph_unload_context(dev);
+
+	inst  = nv_rd32(dev, NV50_PGRAPH_CTXCTL_NEXT);
+	inst &= NV50_PGRAPH_CTXCTL_NEXT_INSTANCE;
+	nv50_graph_do_load_context(dev, inst);
+
+	nv_wr32(dev, NV40_PGRAPH_INTR_EN, nv_rd32(dev,
+		NV40_PGRAPH_INTR_EN) | NV_PGRAPH_INTR_CONTEXT_SWITCH);
+}
+
+static int
+nv50_graph_nvsw_dma_vblsem(struct nouveau_channel *chan, int grclass,
+			   int mthd, uint32_t data)
+{
+	struct nouveau_gpuobj_ref *ref = NULL;
+
+	if (nouveau_gpuobj_ref_find(chan, data, &ref))
+		return -ENOENT;
+
+	if (nouveau_notifier_offset(ref->gpuobj, NULL))
+		return -EINVAL;
+
+	chan->nvsw.vblsem = ref->gpuobj;
+	chan->nvsw.vblsem_offset = ~0;
+	return 0;
+}
+
+static int
+nv50_graph_nvsw_vblsem_offset(struct nouveau_channel *chan, int grclass,
+			      int mthd, uint32_t data)
+{
+	if (nouveau_notifier_offset(chan->nvsw.vblsem, &data))
+		return -ERANGE;
+
+	chan->nvsw.vblsem_offset = data >> 2;
+	return 0;
+}
+
+static int
+nv50_graph_nvsw_vblsem_release_val(struct nouveau_channel *chan, int grclass,
+				   int mthd, uint32_t data)
+{
+	chan->nvsw.vblsem_rval = data;
+	return 0;
+}
+
+static int
+nv50_graph_nvsw_vblsem_release(struct nouveau_channel *chan, int grclass,
+			       int mthd, uint32_t data)
+{
+	struct drm_device *dev = chan->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	if (!chan->nvsw.vblsem || chan->nvsw.vblsem_offset == ~0 || data > 1)
+		return -EINVAL;
+
+	if (!(nv_rd32(dev, NV50_PDISPLAY_INTR_EN) &
+		      NV50_PDISPLAY_INTR_EN_VBLANK_CRTC_(data))) {
+		nv_wr32(dev, NV50_PDISPLAY_INTR_1,
+			NV50_PDISPLAY_INTR_1_VBLANK_CRTC_(data));
+		nv_wr32(dev, NV50_PDISPLAY_INTR_EN, nv_rd32(dev,
+			NV50_PDISPLAY_INTR_EN) |
+			NV50_PDISPLAY_INTR_EN_VBLANK_CRTC_(data));
+	}
+
+	list_add(&chan->nvsw.vbl_wait, &dev_priv->vbl_waiting);
+	return 0;
+}
+
+static struct nouveau_pgraph_object_method nv50_graph_nvsw_methods[] = {
+	{ 0x018c, nv50_graph_nvsw_dma_vblsem },
+	{ 0x0400, nv50_graph_nvsw_vblsem_offset },
+	{ 0x0404, nv50_graph_nvsw_vblsem_release_val },
+	{ 0x0408, nv50_graph_nvsw_vblsem_release },
+	{}
+};
+
+struct nouveau_pgraph_object_class nv50_graph_grclass[] = {
+	{ 0x506e, true, nv50_graph_nvsw_methods }, /* nvsw */
+	{ 0x0030, false, NULL }, /* null */
+	{ 0x5039, false, NULL }, /* m2mf */
+	{ 0x502d, false, NULL }, /* 2d */
+	{ 0x50c0, false, NULL }, /* compute */
+	{ 0x5097, false, NULL }, /* tesla (nv50) */
+	{ 0x8297, false, NULL }, /* tesla (nv80/nv90) */
+	{ 0x8397, false, NULL }, /* tesla (nva0) */
+	{ 0x8597, false, NULL }, /* tesla (nva8) */
+	{}
+};
diff --git a/drivers/gpu/drm/nouveau/nv50_instmem.c b/drivers/gpu/drm/nouveau/nv50_instmem.c
new file mode 100644
index 0000000..94400f7
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv50_instmem.c
@@ -0,0 +1,509 @@
+/*
+ * Copyright (C) 2007 Ben Skeggs.
+ *
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "drmP.h"
+#include "drm.h"
+#include "nouveau_drv.h"
+
+struct nv50_instmem_priv {
+	uint32_t save1700[5]; /* 0x1700->0x1710 */
+
+	struct nouveau_gpuobj_ref *pramin_pt;
+	struct nouveau_gpuobj_ref *pramin_bar;
+	struct nouveau_gpuobj_ref *fb_bar;
+
+	bool last_access_wr;
+};
+
+#define NV50_INSTMEM_PAGE_SHIFT 12
+#define NV50_INSTMEM_PAGE_SIZE  (1 << NV50_INSTMEM_PAGE_SHIFT)
+#define NV50_INSTMEM_PT_SIZE(a)	(((a) >> 12) << 3)
+
+/*NOTE: - Assumes 0x1700 already covers the correct MiB of PRAMIN
+ */
+#define BAR0_WI32(g, o, v) do {                                   \
+	uint32_t offset;                                          \
+	if ((g)->im_backing) {                                    \
+		offset = (g)->im_backing_start;                   \
+	} else {                                                  \
+		offset  = chan->ramin->gpuobj->im_backing_start;  \
+		offset += (g)->im_pramin->start;                  \
+	}                                                         \
+	offset += (o);                                            \
+	nv_wr32(dev, NV_RAMIN + (offset & 0xfffff), (v));              \
+} while (0)
+
+int
+nv50_instmem_init(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_channel *chan;
+	uint32_t c_offset, c_size, c_ramfc, c_vmpd, c_base, pt_size;
+	struct nv50_instmem_priv *priv;
+	int ret, i;
+	uint32_t v, save_nv001700;
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+	dev_priv->engine.instmem.priv = priv;
+
+	/* Save state, will restore at takedown. */
+	for (i = 0x1700; i <= 0x1710; i += 4)
+		priv->save1700[(i-0x1700)/4] = nv_rd32(dev, i);
+
+	/* Reserve the last MiB of VRAM, we should probably try to avoid
+	 * setting up the below tables over the top of the VBIOS image at
+	 * some point.
+	 */
+	dev_priv->ramin_rsvd_vram = 1 << 20;
+	c_offset = nouveau_mem_fb_amount(dev) - dev_priv->ramin_rsvd_vram;
+	c_size   = 128 << 10;
+	c_vmpd   = ((dev_priv->chipset & 0xf0) == 0x50) ? 0x1400 : 0x200;
+	c_ramfc  = ((dev_priv->chipset & 0xf0) == 0x50) ? 0x0 : 0x20;
+	c_base   = c_vmpd + 0x4000;
+	pt_size  = NV50_INSTMEM_PT_SIZE(dev_priv->ramin_size);
+
+	NV_DEBUG(dev, " Rsvd VRAM base: 0x%08x\n", c_offset);
+	NV_DEBUG(dev, "    VBIOS image: 0x%08x\n",
+				(nv_rd32(dev, 0x619f04) & ~0xff) << 8);
+	NV_DEBUG(dev, "  Aperture size: %d MiB\n", dev_priv->ramin_size >> 20);
+	NV_DEBUG(dev, "        PT size: %d KiB\n", pt_size >> 10);
+
+	/* Determine VM layout, we need to do this first to make sure
+	 * we allocate enough memory for all the page tables.
+	 */
+	dev_priv->vm_gart_base = roundup(NV50_VM_BLOCK, NV50_VM_BLOCK);
+	dev_priv->vm_gart_size = NV50_VM_BLOCK;
+
+	dev_priv->vm_vram_base = dev_priv->vm_gart_base + dev_priv->vm_gart_size;
+	dev_priv->vm_vram_size = nouveau_mem_fb_amount(dev);
+	if (dev_priv->vm_vram_size > NV50_VM_MAX_VRAM)
+		dev_priv->vm_vram_size = NV50_VM_MAX_VRAM;
+	dev_priv->vm_vram_size = roundup(dev_priv->vm_vram_size, NV50_VM_BLOCK);
+	dev_priv->vm_vram_pt_nr = dev_priv->vm_vram_size / NV50_VM_BLOCK;
+
+	dev_priv->vm_end = dev_priv->vm_vram_base + dev_priv->vm_vram_size;
+
+	NV_DEBUG(dev, "NV50VM: GART 0x%016llx-0x%016llx\n",
+		 dev_priv->vm_gart_base,
+		 dev_priv->vm_gart_base + dev_priv->vm_gart_size - 1);
+	NV_DEBUG(dev, "NV50VM: VRAM 0x%016llx-0x%016llx\n",
+		 dev_priv->vm_vram_base,
+		 dev_priv->vm_vram_base + dev_priv->vm_vram_size - 1);
+
+	c_size += dev_priv->vm_vram_pt_nr * (NV50_VM_BLOCK / 65536 * 8);
+
+	/* Map BAR0 PRAMIN aperture over the memory we want to use */
+	save_nv001700 = nv_rd32(dev, NV50_PUNK_BAR0_PRAMIN);
+	nv_wr32(dev, NV50_PUNK_BAR0_PRAMIN, (c_offset >> 16));
+
+	/* Create a fake channel, and use it as our "dummy" channels 0/127.
+	 * The main reason for creating a channel is so we can use the gpuobj
+	 * code.  However, it's probably worth noting that NVIDIA also setup
+	 * their channels 0/127 with the same values they configure here.
+	 * So, there may be some other reason for doing this.
+	 *
+	 * Have to create the entire channel manually, as the real channel
+	 * creation code assumes we have PRAMIN access, and we don't until
+	 * we're done here.
+	 */
+	chan = kzalloc(sizeof(*chan), GFP_KERNEL);
+	if (!chan)
+		return -ENOMEM;
+	chan->id = 0;
+	chan->dev = dev;
+	chan->file_priv = (struct drm_file *)-2;
+	dev_priv->fifos[0] = dev_priv->fifos[127] = chan;
+
+	/* Channel's PRAMIN object + heap */
+	ret = nouveau_gpuobj_new_fake(dev, 0, c_offset, c_size, 0,
+							NULL, &chan->ramin);
+	if (ret)
+		return ret;
+
+	if (nouveau_mem_init_heap(&chan->ramin_heap, c_base, c_size - c_base))
+		return -ENOMEM;
+
+	/* RAMFC + zero channel's PRAMIN up to start of VM pagedir */
+	ret = nouveau_gpuobj_new_fake(dev, c_ramfc, c_offset + c_ramfc,
+						0x4000, 0, NULL, &chan->ramfc);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < c_vmpd; i += 4)
+		BAR0_WI32(chan->ramin->gpuobj, i, 0);
+
+	/* VM page directory */
+	ret = nouveau_gpuobj_new_fake(dev, c_vmpd, c_offset + c_vmpd,
+					   0x4000, 0, &chan->vm_pd, NULL);
+	if (ret)
+		return ret;
+	for (i = 0; i < 0x4000; i += 8) {
+		BAR0_WI32(chan->vm_pd, i + 0x00, 0x00000000);
+		BAR0_WI32(chan->vm_pd, i + 0x04, 0x00000000);
+	}
+
+	/* PRAMIN page table, cheat and map into VM at 0x0000000000.
+	 * We map the entire fake channel into the start of the PRAMIN BAR
+	 */
+	ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, pt_size, 0x1000,
+							0, &priv->pramin_pt);
+	if (ret)
+		return ret;
+
+	for (i = 0, v = c_offset; i < pt_size; i += 8, v += 0x1000) {
+		if (v < (c_offset + c_size))
+			BAR0_WI32(priv->pramin_pt->gpuobj, i + 0, v | 1);
+		else
+			BAR0_WI32(priv->pramin_pt->gpuobj, i + 0, 0x00000009);
+		BAR0_WI32(priv->pramin_pt->gpuobj, i + 4, 0x00000000);
+	}
+
+	BAR0_WI32(chan->vm_pd, 0x00, priv->pramin_pt->instance | 0x63);
+	BAR0_WI32(chan->vm_pd, 0x04, 0x00000000);
+
+	/* VRAM page table(s), mapped into VM at +1GiB  */
+	for (i = 0; i < dev_priv->vm_vram_pt_nr; i++) {
+		ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0,
+					     NV50_VM_BLOCK/65536*8, 0, 0,
+					     &chan->vm_vram_pt[i]);
+		if (ret) {
+			NV_ERROR(dev, "Error creating VRAM page tables: %d\n",
+									ret);
+			dev_priv->vm_vram_pt_nr = i;
+			return ret;
+		}
+		dev_priv->vm_vram_pt[i] = chan->vm_vram_pt[i]->gpuobj;
+
+		for (v = 0; v < dev_priv->vm_vram_pt[i]->im_pramin->size;
+								v += 4)
+			BAR0_WI32(dev_priv->vm_vram_pt[i], v, 0);
+
+		BAR0_WI32(chan->vm_pd, 0x10 + (i*8),
+			  chan->vm_vram_pt[i]->instance | 0x61);
+		BAR0_WI32(chan->vm_pd, 0x14 + (i*8), 0);
+	}
+
+	/* DMA object for PRAMIN BAR */
+	ret = nouveau_gpuobj_new_ref(dev, chan, chan, 0, 6*4, 16, 0,
+							&priv->pramin_bar);
+	if (ret)
+		return ret;
+	BAR0_WI32(priv->pramin_bar->gpuobj, 0x00, 0x7fc00000);
+	BAR0_WI32(priv->pramin_bar->gpuobj, 0x04, dev_priv->ramin_size - 1);
+	BAR0_WI32(priv->pramin_bar->gpuobj, 0x08, 0x00000000);
+	BAR0_WI32(priv->pramin_bar->gpuobj, 0x0c, 0x00000000);
+	BAR0_WI32(priv->pramin_bar->gpuobj, 0x10, 0x00000000);
+	BAR0_WI32(priv->pramin_bar->gpuobj, 0x14, 0x00000000);
+
+	/* DMA object for FB BAR */
+	ret = nouveau_gpuobj_new_ref(dev, chan, chan, 0, 6*4, 16, 0,
+							&priv->fb_bar);
+	if (ret)
+		return ret;
+	BAR0_WI32(priv->fb_bar->gpuobj, 0x00, 0x7fc00000);
+	BAR0_WI32(priv->fb_bar->gpuobj, 0x04, 0x40000000 +
+					      drm_get_resource_len(dev, 1) - 1);
+	BAR0_WI32(priv->fb_bar->gpuobj, 0x08, 0x40000000);
+	BAR0_WI32(priv->fb_bar->gpuobj, 0x0c, 0x00000000);
+	BAR0_WI32(priv->fb_bar->gpuobj, 0x10, 0x00000000);
+	BAR0_WI32(priv->fb_bar->gpuobj, 0x14, 0x00000000);
+
+	/* Poke the relevant regs, and pray it works :) */
+	nv_wr32(dev, NV50_PUNK_BAR_CFG_BASE, (chan->ramin->instance >> 12));
+	nv_wr32(dev, NV50_PUNK_UNK1710, 0);
+	nv_wr32(dev, NV50_PUNK_BAR_CFG_BASE, (chan->ramin->instance >> 12) |
+					 NV50_PUNK_BAR_CFG_BASE_VALID);
+	nv_wr32(dev, NV50_PUNK_BAR1_CTXDMA, (priv->fb_bar->instance >> 4) |
+					NV50_PUNK_BAR1_CTXDMA_VALID);
+	nv_wr32(dev, NV50_PUNK_BAR3_CTXDMA, (priv->pramin_bar->instance >> 4) |
+					NV50_PUNK_BAR3_CTXDMA_VALID);
+
+	for (i = 0; i < 8; i++)
+		nv_wr32(dev, 0x1900 + (i*4), 0);
+
+	/* Assume that praying isn't enough, check that we can re-read the
+	 * entire fake channel back from the PRAMIN BAR */
+	dev_priv->engine.instmem.prepare_access(dev, false);
+	for (i = 0; i < c_size; i += 4) {
+		if (nv_rd32(dev, NV_RAMIN + i) != nv_ri32(dev, i)) {
+			NV_ERROR(dev, "Error reading back PRAMIN at 0x%08x\n",
+									i);
+			dev_priv->engine.instmem.finish_access(dev);
+			return -EINVAL;
+		}
+	}
+	dev_priv->engine.instmem.finish_access(dev);
+
+	nv_wr32(dev, NV50_PUNK_BAR0_PRAMIN, save_nv001700);
+
+	/* Global PRAMIN heap */
+	if (nouveau_mem_init_heap(&dev_priv->ramin_heap,
+				  c_size, dev_priv->ramin_size - c_size)) {
+		dev_priv->ramin_heap = NULL;
+		NV_ERROR(dev, "Failed to init RAMIN heap\n");
+	}
+
+	/*XXX: incorrect, but needed to make hash func "work" */
+	dev_priv->ramht_offset = 0x10000;
+	dev_priv->ramht_bits   = 9;
+	dev_priv->ramht_size   = (1 << dev_priv->ramht_bits);
+	return 0;
+}
+
+void
+nv50_instmem_takedown(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv;
+	struct nouveau_channel *chan = dev_priv->fifos[0];
+	int i;
+
+	NV_DEBUG(dev, "\n");
+
+	if (!priv)
+		return;
+
+	/* Restore state from before init */
+	for (i = 0x1700; i <= 0x1710; i += 4)
+		nv_wr32(dev, i, priv->save1700[(i - 0x1700) / 4]);
+
+	nouveau_gpuobj_ref_del(dev, &priv->fb_bar);
+	nouveau_gpuobj_ref_del(dev, &priv->pramin_bar);
+	nouveau_gpuobj_ref_del(dev, &priv->pramin_pt);
+
+	/* Destroy dummy channel */
+	if (chan) {
+		for (i = 0; i < dev_priv->vm_vram_pt_nr; i++) {
+			nouveau_gpuobj_ref_del(dev, &chan->vm_vram_pt[i]);
+			dev_priv->vm_vram_pt[i] = NULL;
+		}
+		dev_priv->vm_vram_pt_nr = 0;
+
+		nouveau_gpuobj_del(dev, &chan->vm_pd);
+		nouveau_gpuobj_ref_del(dev, &chan->ramfc);
+		nouveau_gpuobj_ref_del(dev, &chan->ramin);
+		nouveau_mem_takedown(&chan->ramin_heap);
+
+		dev_priv->fifos[0] = dev_priv->fifos[127] = NULL;
+		kfree(chan);
+	}
+
+	dev_priv->engine.instmem.priv = NULL;
+	kfree(priv);
+}
+
+int
+nv50_instmem_suspend(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_channel *chan = dev_priv->fifos[0];
+	struct nouveau_gpuobj *ramin = chan->ramin->gpuobj;
+	int i;
+
+	ramin->im_backing_suspend = vmalloc(ramin->im_pramin->size);
+	if (!ramin->im_backing_suspend)
+		return -ENOMEM;
+
+	for (i = 0; i < ramin->im_pramin->size; i += 4)
+		ramin->im_backing_suspend[i/4] = nv_ri32(dev, i);
+	return 0;
+}
+
+void
+nv50_instmem_resume(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv;
+	struct nouveau_channel *chan = dev_priv->fifos[0];
+	struct nouveau_gpuobj *ramin = chan->ramin->gpuobj;
+	int i;
+
+	nv_wr32(dev, NV50_PUNK_BAR0_PRAMIN, (ramin->im_backing_start >> 16));
+	for (i = 0; i < ramin->im_pramin->size; i += 4)
+		BAR0_WI32(ramin, i, ramin->im_backing_suspend[i/4]);
+	vfree(ramin->im_backing_suspend);
+	ramin->im_backing_suspend = NULL;
+
+	/* Poke the relevant regs, and pray it works :) */
+	nv_wr32(dev, NV50_PUNK_BAR_CFG_BASE, (chan->ramin->instance >> 12));
+	nv_wr32(dev, NV50_PUNK_UNK1710, 0);
+	nv_wr32(dev, NV50_PUNK_BAR_CFG_BASE, (chan->ramin->instance >> 12) |
+					 NV50_PUNK_BAR_CFG_BASE_VALID);
+	nv_wr32(dev, NV50_PUNK_BAR1_CTXDMA, (priv->fb_bar->instance >> 4) |
+					NV50_PUNK_BAR1_CTXDMA_VALID);
+	nv_wr32(dev, NV50_PUNK_BAR3_CTXDMA, (priv->pramin_bar->instance >> 4) |
+					NV50_PUNK_BAR3_CTXDMA_VALID);
+
+	for (i = 0; i < 8; i++)
+		nv_wr32(dev, 0x1900 + (i*4), 0);
+}
+
+int
+nv50_instmem_populate(struct drm_device *dev, struct nouveau_gpuobj *gpuobj,
+		      uint32_t *sz)
+{
+	int ret;
+
+	if (gpuobj->im_backing)
+		return -EINVAL;
+
+	*sz = (*sz + (NV50_INSTMEM_PAGE_SIZE-1)) & ~(NV50_INSTMEM_PAGE_SIZE-1);
+	if (*sz == 0)
+		return -EINVAL;
+
+	ret = nouveau_bo_new(dev, NULL, *sz, 0, TTM_PL_FLAG_VRAM, 0, 0x0000,
+			     true, false, &gpuobj->im_backing);
+	if (ret) {
+		NV_ERROR(dev, "error getting PRAMIN backing pages: %d\n", ret);
+		return ret;
+	}
+
+	ret = nouveau_bo_pin(gpuobj->im_backing, TTM_PL_FLAG_VRAM);
+	if (ret) {
+		NV_ERROR(dev, "error pinning PRAMIN backing VRAM: %d\n", ret);
+		nouveau_bo_ref(NULL, &gpuobj->im_backing);
+		return ret;
+	}
+
+	gpuobj->im_backing_start = gpuobj->im_backing->bo.mem.mm_node->start;
+	gpuobj->im_backing_start <<= PAGE_SHIFT;
+
+	return 0;
+}
+
+void
+nv50_instmem_clear(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	if (gpuobj && gpuobj->im_backing) {
+		if (gpuobj->im_bound)
+			dev_priv->engine.instmem.unbind(dev, gpuobj);
+		nouveau_bo_unpin(gpuobj->im_backing);
+		nouveau_bo_ref(NULL, &gpuobj->im_backing);
+		gpuobj->im_backing = NULL;
+	}
+}
+
+int
+nv50_instmem_bind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv;
+	uint32_t pte, pte_end, vram;
+
+	if (!gpuobj->im_backing || !gpuobj->im_pramin || gpuobj->im_bound)
+		return -EINVAL;
+
+	NV_DEBUG(dev, "st=0x%0llx sz=0x%0llx\n",
+		 gpuobj->im_pramin->start, gpuobj->im_pramin->size);
+
+	pte     = (gpuobj->im_pramin->start >> 12) << 3;
+	pte_end = ((gpuobj->im_pramin->size >> 12) << 3) + pte;
+	vram    = gpuobj->im_backing_start;
+
+	NV_DEBUG(dev, "pramin=0x%llx, pte=%d, pte_end=%d\n",
+		 gpuobj->im_pramin->start, pte, pte_end);
+	NV_DEBUG(dev, "first vram page: 0x%08x\n", gpuobj->im_backing_start);
+
+	dev_priv->engine.instmem.prepare_access(dev, true);
+	while (pte < pte_end) {
+		nv_wo32(dev, priv->pramin_pt->gpuobj, (pte + 0)/4, vram | 1);
+		nv_wo32(dev, priv->pramin_pt->gpuobj, (pte + 4)/4, 0x00000000);
+
+		pte += 8;
+		vram += NV50_INSTMEM_PAGE_SIZE;
+	}
+	dev_priv->engine.instmem.finish_access(dev);
+
+	nv_wr32(dev, 0x100c80, 0x00040001);
+	if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
+		NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (1)\n");
+		NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
+		return -EBUSY;
+	}
+
+	nv_wr32(dev, 0x100c80, 0x00060001);
+	if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
+		NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
+		NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
+		return -EBUSY;
+	}
+
+	gpuobj->im_bound = 1;
+	return 0;
+}
+
+int
+nv50_instmem_unbind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv;
+	uint32_t pte, pte_end;
+
+	if (gpuobj->im_bound == 0)
+		return -EINVAL;
+
+	pte     = (gpuobj->im_pramin->start >> 12) << 3;
+	pte_end = ((gpuobj->im_pramin->size >> 12) << 3) + pte;
+
+	dev_priv->engine.instmem.prepare_access(dev, true);
+	while (pte < pte_end) {
+		nv_wo32(dev, priv->pramin_pt->gpuobj, (pte + 0)/4, 0x00000009);
+		nv_wo32(dev, priv->pramin_pt->gpuobj, (pte + 4)/4, 0x00000000);
+		pte += 8;
+	}
+	dev_priv->engine.instmem.finish_access(dev);
+
+	gpuobj->im_bound = 0;
+	return 0;
+}
+
+void
+nv50_instmem_prepare_access(struct drm_device *dev, bool write)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv;
+
+	priv->last_access_wr = write;
+}
+
+void
+nv50_instmem_finish_access(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv;
+
+	if (priv->last_access_wr) {
+		nv_wr32(dev, 0x070000, 0x00000001);
+		if (!nv_wait(0x070000, 0x00000001, 0x00000000))
+			NV_ERROR(dev, "PRAMIN flush timeout\n");
+	}
+}
+
diff --git a/drivers/gpu/drm/nouveau/nv50_mc.c b/drivers/gpu/drm/nouveau/nv50_mc.c
new file mode 100644
index 0000000..e0a9c3f
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv50_mc.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2007 Ben Skeggs.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "drmP.h"
+#include "drm.h"
+#include "nouveau_drv.h"
+
+int
+nv50_mc_init(struct drm_device *dev)
+{
+	nv_wr32(dev, NV03_PMC_ENABLE, 0xFFFFFFFF);
+	return 0;
+}
+
+void nv50_mc_takedown(struct drm_device *dev)
+{
+}
diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c
new file mode 100644
index 0000000..8c28046
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv50_sor.c
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2008 Maarten Maathuis.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "drmP.h"
+#include "drm_crtc_helper.h"
+
+#define NOUVEAU_DMA_DEBUG (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO)
+#include "nouveau_reg.h"
+#include "nouveau_drv.h"
+#include "nouveau_dma.h"
+#include "nouveau_encoder.h"
+#include "nouveau_connector.h"
+#include "nouveau_crtc.h"
+#include "nv50_display.h"
+
+static void
+nv50_sor_disconnect(struct nouveau_encoder *nv_encoder)
+{
+	struct drm_device *dev = to_drm_encoder(nv_encoder)->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_channel *evo = dev_priv->evo;
+	int ret;
+
+	NV_DEBUG(dev, "Disconnecting SOR %d\n", nv_encoder->or);
+
+	ret = RING_SPACE(evo, 2);
+	if (ret) {
+		NV_ERROR(dev, "no space while disconnecting SOR\n");
+		return;
+	}
+	BEGIN_RING(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1);
+	OUT_RING(evo, 0);
+}
+
+static void
+nv50_sor_dp_link_train(struct drm_encoder *encoder)
+{
+	struct drm_device *dev = encoder->dev;
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct bit_displayport_encoder_table *dpe;
+	int dpe_headerlen;
+
+	dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen);
+	if (!dpe) {
+		NV_ERROR(dev, "SOR-%d: no DP encoder table!\n", nv_encoder->or);
+		return;
+	}
+
+	if (dpe->script0) {
+		NV_DEBUG(dev, "SOR-%d: running DP script 0\n", nv_encoder->or);
+		nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script0),
+					    nv_encoder->dcb);
+	}
+
+	if (!nouveau_dp_link_train(encoder))
+		NV_ERROR(dev, "SOR-%d: link training failed\n", nv_encoder->or);
+
+	if (dpe->script1) {
+		NV_DEBUG(dev, "SOR-%d: running DP script 1\n", nv_encoder->or);
+		nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script1),
+					    nv_encoder->dcb);
+	}
+}
+
+static void
+nv50_sor_dpms(struct drm_encoder *encoder, int mode)
+{
+	struct drm_device *dev = encoder->dev;
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	uint32_t val;
+	int or = nv_encoder->or;
+
+	NV_DEBUG(dev, "or %d mode %d\n", or, mode);
+
+	/* wait for it to be done */
+	if (!nv_wait(NV50_PDISPLAY_SOR_DPMS_CTRL(or),
+		     NV50_PDISPLAY_SOR_DPMS_CTRL_PENDING, 0)) {
+		NV_ERROR(dev, "timeout: SOR_DPMS_CTRL_PENDING(%d) == 0\n", or);
+		NV_ERROR(dev, "SOR_DPMS_CTRL(%d) = 0x%08x\n", or,
+			 nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_CTRL(or)));
+	}
+
+	val = nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_CTRL(or));
+
+	if (mode == DRM_MODE_DPMS_ON)
+		val |= NV50_PDISPLAY_SOR_DPMS_CTRL_ON;
+	else
+		val &= ~NV50_PDISPLAY_SOR_DPMS_CTRL_ON;
+
+	nv_wr32(dev, NV50_PDISPLAY_SOR_DPMS_CTRL(or), val |
+		NV50_PDISPLAY_SOR_DPMS_CTRL_PENDING);
+	if (!nv_wait(NV50_PDISPLAY_SOR_DPMS_STATE(or),
+		     NV50_PDISPLAY_SOR_DPMS_STATE_WAIT, 0)) {
+		NV_ERROR(dev, "timeout: SOR_DPMS_STATE_WAIT(%d) == 0\n", or);
+		NV_ERROR(dev, "SOR_DPMS_STATE(%d) = 0x%08x\n", or,
+			 nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_STATE(or)));
+	}
+
+	if (nv_encoder->dcb->type == OUTPUT_DP && mode == DRM_MODE_DPMS_ON)
+		nv50_sor_dp_link_train(encoder);
+}
+
+static void
+nv50_sor_save(struct drm_encoder *encoder)
+{
+	NV_ERROR(encoder->dev, "!!\n");
+}
+
+static void
+nv50_sor_restore(struct drm_encoder *encoder)
+{
+	NV_ERROR(encoder->dev, "!!\n");
+}
+
+static bool
+nv50_sor_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
+		    struct drm_display_mode *adjusted_mode)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct nouveau_connector *connector;
+
+	NV_DEBUG(encoder->dev, "or %d\n", nv_encoder->or);
+
+	connector = nouveau_encoder_connector_get(nv_encoder);
+	if (!connector) {
+		NV_ERROR(encoder->dev, "Encoder has no connector\n");
+		return false;
+	}
+
+	if (connector->scaling_mode != DRM_MODE_SCALE_NONE &&
+	     connector->native_mode) {
+		int id = adjusted_mode->base.id;
+		*adjusted_mode = *connector->native_mode;
+		adjusted_mode->base.id = id;
+	}
+
+	return true;
+}
+
+static void
+nv50_sor_prepare(struct drm_encoder *encoder)
+{
+}
+
+static void
+nv50_sor_commit(struct drm_encoder *encoder)
+{
+}
+
+static void
+nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
+		  struct drm_display_mode *adjusted_mode)
+{
+	struct drm_nouveau_private *dev_priv = encoder->dev->dev_private;
+	struct nouveau_channel *evo = dev_priv->evo;
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct drm_device *dev = encoder->dev;
+	struct nouveau_crtc *crtc = nouveau_crtc(encoder->crtc);
+	uint32_t mode_ctl = 0;
+	int ret;
+
+	NV_DEBUG(dev, "or %d\n", nv_encoder->or);
+
+	nv50_sor_dpms(encoder, DRM_MODE_DPMS_ON);
+
+	switch (nv_encoder->dcb->type) {
+	case OUTPUT_TMDS:
+		if (nv_encoder->dcb->sorconf.link & 1) {
+			if (adjusted_mode->clock < 165000)
+				mode_ctl = 0x0100;
+			else
+				mode_ctl = 0x0500;
+		} else
+			mode_ctl = 0x0200;
+		break;
+	case OUTPUT_DP:
+		mode_ctl |= 0x00050000;
+		if (nv_encoder->dcb->sorconf.link & 1)
+			mode_ctl |= 0x00000800;
+		else
+			mode_ctl |= 0x00000900;
+		break;
+	default:
+		break;
+	}
+
+	if (crtc->index == 1)
+		mode_ctl |= NV50_EVO_SOR_MODE_CTRL_CRTC1;
+	else
+		mode_ctl |= NV50_EVO_SOR_MODE_CTRL_CRTC0;
+
+	if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC)
+		mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NHSYNC;
+
+	if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC)
+		mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NVSYNC;
+
+	ret = RING_SPACE(evo, 2);
+	if (ret) {
+		NV_ERROR(dev, "no space while connecting SOR\n");
+		return;
+	}
+	BEGIN_RING(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1);
+	OUT_RING(evo, mode_ctl);
+}
+
+static const struct drm_encoder_helper_funcs nv50_sor_helper_funcs = {
+	.dpms = nv50_sor_dpms,
+	.save = nv50_sor_save,
+	.restore = nv50_sor_restore,
+	.mode_fixup = nv50_sor_mode_fixup,
+	.prepare = nv50_sor_prepare,
+	.commit = nv50_sor_commit,
+	.mode_set = nv50_sor_mode_set,
+	.detect = NULL
+};
+
+static void
+nv50_sor_destroy(struct drm_encoder *encoder)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+
+	if (!encoder)
+		return;
+
+	NV_DEBUG(encoder->dev, "\n");
+
+	drm_encoder_cleanup(encoder);
+
+	kfree(nv_encoder);
+}
+
+static const struct drm_encoder_funcs nv50_sor_encoder_funcs = {
+	.destroy = nv50_sor_destroy,
+};
+
+int
+nv50_sor_create(struct drm_device *dev, struct dcb_entry *entry)
+{
+	struct nouveau_encoder *nv_encoder = NULL;
+	struct drm_encoder *encoder;
+	bool dum;
+	int type;
+
+	NV_DEBUG(dev, "\n");
+
+	switch (entry->type) {
+	case OUTPUT_TMDS:
+		NV_INFO(dev, "Detected a TMDS output\n");
+		type = DRM_MODE_ENCODER_TMDS;
+		break;
+	case OUTPUT_LVDS:
+		NV_INFO(dev, "Detected a LVDS output\n");
+		type = DRM_MODE_ENCODER_LVDS;
+
+		if (nouveau_bios_parse_lvds_table(dev, 0, &dum, &dum)) {
+			NV_ERROR(dev, "Failed parsing LVDS table\n");
+			return -EINVAL;
+		}
+		break;
+	case OUTPUT_DP:
+		NV_INFO(dev, "Detected a DP output\n");
+		type = DRM_MODE_ENCODER_TMDS;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
+	if (!nv_encoder)
+		return -ENOMEM;
+	encoder = to_drm_encoder(nv_encoder);
+
+	nv_encoder->dcb = entry;
+	nv_encoder->or = ffs(entry->or) - 1;
+
+	nv_encoder->disconnect = nv50_sor_disconnect;
+
+	drm_encoder_init(dev, encoder, &nv50_sor_encoder_funcs, type);
+	drm_encoder_helper_add(encoder, &nv50_sor_helper_funcs);
+
+	encoder->possible_crtcs = entry->heads;
+	encoder->possible_clones = 0;
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/nouveau/nvreg.h b/drivers/gpu/drm/nouveau/nvreg.h
new file mode 100644
index 0000000..5998c35
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nvreg.h
@@ -0,0 +1,535 @@
+/* $XConsortium: nvreg.h /main/2 1996/10/28 05:13:41 kaleb $ */
+/*
+ * Copyright 1996-1997  David J. McKay
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * DAVID J. MCKAY BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/nv/nvreg.h,v 1.6 2002/01/25 21:56:06 tsi Exp $ */
+
+#ifndef __NVREG_H_
+#define __NVREG_H_
+
+#define NV_PMC_OFFSET               0x00000000
+#define NV_PMC_SIZE                 0x00001000
+
+#define NV_PBUS_OFFSET              0x00001000
+#define NV_PBUS_SIZE                0x00001000
+
+#define NV_PFIFO_OFFSET             0x00002000
+#define NV_PFIFO_SIZE               0x00002000
+
+#define NV_HDIAG_OFFSET             0x00005000
+#define NV_HDIAG_SIZE               0x00001000
+
+#define NV_PRAM_OFFSET              0x00006000
+#define NV_PRAM_SIZE                0x00001000
+
+#define NV_PVIDEO_OFFSET            0x00008000
+#define NV_PVIDEO_SIZE              0x00001000
+
+#define NV_PTIMER_OFFSET            0x00009000
+#define NV_PTIMER_SIZE              0x00001000
+
+#define NV_PPM_OFFSET               0x0000A000
+#define NV_PPM_SIZE                 0x00001000
+
+#define NV_PTV_OFFSET               0x0000D000
+#define NV_PTV_SIZE                 0x00001000
+
+#define NV_PRMVGA_OFFSET            0x000A0000
+#define NV_PRMVGA_SIZE              0x00020000
+
+#define NV_PRMVIO0_OFFSET           0x000C0000
+#define NV_PRMVIO_SIZE              0x00002000
+#define NV_PRMVIO1_OFFSET           0x000C2000
+
+#define NV_PFB_OFFSET               0x00100000
+#define NV_PFB_SIZE                 0x00001000
+
+#define NV_PEXTDEV_OFFSET           0x00101000
+#define NV_PEXTDEV_SIZE             0x00001000
+
+#define NV_PME_OFFSET               0x00200000
+#define NV_PME_SIZE                 0x00001000
+
+#define NV_PROM_OFFSET              0x00300000
+#define NV_PROM_SIZE                0x00010000
+
+#define NV_PGRAPH_OFFSET            0x00400000
+#define NV_PGRAPH_SIZE              0x00010000
+
+#define NV_PCRTC0_OFFSET            0x00600000
+#define NV_PCRTC0_SIZE              0x00002000 /* empirical */
+
+#define NV_PRMCIO0_OFFSET           0x00601000
+#define NV_PRMCIO_SIZE              0x00002000
+#define NV_PRMCIO1_OFFSET           0x00603000
+
+#define NV50_DISPLAY_OFFSET           0x00610000
+#define NV50_DISPLAY_SIZE             0x0000FFFF
+
+#define NV_PRAMDAC0_OFFSET          0x00680000
+#define NV_PRAMDAC0_SIZE            0x00002000
+
+#define NV_PRMDIO0_OFFSET           0x00681000
+#define NV_PRMDIO_SIZE              0x00002000
+#define NV_PRMDIO1_OFFSET           0x00683000
+
+#define NV_PRAMIN_OFFSET            0x00700000
+#define NV_PRAMIN_SIZE              0x00100000
+
+#define NV_FIFO_OFFSET              0x00800000
+#define NV_FIFO_SIZE                0x00800000
+
+#define NV_PMC_BOOT_0			0x00000000
+#define NV_PMC_ENABLE			0x00000200
+
+#define NV_VIO_VSE2			0x000003c3
+#define NV_VIO_SRX			0x000003c4
+
+#define NV_CIO_CRX__COLOR		0x000003d4
+#define NV_CIO_CR__COLOR		0x000003d5
+
+#define NV_PBUS_DEBUG_1			0x00001084
+#define NV_PBUS_DEBUG_4			0x00001098
+#define NV_PBUS_DEBUG_DUALHEAD_CTL	0x000010f0
+#define NV_PBUS_POWERCTRL_1		0x00001584
+#define NV_PBUS_POWERCTRL_2		0x00001588
+#define NV_PBUS_POWERCTRL_4		0x00001590
+#define NV_PBUS_PCI_NV_19		0x0000184C
+#define NV_PBUS_PCI_NV_20		0x00001850
+#	define NV_PBUS_PCI_NV_20_ROM_SHADOW_DISABLED	(0 << 0)
+#	define NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED	(1 << 0)
+
+#define NV_PFIFO_RAMHT			0x00002210
+
+#define NV_PTV_TV_INDEX			0x0000d220
+#define NV_PTV_TV_DATA			0x0000d224
+#define NV_PTV_HFILTER			0x0000d310
+#define NV_PTV_HFILTER2			0x0000d390
+#define NV_PTV_VFILTER			0x0000d510
+
+#define NV_PRMVIO_MISC__WRITE		0x000c03c2
+#define NV_PRMVIO_SRX			0x000c03c4
+#define NV_PRMVIO_SR			0x000c03c5
+#	define NV_VIO_SR_RESET_INDEX		0x00
+#	define NV_VIO_SR_CLOCK_INDEX		0x01
+#	define NV_VIO_SR_PLANE_MASK_INDEX	0x02
+#	define NV_VIO_SR_CHAR_MAP_INDEX		0x03
+#	define NV_VIO_SR_MEM_MODE_INDEX		0x04
+#define NV_PRMVIO_MISC__READ		0x000c03cc
+#define NV_PRMVIO_GRX			0x000c03ce
+#define NV_PRMVIO_GX			0x000c03cf
+#	define NV_VIO_GX_SR_INDEX		0x00
+#	define NV_VIO_GX_SREN_INDEX		0x01
+#	define NV_VIO_GX_CCOMP_INDEX		0x02
+#	define NV_VIO_GX_ROP_INDEX		0x03
+#	define NV_VIO_GX_READ_MAP_INDEX		0x04
+#	define NV_VIO_GX_MODE_INDEX		0x05
+#	define NV_VIO_GX_MISC_INDEX		0x06
+#	define NV_VIO_GX_DONT_CARE_INDEX	0x07
+#	define NV_VIO_GX_BIT_MASK_INDEX		0x08
+
+#define NV_PFB_BOOT_0			0x00100000
+#define NV_PFB_CFG0			0x00100200
+#define NV_PFB_CFG1			0x00100204
+#define NV_PFB_CSTATUS			0x0010020C
+#define NV_PFB_REFCTRL			0x00100210
+#	define NV_PFB_REFCTRL_VALID_1			(1 << 31)
+#define NV_PFB_PAD			0x0010021C
+#	define NV_PFB_PAD_CKE_NORMAL			(1 << 0)
+#define NV_PFB_TILE_NV10		0x00100240
+#define NV_PFB_TILE_SIZE_NV10		0x00100244
+#define NV_PFB_REF			0x001002D0
+#	define NV_PFB_REF_CMD_REFRESH			(1 << 0)
+#define NV_PFB_PRE			0x001002D4
+#	define NV_PFB_PRE_CMD_PRECHARGE			(1 << 0)
+#define NV_PFB_CLOSE_PAGE2		0x0010033C
+#define NV_PFB_TILE_NV40		0x00100600
+#define NV_PFB_TILE_SIZE_NV40		0x00100604
+
+#define NV_PEXTDEV_BOOT_0		0x00101000
+#	define NV_PEXTDEV_BOOT_0_STRAP_FP_IFACE_12BIT	(8 << 12)
+#define NV_PEXTDEV_BOOT_3		0x0010100c
+
+#define NV_PCRTC_INTR_0					0x00600100
+#	define NV_PCRTC_INTR_0_VBLANK				(1 << 0)
+#define NV_PCRTC_INTR_EN_0				0x00600140
+#define NV_PCRTC_START					0x00600800
+#define NV_PCRTC_CONFIG					0x00600804
+#	define NV_PCRTC_CONFIG_START_ADDRESS_NON_VGA		(1 << 0)
+#	define NV_PCRTC_CONFIG_START_ADDRESS_HSYNC		(2 << 0)
+#define NV_PCRTC_CURSOR_CONFIG				0x00600810
+#	define NV_PCRTC_CURSOR_CONFIG_ENABLE_ENABLE		(1 << 0)
+#	define NV_PCRTC_CURSOR_CONFIG_DOUBLE_SCAN_ENABLE	(1 << 4)
+#	define NV_PCRTC_CURSOR_CONFIG_ADDRESS_SPACE_PNVM	(1 << 8)
+#	define NV_PCRTC_CURSOR_CONFIG_CUR_BPP_32		(1 << 12)
+#	define NV_PCRTC_CURSOR_CONFIG_CUR_PIXELS_64		(1 << 16)
+#	define NV_PCRTC_CURSOR_CONFIG_CUR_LINES_32		(2 << 24)
+#	define NV_PCRTC_CURSOR_CONFIG_CUR_LINES_64		(4 << 24)
+#	define NV_PCRTC_CURSOR_CONFIG_CUR_BLEND_ALPHA		(1 << 28)
+
+/* note: PCRTC_GPIO is not available on nv10, and in fact aliases 0x600810 */
+#define NV_PCRTC_GPIO					0x00600818
+#define NV_PCRTC_GPIO_EXT				0x0060081c
+#define NV_PCRTC_830					0x00600830
+#define NV_PCRTC_834					0x00600834
+#define NV_PCRTC_850					0x00600850
+#define NV_PCRTC_ENGINE_CTRL				0x00600860
+#	define NV_CRTC_FSEL_I2C					(1 << 4)
+#	define NV_CRTC_FSEL_OVERLAY				(1 << 12)
+
+#define NV_PRMCIO_ARX			0x006013c0
+#define NV_PRMCIO_AR__WRITE		0x006013c0
+#define NV_PRMCIO_AR__READ		0x006013c1
+#	define NV_CIO_AR_MODE_INDEX		0x10
+#	define NV_CIO_AR_OSCAN_INDEX		0x11
+#	define NV_CIO_AR_PLANE_INDEX		0x12
+#	define NV_CIO_AR_HPP_INDEX		0x13
+#	define NV_CIO_AR_CSEL_INDEX		0x14
+#define NV_PRMCIO_INP0			0x006013c2
+#define NV_PRMCIO_CRX__COLOR		0x006013d4
+#define NV_PRMCIO_CR__COLOR		0x006013d5
+	/* Standard VGA CRTC registers */
+#	define NV_CIO_CR_HDT_INDEX		0x00	/* horizontal display total */
+#	define NV_CIO_CR_HDE_INDEX		0x01	/* horizontal display end */
+#	define NV_CIO_CR_HBS_INDEX		0x02	/* horizontal blanking start */
+#	define NV_CIO_CR_HBE_INDEX		0x03	/* horizontal blanking end */
+#		define NV_CIO_CR_HBE_4_0		4:0
+#	define NV_CIO_CR_HRS_INDEX		0x04	/* horizontal retrace start */
+#	define NV_CIO_CR_HRE_INDEX		0x05	/* horizontal retrace end */
+#		define NV_CIO_CR_HRE_4_0		4:0
+#		define NV_CIO_CR_HRE_HBE_5		7:7
+#	define NV_CIO_CR_VDT_INDEX		0x06	/* vertical display total */
+#	define NV_CIO_CR_OVL_INDEX		0x07	/* overflow bits */
+#		define NV_CIO_CR_OVL_VDT_8		0:0
+#		define NV_CIO_CR_OVL_VDE_8		1:1
+#		define NV_CIO_CR_OVL_VRS_8		2:2
+#		define NV_CIO_CR_OVL_VBS_8		3:3
+#		define NV_CIO_CR_OVL_VDT_9		5:5
+#		define NV_CIO_CR_OVL_VDE_9		6:6
+#		define NV_CIO_CR_OVL_VRS_9		7:7
+#	define NV_CIO_CR_RSAL_INDEX		0x08	/* normally "preset row scan" */
+#	define NV_CIO_CR_CELL_HT_INDEX		0x09	/* cell height?! normally "max scan line" */
+#		define NV_CIO_CR_CELL_HT_VBS_9		5:5
+#		define NV_CIO_CR_CELL_HT_SCANDBL	7:7
+#	define NV_CIO_CR_CURS_ST_INDEX		0x0a	/* cursor start */
+#	define NV_CIO_CR_CURS_END_INDEX		0x0b	/* cursor end */
+#	define NV_CIO_CR_SA_HI_INDEX		0x0c	/* screen start address high */
+#	define NV_CIO_CR_SA_LO_INDEX		0x0d	/* screen start address low */
+#	define NV_CIO_CR_TCOFF_HI_INDEX		0x0e	/* cursor offset high */
+#	define NV_CIO_CR_TCOFF_LO_INDEX		0x0f	/* cursor offset low */
+#	define NV_CIO_CR_VRS_INDEX		0x10	/* vertical retrace start */
+#	define NV_CIO_CR_VRE_INDEX		0x11	/* vertical retrace end */
+#		define NV_CIO_CR_VRE_3_0		3:0
+#	define NV_CIO_CR_VDE_INDEX		0x12	/* vertical display end */
+#	define NV_CIO_CR_OFFSET_INDEX		0x13	/* sets screen pitch */
+#	define NV_CIO_CR_ULINE_INDEX		0x14	/* underline location */
+#	define NV_CIO_CR_VBS_INDEX		0x15	/* vertical blank start */
+#	define NV_CIO_CR_VBE_INDEX		0x16	/* vertical blank end */
+#	define NV_CIO_CR_MODE_INDEX		0x17	/* crtc mode control */
+#	define NV_CIO_CR_LCOMP_INDEX		0x18	/* line compare */
+	/* Extended VGA CRTC registers */
+#	define NV_CIO_CRE_RPC0_INDEX		0x19	/* repaint control 0 */
+#		define NV_CIO_CRE_RPC0_OFFSET_10_8	7:5
+#	define NV_CIO_CRE_RPC1_INDEX		0x1a	/* repaint control 1 */
+#		define NV_CIO_CRE_RPC1_LARGE		2:2
+#	define NV_CIO_CRE_FF_INDEX		0x1b	/* fifo control */
+#	define NV_CIO_CRE_ENH_INDEX		0x1c	/* enhanced? */
+#	define NV_CIO_SR_LOCK_INDEX		0x1f	/* crtc lock */
+#		define NV_CIO_SR_UNLOCK_RW_VALUE	0x57
+#		define NV_CIO_SR_LOCK_VALUE		0x99
+#	define NV_CIO_CRE_FFLWM__INDEX		0x20	/* fifo low water mark */
+#	define NV_CIO_CRE_21			0x21	/* vga shadow crtc lock */
+#	define NV_CIO_CRE_LSR_INDEX		0x25	/* ? */
+#		define NV_CIO_CRE_LSR_VDT_10		0:0
+#		define NV_CIO_CRE_LSR_VDE_10		1:1
+#		define NV_CIO_CRE_LSR_VRS_10		2:2
+#		define NV_CIO_CRE_LSR_VBS_10		3:3
+#		define NV_CIO_CRE_LSR_HBE_6		4:4
+#	define NV_CIO_CR_ARX_INDEX		0x26	/* attribute index -- ro copy of 0x60.3c0 */
+#	define NV_CIO_CRE_CHIP_ID_INDEX		0x27	/* chip revision */
+#	define NV_CIO_CRE_PIXEL_INDEX		0x28
+#		define NV_CIO_CRE_PIXEL_FORMAT		1:0
+#	define NV_CIO_CRE_HEB__INDEX		0x2d	/* horizontal extra bits? */
+#		define NV_CIO_CRE_HEB_HDT_8		0:0
+#		define NV_CIO_CRE_HEB_HDE_8		1:1
+#		define NV_CIO_CRE_HEB_HBS_8		2:2
+#		define NV_CIO_CRE_HEB_HRS_8		3:3
+#		define NV_CIO_CRE_HEB_ILC_8		4:4
+#	define NV_CIO_CRE_2E			0x2e	/* some scratch or dummy reg to force writes to sink in */
+#	define NV_CIO_CRE_HCUR_ADDR2_INDEX	0x2f	/* cursor */
+#	define NV_CIO_CRE_HCUR_ADDR0_INDEX	0x30		/* pixmap */
+#		define NV_CIO_CRE_HCUR_ADDR0_ADR	6:0
+#		define NV_CIO_CRE_HCUR_ASI		7:7
+#	define NV_CIO_CRE_HCUR_ADDR1_INDEX	0x31			/* address */
+#		define NV_CIO_CRE_HCUR_ADDR1_ENABLE	0:0
+#		define NV_CIO_CRE_HCUR_ADDR1_CUR_DBL	1:1
+#		define NV_CIO_CRE_HCUR_ADDR1_ADR	7:2
+#	define NV_CIO_CRE_LCD__INDEX		0x33
+#		define NV_CIO_CRE_LCD_LCD_SELECT	0:0
+#	define NV_CIO_CRE_DDC0_STATUS__INDEX	0x36
+#	define NV_CIO_CRE_DDC0_WR__INDEX	0x37
+#	define NV_CIO_CRE_ILACE__INDEX		0x39	/* interlace */
+#	define NV_CIO_CRE_SCRATCH3__INDEX	0x3b
+#	define NV_CIO_CRE_SCRATCH4__INDEX	0x3c
+#	define NV_CIO_CRE_DDC_STATUS__INDEX	0x3e
+#	define NV_CIO_CRE_DDC_WR__INDEX		0x3f
+#	define NV_CIO_CRE_EBR_INDEX		0x41	/* extra bits ? (vertical) */
+#		define NV_CIO_CRE_EBR_VDT_11		0:0
+#		define NV_CIO_CRE_EBR_VDE_11		2:2
+#		define NV_CIO_CRE_EBR_VRS_11		4:4
+#		define NV_CIO_CRE_EBR_VBS_11		6:6
+#	define NV_CIO_CRE_43			0x43
+#	define NV_CIO_CRE_44			0x44	/* head control */
+#	define NV_CIO_CRE_CSB			0x45	/* colour saturation boost */
+#	define NV_CIO_CRE_RCR			0x46
+#		define NV_CIO_CRE_RCR_ENDIAN_BIG	7:7
+#	define NV_CIO_CRE_47			0x47	/* extended fifo lwm, used on nv30+ */
+#	define NV_CIO_CRE_49			0x49
+#	define NV_CIO_CRE_4B			0x4b	/* given patterns in 0x[2-3][a-c] regs, probably scratch 6 */
+#	define NV_CIO_CRE_TVOUT_LATENCY		0x52
+#	define NV_CIO_CRE_53			0x53	/* `fp_htiming' according to Haiku */
+#	define NV_CIO_CRE_54			0x54	/* `fp_vtiming' according to Haiku */
+#	define NV_CIO_CRE_57			0x57	/* index reg for cr58 */
+#	define NV_CIO_CRE_58			0x58	/* data reg for cr57 */
+#	define NV_CIO_CRE_59			0x59	/* related to on/off-chip-ness of digital outputs */
+#	define NV_CIO_CRE_5B			0x5B	/* newer colour saturation reg */
+#	define NV_CIO_CRE_85			0x85
+#	define NV_CIO_CRE_86			0x86
+#define NV_PRMCIO_INP0__COLOR		0x006013da
+
+#define NV_PRAMDAC_CU_START_POS				0x00680300
+#	define NV_PRAMDAC_CU_START_POS_X			15:0
+#	define NV_PRAMDAC_CU_START_POS_Y			31:16
+#define NV_RAMDAC_NV10_CURSYNC				0x00680404
+
+#define NV_PRAMDAC_NVPLL_COEFF				0x00680500
+#define NV_PRAMDAC_MPLL_COEFF				0x00680504
+#define NV_PRAMDAC_VPLL_COEFF				0x00680508
+#	define NV30_RAMDAC_ENABLE_VCO2				(8 << 4)
+
+#define NV_PRAMDAC_PLL_COEFF_SELECT			0x0068050c
+#	define NV_PRAMDAC_PLL_COEFF_SELECT_USE_VPLL2_TRUE	(4 << 0)
+#	define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_MPLL	(1 << 8)
+#	define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_VPLL	(2 << 8)
+#	define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_NVPLL	(4 << 8)
+#	define NV_PRAMDAC_PLL_COEFF_SELECT_PLL_SOURCE_VPLL2	(8 << 8)
+#	define NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1		(1 << 16)
+#	define NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1		(2 << 16)
+#	define NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2		(4 << 16)
+#	define NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2		(8 << 16)
+#	define NV_PRAMDAC_PLL_COEFF_SELECT_TV_CLK_SOURCE_VIP	(1 << 20)
+#	define NV_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO_DB2	(1 << 28)
+#	define NV_PRAMDAC_PLL_COEFF_SELECT_VCLK2_RATIO_DB2	(2 << 28)
+
+#define NV_PRAMDAC_PLL_SETUP_CONTROL			0x00680510
+#define NV_RAMDAC_VPLL2					0x00680520
+#define NV_PRAMDAC_SEL_CLK				0x00680524
+#define NV_RAMDAC_DITHER_NV11				0x00680528
+#define NV_PRAMDAC_DACCLK				0x0068052c
+#	define NV_PRAMDAC_DACCLK_SEL_DACCLK			(1 << 0)
+
+#define NV_RAMDAC_NVPLL_B				0x00680570
+#define NV_RAMDAC_MPLL_B				0x00680574
+#define NV_RAMDAC_VPLL_B				0x00680578
+#define NV_RAMDAC_VPLL2_B				0x0068057c
+#	define NV31_RAMDAC_ENABLE_VCO2				(8 << 28)
+#define NV_PRAMDAC_580					0x00680580
+#	define NV_RAMDAC_580_VPLL1_ACTIVE			(1 << 8)
+#	define NV_RAMDAC_580_VPLL2_ACTIVE			(1 << 28)
+
+#define NV_PRAMDAC_GENERAL_CONTROL			0x00680600
+#	define NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON		(3 << 4)
+#	define NV_PRAMDAC_GENERAL_CONTROL_VGA_STATE_SEL		(1 << 8)
+#	define NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL		(1 << 12)
+#	define NV_PRAMDAC_GENERAL_CONTROL_TERMINATION_75OHM	(2 << 16)
+#	define NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS		(1 << 20)
+#	define NV_PRAMDAC_GENERAL_CONTROL_PIPE_LONG		(2 << 28)
+#define NV_PRAMDAC_TEST_CONTROL				0x00680608
+#	define NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED	(1 << 12)
+#	define NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF		(1 << 16)
+#	define NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI		(1 << 28)
+#define NV_PRAMDAC_TESTPOINT_DATA			0x00680610
+#	define NV_PRAMDAC_TESTPOINT_DATA_NOTBLANK		(8 << 28)
+#define NV_PRAMDAC_630					0x00680630
+#define NV_PRAMDAC_634					0x00680634
+
+#define NV_PRAMDAC_TV_SETUP				0x00680700
+#define NV_PRAMDAC_TV_VTOTAL				0x00680720
+#define NV_PRAMDAC_TV_VSKEW				0x00680724
+#define NV_PRAMDAC_TV_VSYNC_DELAY			0x00680728
+#define NV_PRAMDAC_TV_HTOTAL				0x0068072c
+#define NV_PRAMDAC_TV_HSKEW				0x00680730
+#define NV_PRAMDAC_TV_HSYNC_DELAY			0x00680734
+#define NV_PRAMDAC_TV_HSYNC_DELAY2			0x00680738
+
+#define NV_PRAMDAC_TV_SETUP                             0x00680700
+
+#define NV_PRAMDAC_FP_VDISPLAY_END			0x00680800
+#define NV_PRAMDAC_FP_VTOTAL				0x00680804
+#define NV_PRAMDAC_FP_VCRTC				0x00680808
+#define NV_PRAMDAC_FP_VSYNC_START			0x0068080c
+#define NV_PRAMDAC_FP_VSYNC_END				0x00680810
+#define NV_PRAMDAC_FP_VVALID_START			0x00680814
+#define NV_PRAMDAC_FP_VVALID_END			0x00680818
+#define NV_PRAMDAC_FP_HDISPLAY_END			0x00680820
+#define NV_PRAMDAC_FP_HTOTAL				0x00680824
+#define NV_PRAMDAC_FP_HCRTC				0x00680828
+#define NV_PRAMDAC_FP_HSYNC_START			0x0068082c
+#define NV_PRAMDAC_FP_HSYNC_END				0x00680830
+#define NV_PRAMDAC_FP_HVALID_START			0x00680834
+#define NV_PRAMDAC_FP_HVALID_END			0x00680838
+
+#define NV_RAMDAC_FP_DITHER				0x0068083c
+#define NV_PRAMDAC_FP_TG_CONTROL			0x00680848
+#	define NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS		(1 << 0)
+#	define NV_PRAMDAC_FP_TG_CONTROL_VSYNC_DISABLE		(2 << 0)
+#	define NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS		(1 << 4)
+#	define NV_PRAMDAC_FP_TG_CONTROL_HSYNC_DISABLE		(2 << 4)
+#	define NV_PRAMDAC_FP_TG_CONTROL_MODE_SCALE		(0 << 8)
+#	define NV_PRAMDAC_FP_TG_CONTROL_MODE_CENTER		(1 << 8)
+#	define NV_PRAMDAC_FP_TG_CONTROL_MODE_NATIVE		(2 << 8)
+#	define NV_PRAMDAC_FP_TG_CONTROL_READ_PROG		(1 << 20)
+#	define NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12		(1 << 24)
+#	define NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS		(1 << 28)
+#	define NV_PRAMDAC_FP_TG_CONTROL_DISPEN_DISABLE		(2 << 28)
+#define NV_PRAMDAC_FP_MARGIN_COLOR			0x0068084c
+#define NV_PRAMDAC_850					0x00680850
+#define NV_PRAMDAC_85C					0x0068085c
+#define NV_PRAMDAC_FP_DEBUG_0				0x00680880
+#	define NV_PRAMDAC_FP_DEBUG_0_XSCALE_ENABLE		(1 << 0)
+#	define NV_PRAMDAC_FP_DEBUG_0_YSCALE_ENABLE		(1 << 4)
+/* This doesn't seem to be essential for tmds, but still often set */
+#	define NV_RAMDAC_FP_DEBUG_0_TMDS_ENABLED		(8 << 4)
+#	define NV_PRAMDAC_FP_DEBUG_0_XINTERP_BILINEAR		(1 << 8)
+#	define NV_PRAMDAC_FP_DEBUG_0_YINTERP_BILINEAR		(1 << 12)
+#	define NV_PRAMDAC_FP_DEBUG_0_XWEIGHT_ROUND		(1 << 20)
+#	define NV_PRAMDAC_FP_DEBUG_0_YWEIGHT_ROUND		(1 << 24)
+#       define NV_PRAMDAC_FP_DEBUG_0_PWRDOWN_FPCLK              (1 << 28)
+#define NV_PRAMDAC_FP_DEBUG_1				0x00680884
+#	define NV_PRAMDAC_FP_DEBUG_1_XSCALE_VALUE		11:0
+#	define NV_PRAMDAC_FP_DEBUG_1_XSCALE_TESTMODE_ENABLE	(1 << 12)
+#	define NV_PRAMDAC_FP_DEBUG_1_YSCALE_VALUE		27:16
+#	define NV_PRAMDAC_FP_DEBUG_1_YSCALE_TESTMODE_ENABLE	(1 << 28)
+#define NV_PRAMDAC_FP_DEBUG_2				0x00680888
+#define NV_PRAMDAC_FP_DEBUG_3				0x0068088C
+
+/* see NV_PRAMDAC_INDIR_TMDS in rules.xml */
+#define NV_PRAMDAC_FP_TMDS_CONTROL			0x006808b0
+#	define NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE		(1 << 16)
+#define NV_PRAMDAC_FP_TMDS_DATA				0x006808b4
+
+#define NV_PRAMDAC_8C0                                  0x006808c0
+
+/* Some kind of switch */
+#define NV_PRAMDAC_900					0x00680900
+#define NV_PRAMDAC_A20					0x00680A20
+#define NV_PRAMDAC_A24					0x00680A24
+#define NV_PRAMDAC_A34					0x00680A34
+
+#define NV_PRAMDAC_CTV					0x00680c00
+
+/* names fabricated from NV_USER_DAC info */
+#define NV_PRMDIO_PIXEL_MASK		0x006813c6
+#	define NV_PRMDIO_PIXEL_MASK_MASK	0xff
+#define NV_PRMDIO_READ_MODE_ADDRESS	0x006813c7
+#define NV_PRMDIO_WRITE_MODE_ADDRESS	0x006813c8
+#define NV_PRMDIO_PALETTE_DATA		0x006813c9
+
+#define NV_PGRAPH_DEBUG_0		0x00400080
+#define NV_PGRAPH_DEBUG_1		0x00400084
+#define NV_PGRAPH_DEBUG_2_NV04		0x00400088
+#define NV_PGRAPH_DEBUG_2		0x00400620
+#define NV_PGRAPH_DEBUG_3		0x0040008c
+#define NV_PGRAPH_DEBUG_4		0x00400090
+#define NV_PGRAPH_INTR			0x00400100
+#define NV_PGRAPH_INTR_EN		0x00400140
+#define NV_PGRAPH_CTX_CONTROL		0x00400144
+#define NV_PGRAPH_CTX_CONTROL_NV04	0x00400170
+#define NV_PGRAPH_ABS_UCLIP_XMIN	0x0040053C
+#define NV_PGRAPH_ABS_UCLIP_YMIN	0x00400540
+#define NV_PGRAPH_ABS_UCLIP_XMAX	0x00400544
+#define NV_PGRAPH_ABS_UCLIP_YMAX	0x00400548
+#define NV_PGRAPH_BETA_AND		0x00400608
+#define NV_PGRAPH_LIMIT_VIOL_PIX	0x00400610
+#define NV_PGRAPH_BOFFSET0		0x00400640
+#define NV_PGRAPH_BOFFSET1		0x00400644
+#define NV_PGRAPH_BOFFSET2		0x00400648
+#define NV_PGRAPH_BLIMIT0		0x00400684
+#define NV_PGRAPH_BLIMIT1		0x00400688
+#define NV_PGRAPH_BLIMIT2		0x0040068c
+#define NV_PGRAPH_STATUS		0x00400700
+#define NV_PGRAPH_SURFACE		0x00400710
+#define NV_PGRAPH_STATE			0x00400714
+#define NV_PGRAPH_FIFO			0x00400720
+#define NV_PGRAPH_PATTERN_SHAPE		0x00400810
+#define NV_PGRAPH_TILE			0x00400b00
+
+#define NV_PVIDEO_INTR_EN		0x00008140
+#define NV_PVIDEO_BUFFER		0x00008700
+#define NV_PVIDEO_STOP			0x00008704
+#define NV_PVIDEO_UVPLANE_BASE(buff)	(0x00008800+(buff)*4)
+#define NV_PVIDEO_UVPLANE_LIMIT(buff)	(0x00008808+(buff)*4)
+#define NV_PVIDEO_UVPLANE_OFFSET_BUFF(buff)	(0x00008820+(buff)*4)
+#define NV_PVIDEO_BASE(buff)		(0x00008900+(buff)*4)
+#define NV_PVIDEO_LIMIT(buff)		(0x00008908+(buff)*4)
+#define NV_PVIDEO_LUMINANCE(buff)	(0x00008910+(buff)*4)
+#define NV_PVIDEO_CHROMINANCE(buff)	(0x00008918+(buff)*4)
+#define NV_PVIDEO_OFFSET_BUFF(buff)	(0x00008920+(buff)*4)
+#define NV_PVIDEO_SIZE_IN(buff)		(0x00008928+(buff)*4)
+#define NV_PVIDEO_POINT_IN(buff)	(0x00008930+(buff)*4)
+#define NV_PVIDEO_DS_DX(buff)		(0x00008938+(buff)*4)
+#define NV_PVIDEO_DT_DY(buff)		(0x00008940+(buff)*4)
+#define NV_PVIDEO_POINT_OUT(buff)	(0x00008948+(buff)*4)
+#define NV_PVIDEO_SIZE_OUT(buff)	(0x00008950+(buff)*4)
+#define NV_PVIDEO_FORMAT(buff)		(0x00008958+(buff)*4)
+#	define NV_PVIDEO_FORMAT_PLANAR			(1 << 0)
+#	define NV_PVIDEO_FORMAT_COLOR_LE_CR8YB8CB8YA8	(1 << 16)
+#	define NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY	(1 << 20)
+#	define NV_PVIDEO_FORMAT_MATRIX_ITURBT709	(1 << 24)
+#define NV_PVIDEO_COLOR_KEY		0x00008B00
+
+/* NV04 overlay defines from VIDIX & Haiku */
+#define NV_PVIDEO_INTR_EN_0		0x00680140
+#define NV_PVIDEO_STEP_SIZE		0x00680200
+#define NV_PVIDEO_CONTROL_Y		0x00680204
+#define NV_PVIDEO_CONTROL_X		0x00680208
+#define NV_PVIDEO_BUFF0_START_ADDRESS	0x0068020c
+#define NV_PVIDEO_BUFF0_PITCH_LENGTH	0x00680214
+#define NV_PVIDEO_BUFF0_OFFSET		0x0068021c
+#define NV_PVIDEO_BUFF1_START_ADDRESS	0x00680210
+#define NV_PVIDEO_BUFF1_PITCH_LENGTH	0x00680218
+#define NV_PVIDEO_BUFF1_OFFSET		0x00680220
+#define NV_PVIDEO_OE_STATE		0x00680224
+#define NV_PVIDEO_SU_STATE		0x00680228
+#define NV_PVIDEO_RM_STATE		0x0068022c
+#define NV_PVIDEO_WINDOW_START		0x00680230
+#define NV_PVIDEO_WINDOW_SIZE		0x00680234
+#define NV_PVIDEO_FIFO_THRES_SIZE	0x00680238
+#define NV_PVIDEO_FIFO_BURST_LENGTH	0x0068023c
+#define NV_PVIDEO_KEY			0x00680240
+#define NV_PVIDEO_OVERLAY		0x00680244
+#define NV_PVIDEO_RED_CSC_OFFSET	0x00680280
+#define NV_PVIDEO_GREEN_CSC_OFFSET	0x00680284
+#define NV_PVIDEO_BLUE_CSC_OFFSET	0x00680288
+#define NV_PVIDEO_CSC_ADJUST		0x0068028c
+
+#endif
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
index 2807724..c5c45e6 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.c
+++ b/drivers/gpu/drm/radeon/radeon_drv.c
@@ -121,7 +121,7 @@
 MODULE_PARM_DESC(tv, "TV enable (0 = disable)");
 module_param_named(tv, radeon_tv, int, 0444);
 
-MODULE_PARM_DESC(r4xx_atom, "Select new PLL code for AVIVO chips");
+MODULE_PARM_DESC(new_pll, "Select new PLL code for AVIVO chips");
 module_param_named(new_pll, radeon_new_pll, int, 0444);
 
 static int radeon_suspend(struct drm_device *dev, pm_message_t state)
diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c
index 2040937..544e18f 100644
--- a/drivers/gpu/drm/radeon/radeon_object.c
+++ b/drivers/gpu/drm/radeon/radeon_object.c
@@ -56,25 +56,6 @@
 	kfree(bo);
 }
 
-static inline u32 radeon_ttm_flags_from_domain(u32 domain)
-{
-	u32 flags = 0;
-
-	if (domain & RADEON_GEM_DOMAIN_VRAM) {
-		flags |= TTM_PL_FLAG_VRAM | TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED;
-	}
-	if (domain & RADEON_GEM_DOMAIN_GTT) {
-		flags |= TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING;
-	}
-	if (domain & RADEON_GEM_DOMAIN_CPU) {
-		flags |= TTM_PL_FLAG_SYSTEM | TTM_PL_MASK_CACHING;
-	}
-	if (!flags) {
-		flags |= TTM_PL_FLAG_SYSTEM | TTM_PL_MASK_CACHING;
-	}
-	return flags;
-}
-
 void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain)
 {
 	u32 c = 0;
@@ -100,7 +81,6 @@
 {
 	struct radeon_bo *bo;
 	enum ttm_bo_type type;
-	u32 flags;
 	int r;
 
 	if (unlikely(rdev->mman.bdev.dev_mapping == NULL)) {
@@ -120,16 +100,16 @@
 	bo->surface_reg = -1;
 	INIT_LIST_HEAD(&bo->list);
 
-	flags = radeon_ttm_flags_from_domain(domain);
+	radeon_ttm_placement_from_domain(bo, domain);
 	/* Kernel allocation are uninterruptible */
-	r = ttm_buffer_object_init(&rdev->mman.bdev, &bo->tbo, size, type,
-					flags, 0, 0, !kernel, NULL, size,
-					&radeon_ttm_bo_destroy);
+	r = ttm_bo_init(&rdev->mman.bdev, &bo->tbo, size, type,
+			&bo->placement, 0, 0, !kernel, NULL, size,
+			&radeon_ttm_bo_destroy);
 	if (unlikely(r != 0)) {
 		if (r != -ERESTARTSYS)
 			dev_err(rdev->dev,
-				"object_init failed for (%ld, 0x%08X)\n",
-				size, flags);
+				"object_init failed for (%lu, 0x%08X)\n",
+				size, domain);
 		return r;
 	}
 	*bo_ptr = bo;
@@ -199,7 +179,7 @@
 	radeon_ttm_placement_from_domain(bo, domain);
 	for (i = 0; i < bo->placement.num_placement; i++)
 		bo->placements[i] |= TTM_PL_FLAG_NO_EVICT;
-	r = ttm_buffer_object_validate(&bo->tbo, &bo->placement, false, false);
+	r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false);
 	if (likely(r == 0)) {
 		bo->pin_count = 1;
 		if (gpu_addr != NULL)
@@ -223,7 +203,7 @@
 		return 0;
 	for (i = 0; i < bo->placement.num_placement; i++)
 		bo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT;
-	r = ttm_buffer_object_validate(&bo->tbo, &bo->placement, false, false);
+	r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false);
 	if (unlikely(r != 0))
 		dev_err(bo->rdev->dev, "%p validate failed for unpin\n", bo);
 	return r;
@@ -336,8 +316,7 @@
 				radeon_ttm_placement_from_domain(bo,
 								lobj->rdomain);
 			}
-			r = ttm_buffer_object_validate(&bo->tbo,
-						&bo->placement,
+			r = ttm_bo_validate(&bo->tbo, &bo->placement,
 						true, false);
 			if (unlikely(r))
 				return r;
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index a835b6f..1fbb2ee 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -185,6 +185,7 @@
 	}
 	return 0;
 }
+EXPORT_SYMBOL(ttm_bo_wait_unreserved);
 
 static void ttm_bo_add_to_lru(struct ttm_buffer_object *bo)
 {
@@ -946,6 +947,7 @@
 	return wait_event_interruptible(bo->event_queue,
 					atomic_read(&bo->cpu_writers) == 0);
 }
+EXPORT_SYMBOL(ttm_bo_wait_cpu);
 
 int ttm_bo_move_buffer(struct ttm_buffer_object *bo,
 			struct ttm_placement *placement,
@@ -1002,9 +1004,9 @@
 	return -1;
 }
 
-int ttm_buffer_object_validate(struct ttm_buffer_object *bo,
-				struct ttm_placement *placement,
-				bool interruptible, bool no_wait)
+int ttm_bo_validate(struct ttm_buffer_object *bo,
+			struct ttm_placement *placement,
+			bool interruptible, bool no_wait)
 {
 	int ret;
 
@@ -1040,55 +1042,57 @@
 	}
 	return 0;
 }
-EXPORT_SYMBOL(ttm_buffer_object_validate);
+EXPORT_SYMBOL(ttm_bo_validate);
 
-int
-ttm_bo_check_placement(struct ttm_buffer_object *bo,
-		       uint32_t set_flags, uint32_t clr_flags)
+int ttm_bo_check_placement(struct ttm_buffer_object *bo,
+				struct ttm_placement *placement)
 {
-	uint32_t new_mask = set_flags | clr_flags;
+	int i;
 
-	if ((bo->type == ttm_bo_type_user) &&
-	    (clr_flags & TTM_PL_FLAG_CACHED)) {
-		printk(KERN_ERR TTM_PFX
-		       "User buffers require cache-coherent memory.\n");
-		return -EINVAL;
-	}
-
-	if (!capable(CAP_SYS_ADMIN)) {
-		if (new_mask & TTM_PL_FLAG_NO_EVICT) {
-			printk(KERN_ERR TTM_PFX "Need to be root to modify"
-			       " NO_EVICT status.\n");
+	if (placement->fpfn || placement->lpfn) {
+		if (bo->mem.num_pages > (placement->lpfn - placement->fpfn)) {
+			printk(KERN_ERR TTM_PFX "Page number range to small "
+				"Need %lu pages, range is [%u, %u]\n",
+				bo->mem.num_pages, placement->fpfn,
+				placement->lpfn);
 			return -EINVAL;
 		}
-
-		if ((clr_flags & bo->mem.placement & TTM_PL_MASK_MEMTYPE) &&
-		    (bo->mem.placement & TTM_PL_FLAG_NO_EVICT)) {
-			printk(KERN_ERR TTM_PFX
-			       "Incompatible memory specification"
-			       " for NO_EVICT buffer.\n");
-			return -EINVAL;
+	}
+	for (i = 0; i < placement->num_placement; i++) {
+		if (!capable(CAP_SYS_ADMIN)) {
+			if (placement->placement[i] & TTM_PL_FLAG_NO_EVICT) {
+				printk(KERN_ERR TTM_PFX "Need to be root to "
+					"modify NO_EVICT status.\n");
+				return -EINVAL;
+			}
+		}
+	}
+	for (i = 0; i < placement->num_busy_placement; i++) {
+		if (!capable(CAP_SYS_ADMIN)) {
+			if (placement->busy_placement[i] & TTM_PL_FLAG_NO_EVICT) {
+				printk(KERN_ERR TTM_PFX "Need to be root to "
+					"modify NO_EVICT status.\n");
+				return -EINVAL;
+			}
 		}
 	}
 	return 0;
 }
 
-int ttm_buffer_object_init(struct ttm_bo_device *bdev,
-			   struct ttm_buffer_object *bo,
-			   unsigned long size,
-			   enum ttm_bo_type type,
-			   uint32_t flags,
-			   uint32_t page_alignment,
-			   unsigned long buffer_start,
-			   bool interruptible,
-			   struct file *persistant_swap_storage,
-			   size_t acc_size,
-			   void (*destroy) (struct ttm_buffer_object *))
+int ttm_bo_init(struct ttm_bo_device *bdev,
+		struct ttm_buffer_object *bo,
+		unsigned long size,
+		enum ttm_bo_type type,
+		struct ttm_placement *placement,
+		uint32_t page_alignment,
+		unsigned long buffer_start,
+		bool interruptible,
+		struct file *persistant_swap_storage,
+		size_t acc_size,
+		void (*destroy) (struct ttm_buffer_object *))
 {
-	int i, c, ret = 0;
+	int ret = 0;
 	unsigned long num_pages;
-	uint32_t placements[8];
-	struct ttm_placement placement;
 
 	size += buffer_start & ~PAGE_MASK;
 	num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
@@ -1123,38 +1127,21 @@
 	bo->acc_size = acc_size;
 	atomic_inc(&bo->glob->bo_count);
 
-	ret = ttm_bo_check_placement(bo, flags, 0ULL);
+	ret = ttm_bo_check_placement(bo, placement);
 	if (unlikely(ret != 0))
 		goto out_err;
 
 	/*
-	 * If no caching attributes are set, accept any form of caching.
-	 */
-
-	if ((flags & TTM_PL_MASK_CACHING) == 0)
-		flags |= TTM_PL_MASK_CACHING;
-
-	/*
 	 * For ttm_bo_type_device buffers, allocate
 	 * address space from the device.
 	 */
-
 	if (bo->type == ttm_bo_type_device) {
 		ret = ttm_bo_setup_vm(bo);
 		if (ret)
 			goto out_err;
 	}
 
-	placement.fpfn = 0;
-	placement.lpfn = 0;
-	for (i = 0, c = 0; i <= TTM_PL_PRIV5; i++)
-		if (flags & (1 << i))
-			placements[c++] = (flags & ~TTM_PL_MASK_MEM) | (1 << i);
-	placement.placement = placements;
-	placement.num_placement = c;
-	placement.busy_placement = placements;
-	placement.num_busy_placement = c;
-	ret = ttm_buffer_object_validate(bo, &placement, interruptible, false);
+	ret = ttm_bo_validate(bo, placement, interruptible, false);
 	if (ret)
 		goto out_err;
 
@@ -1167,7 +1154,7 @@
 
 	return ret;
 }
-EXPORT_SYMBOL(ttm_buffer_object_init);
+EXPORT_SYMBOL(ttm_bo_init);
 
 static inline size_t ttm_bo_size(struct ttm_bo_global *glob,
 				 unsigned long num_pages)
@@ -1178,15 +1165,15 @@
 	return glob->ttm_bo_size + 2 * page_array_size;
 }
 
-int ttm_buffer_object_create(struct ttm_bo_device *bdev,
-			     unsigned long size,
-			     enum ttm_bo_type type,
-			     uint32_t flags,
-			     uint32_t page_alignment,
-			     unsigned long buffer_start,
-			     bool interruptible,
-			     struct file *persistant_swap_storage,
-			     struct ttm_buffer_object **p_bo)
+int ttm_bo_create(struct ttm_bo_device *bdev,
+			unsigned long size,
+			enum ttm_bo_type type,
+			struct ttm_placement *placement,
+			uint32_t page_alignment,
+			unsigned long buffer_start,
+			bool interruptible,
+			struct file *persistant_swap_storage,
+			struct ttm_buffer_object **p_bo)
 {
 	struct ttm_buffer_object *bo;
 	struct ttm_mem_global *mem_glob = bdev->glob->mem_glob;
@@ -1205,10 +1192,9 @@
 		return -ENOMEM;
 	}
 
-	ret = ttm_buffer_object_init(bdev, bo, size, type, flags,
-				     page_alignment, buffer_start,
-				     interruptible,
-				     persistant_swap_storage, acc_size, NULL);
+	ret = ttm_bo_init(bdev, bo, size, type, placement, page_alignment,
+				buffer_start, interruptible,
+				persistant_swap_storage, acc_size, NULL);
 	if (likely(ret == 0))
 		*p_bo = bo;
 
@@ -1743,12 +1729,14 @@
 	ttm_bo_unreserve(bo);
 	return ret;
 }
+EXPORT_SYMBOL(ttm_bo_synccpu_write_grab);
 
 void ttm_bo_synccpu_write_release(struct ttm_buffer_object *bo)
 {
 	if (atomic_dec_and_test(&bo->cpu_writers))
 		wake_up_all(&bo->event_queue);
 }
+EXPORT_SYMBOL(ttm_bo_synccpu_write_release);
 
 /**
  * A buffer object shrink method that tries to swap out the first
diff --git a/drivers/input/xen-kbdfront.c b/drivers/input/xen-kbdfront.c
index b115726..c721c0a 100644
--- a/drivers/input/xen-kbdfront.c
+++ b/drivers/input/xen-kbdfront.c
@@ -21,7 +21,10 @@
 #include <linux/errno.h>
 #include <linux/module.h>
 #include <linux/input.h>
+
 #include <asm/xen/hypervisor.h>
+
+#include <xen/xen.h>
 #include <xen/events.h>
 #include <xen/page.h>
 #include <xen/interface/io/fbif.h>
diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c
index baa051d..a869b45 100644
--- a/drivers/net/xen-netfront.c
+++ b/drivers/net/xen-netfront.c
@@ -42,6 +42,7 @@
 #include <linux/mm.h>
 #include <net/ip.h>
 
+#include <xen/xen.h>
 #include <xen/xenbus.h>
 #include <xen/events.h>
 #include <xen/page.h>
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index fdc864f..b1ecefa 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -27,10 +27,10 @@
 	default y
 	help
 	  Say Y here if you want to include support for the deprecated
-	  pci_find_slot() and pci_find_device() APIs.  Most drivers have
-	  been converted over to using the proper hotplug APIs, so this
-	  option serves to include/exclude only a few drivers that are
-	  still using this API.
+	  pci_find_device() API.  Most drivers have been converted over
+	  to using the proper hotplug APIs, so this option serves to
+	  include/exclude only a few drivers that are still using this
+	  API.
 
 config PCI_DEBUG
 	bool "PCI Debugging"
@@ -69,3 +69,10 @@
 	  physical resources.
 
 	  If unsure, say N.
+
+config PCI_IOAPIC
+	bool
+	depends on PCI
+	depends on ACPI
+	depends on HOTPLUG
+	default y
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 4a7f11d..4df48d5 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -14,6 +14,8 @@
 # Build PCI Express stuff if needed
 obj-$(CONFIG_PCIEPORTBUS) += pcie/
 
+obj-$(CONFIG_PCI_IOAPIC) += ioapic.o
+
 obj-$(CONFIG_HOTPLUG) += hotplug.o
 
 # Build the PCI Hotplug drivers if we were asked to
diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c
index 416f6ac..6cdc931 100644
--- a/drivers/pci/dmar.c
+++ b/drivers/pci/dmar.c
@@ -320,7 +320,7 @@
 	for (bus = dev->bus; bus; bus = bus->parent) {
 		struct pci_dev *bridge = bus->self;
 
-		if (!bridge || !bridge->is_pcie ||
+		if (!bridge || !pci_is_pcie(bridge) ||
 		    bridge->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE)
 			return 0;
 
@@ -645,8 +645,11 @@
 			       "x2apic and Intr-remapping.\n");
 #endif
 #ifdef CONFIG_DMAR
-		if (ret && !no_iommu && !iommu_detected && !dmar_disabled)
+		if (ret && !no_iommu && !iommu_detected && !dmar_disabled) {
 			iommu_detected = 1;
+			/* Make sure ACS will be enabled */
+			pci_request_acs();
+		}
 #endif
 #ifdef CONFIG_X86
 		if (ret)
diff --git a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile
index 3625b09..6cd9f3c 100644
--- a/drivers/pci/hotplug/Makefile
+++ b/drivers/pci/hotplug/Makefile
@@ -6,18 +6,22 @@
 obj-$(CONFIG_HOTPLUG_PCI_COMPAQ)	+= cpqphp.o
 obj-$(CONFIG_HOTPLUG_PCI_IBM)		+= ibmphp.o
 
-# pciehp should be linked before acpiphp in order to allow the native driver
-# to attempt to bind first. We can then fall back to generic support.
+# native drivers should be linked before acpiphp in order to allow the
+# native driver to attempt to bind first. We can then fall back to
+# generic support.
 
 obj-$(CONFIG_HOTPLUG_PCI_PCIE)		+= pciehp.o
-obj-$(CONFIG_HOTPLUG_PCI_ACPI)		+= acpiphp.o
-obj-$(CONFIG_HOTPLUG_PCI_ACPI_IBM)	+= acpiphp_ibm.o
 obj-$(CONFIG_HOTPLUG_PCI_CPCI_ZT5550)	+= cpcihp_zt5550.o
 obj-$(CONFIG_HOTPLUG_PCI_CPCI_GENERIC)	+= cpcihp_generic.o
 obj-$(CONFIG_HOTPLUG_PCI_SHPC)		+= shpchp.o
 obj-$(CONFIG_HOTPLUG_PCI_RPA)		+= rpaphp.o
 obj-$(CONFIG_HOTPLUG_PCI_RPA_DLPAR)	+= rpadlpar_io.o
 obj-$(CONFIG_HOTPLUG_PCI_SGI)		+= sgi_hotplug.o
+obj-$(CONFIG_HOTPLUG_PCI_ACPI)		+= acpiphp.o
+
+# acpiphp_ibm extends acpiphp, so should be linked afterwards.
+
+obj-$(CONFIG_HOTPLUG_PCI_ACPI_IBM)	+= acpiphp_ibm.o
 
 # Link this last so it doesn't claim devices that have a real hotplug driver
 obj-$(CONFIG_HOTPLUG_PCI_FAKE)		+= fakephp.o
diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c
index 0f32571..3c76fc6 100644
--- a/drivers/pci/hotplug/acpi_pcihp.c
+++ b/drivers/pci/hotplug/acpi_pcihp.c
@@ -362,6 +362,8 @@
 		status = acpi_pci_osc_control_set(handle, flags);
 		if (ACPI_SUCCESS(status))
 			goto got_one;
+		if (status == AE_SUPPORT)
+			goto no_control;
 		kfree(string.pointer);
 		string = (struct acpi_buffer){ ACPI_ALLOCATE_BUFFER, NULL };
 	}
@@ -394,10 +396,9 @@
 		if (ACPI_FAILURE(status))
 			break;
 	}
-
+no_control:
 	dbg("Cannot get control of hotplug hardware for pci %s\n",
 	    pci_name(pdev));
-
 	kfree(string.pointer);
 	return -ENODEV;
 got_one:
diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h
index 7d938df..bab5204 100644
--- a/drivers/pci/hotplug/acpiphp.h
+++ b/drivers/pci/hotplug/acpiphp.h
@@ -146,12 +146,6 @@
 	struct module *owner;
 };
 
-struct acpiphp_ioapic {
-	struct pci_dev *dev;
-	u32 gsi_base;
-	struct list_head list;
-};
-
 /* PCI bus bridge HID */
 #define ACPI_PCI_HOST_HID		"PNP0A03"
 
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index df1b0ea..8e952fd 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -52,8 +52,6 @@
 #include "acpiphp.h"
 
 static LIST_HEAD(bridge_list);
-static LIST_HEAD(ioapic_list);
-static DEFINE_SPINLOCK(ioapic_list_lock);
 
 #define MY_NAME "acpiphp_glue"
 
@@ -311,17 +309,13 @@
 /* find acpiphp_func from acpiphp_bridge */
 static struct acpiphp_func *acpiphp_bridge_handle_to_function(acpi_handle handle)
 {
-	struct list_head *node, *l;
 	struct acpiphp_bridge *bridge;
 	struct acpiphp_slot *slot;
 	struct acpiphp_func *func;
 
-	list_for_each(node, &bridge_list) {
-		bridge = list_entry(node, struct acpiphp_bridge, list);
+	list_for_each_entry(bridge, &bridge_list, list) {
 		for (slot = bridge->slots; slot; slot = slot->next) {
-			list_for_each(l, &slot->funcs) {
-				func = list_entry(l, struct acpiphp_func,
-							sibling);
+			list_for_each_entry(func, &slot->funcs, sibling) {
 				if (func->handle == handle)
 					return func;
 			}
@@ -495,21 +489,19 @@
 
 static struct acpiphp_bridge *acpiphp_handle_to_bridge(acpi_handle handle)
 {
-	struct list_head *head;
-	list_for_each(head, &bridge_list) {
-		struct acpiphp_bridge *bridge = list_entry(head,
-						struct acpiphp_bridge, list);
+	struct acpiphp_bridge *bridge;
+
+	list_for_each_entry(bridge, &bridge_list, list)
 		if (bridge->handle == handle)
 			return bridge;
-	}
 
 	return NULL;
 }
 
 static void cleanup_bridge(struct acpiphp_bridge *bridge)
 {
-	struct list_head *list, *tmp;
-	struct acpiphp_slot *slot;
+	struct acpiphp_slot *slot, *next;
+	struct acpiphp_func *func, *tmp;
 	acpi_status status;
 	acpi_handle handle = bridge->handle;
 
@@ -530,10 +522,8 @@
 
 	slot = bridge->slots;
 	while (slot) {
-		struct acpiphp_slot *next = slot->next;
-		list_for_each_safe (list, tmp, &slot->funcs) {
-			struct acpiphp_func *func;
-			func = list_entry(list, struct acpiphp_func, sibling);
+		next = slot->next;
+		list_for_each_entry_safe(func, tmp, &slot->funcs, sibling) {
 			if (is_dock_device(func->handle)) {
 				unregister_hotplug_dock_device(func->handle);
 				unregister_dock_notifier(&func->nb);
@@ -545,7 +535,7 @@
 				if (ACPI_FAILURE(status))
 					err("failed to remove notify handler\n");
 			}
-			list_del(list);
+			list_del(&func->sibling);
 			kfree(func);
 		}
 		acpiphp_unregister_hotplug_slot(slot);
@@ -606,204 +596,17 @@
 					   handle_hotplug_event_bridge);
 }
 
-static struct pci_dev * get_apic_pci_info(acpi_handle handle)
-{
-	struct pci_dev *dev;
-
-	dev = acpi_get_pci_dev(handle);
-	if (!dev)
-		return NULL;
-
-	if ((dev->class != PCI_CLASS_SYSTEM_PIC_IOAPIC) &&
-	    (dev->class != PCI_CLASS_SYSTEM_PIC_IOXAPIC))
-	{
-		pci_dev_put(dev);
-		return NULL;
-	}
-
-	return dev;
-}
-
-static int get_gsi_base(acpi_handle handle, u32 *gsi_base)
-{
-	acpi_status status;
-	int result = -1;
-	unsigned long long gsb;
-	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
-	union acpi_object *obj;
-	void *table;
-
-	status = acpi_evaluate_integer(handle, "_GSB", NULL, &gsb);
-	if (ACPI_SUCCESS(status)) {
-		*gsi_base = (u32)gsb;
-		return 0;
-	}
-
-	status = acpi_evaluate_object(handle, "_MAT", NULL, &buffer);
-	if (ACPI_FAILURE(status) || !buffer.length || !buffer.pointer)
-		return -1;
-
-	obj = buffer.pointer;
-	if (obj->type != ACPI_TYPE_BUFFER)
-		goto out;
-
-	table = obj->buffer.pointer;
-	switch (((struct acpi_subtable_header *)table)->type) {
-	case ACPI_MADT_TYPE_IO_SAPIC:
-		*gsi_base = ((struct acpi_madt_io_sapic *)table)->global_irq_base;
-		result = 0;
-		break;
-	case ACPI_MADT_TYPE_IO_APIC:
-		*gsi_base = ((struct acpi_madt_io_apic *)table)->global_irq_base;
-		result = 0;
-		break;
-	default:
-		break;
-	}
- out:
-	kfree(buffer.pointer);
-	return result;
-}
-
-static acpi_status
-ioapic_add(acpi_handle handle, u32 lvl, void *context, void **rv)
-{
-	acpi_status status;
-	unsigned long long sta;
-	acpi_handle tmp;
-	struct pci_dev *pdev;
-	u32 gsi_base;
-	u64 phys_addr;
-	struct acpiphp_ioapic *ioapic;
-
-	/* Evaluate _STA if present */
-	status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
-	if (ACPI_SUCCESS(status) && sta != ACPI_STA_ALL)
-		return AE_CTRL_DEPTH;
-
-	/* Scan only PCI bus scope */
-	status = acpi_get_handle(handle, "_HID", &tmp);
-	if (ACPI_SUCCESS(status))
-		return AE_CTRL_DEPTH;
-
-	if (get_gsi_base(handle, &gsi_base))
-		return AE_OK;
-
-	ioapic = kmalloc(sizeof(*ioapic), GFP_KERNEL);
-	if (!ioapic)
-		return AE_NO_MEMORY;
-
-	pdev = get_apic_pci_info(handle);
-	if (!pdev)
-		goto exit_kfree;
-
-	if (pci_enable_device(pdev))
-		goto exit_pci_dev_put;
-
-	pci_set_master(pdev);
-
-	if (pci_request_region(pdev, 0, "I/O APIC(acpiphp)"))
-		goto exit_pci_disable_device;
-
-	phys_addr = pci_resource_start(pdev, 0);
-	if (acpi_register_ioapic(handle, phys_addr, gsi_base))
-		goto exit_pci_release_region;
-
-	ioapic->gsi_base = gsi_base;
-	ioapic->dev = pdev;
-	spin_lock(&ioapic_list_lock);
-	list_add_tail(&ioapic->list, &ioapic_list);
-	spin_unlock(&ioapic_list_lock);
-
-	return AE_OK;
-
- exit_pci_release_region:
-	pci_release_region(pdev, 0);
- exit_pci_disable_device:
-	pci_disable_device(pdev);
- exit_pci_dev_put:
-	pci_dev_put(pdev);
- exit_kfree:
-	kfree(ioapic);
-
-	return AE_OK;
-}
-
-static acpi_status
-ioapic_remove(acpi_handle handle, u32 lvl, void *context, void **rv)
-{
-	acpi_status status;
-	unsigned long long sta;
-	acpi_handle tmp;
-	u32 gsi_base;
-	struct acpiphp_ioapic *pos, *n, *ioapic = NULL;
-
-	/* Evaluate _STA if present */
-	status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
-	if (ACPI_SUCCESS(status) && sta != ACPI_STA_ALL)
-		return AE_CTRL_DEPTH;
-
-	/* Scan only PCI bus scope */
-	status = acpi_get_handle(handle, "_HID", &tmp);
-	if (ACPI_SUCCESS(status))
-		return AE_CTRL_DEPTH;
-
-	if (get_gsi_base(handle, &gsi_base))
-		return AE_OK;
-
-	acpi_unregister_ioapic(handle, gsi_base);
-
-	spin_lock(&ioapic_list_lock);
-	list_for_each_entry_safe(pos, n, &ioapic_list, list) {
-		if (pos->gsi_base != gsi_base)
-			continue;
-		ioapic = pos;
-		list_del(&ioapic->list);
-		break;
-	}
-	spin_unlock(&ioapic_list_lock);
-
-	if (!ioapic)
-		return AE_OK;
-
-	pci_release_region(ioapic->dev, 0);
-	pci_disable_device(ioapic->dev);
-	pci_dev_put(ioapic->dev);
-	kfree(ioapic);
-
-	return AE_OK;
-}
-
-static int acpiphp_configure_ioapics(acpi_handle handle)
-{
-	ioapic_add(handle, 0, NULL, NULL);
-	acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
-			    ACPI_UINT32_MAX, ioapic_add, NULL, NULL, NULL);
-	return 0;
-}
-
-static int acpiphp_unconfigure_ioapics(acpi_handle handle)
-{
-	ioapic_remove(handle, 0, NULL, NULL);
-	acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
-			    ACPI_UINT32_MAX, ioapic_remove, NULL, NULL, NULL);
-	return 0;
-}
-
 static int power_on_slot(struct acpiphp_slot *slot)
 {
 	acpi_status status;
 	struct acpiphp_func *func;
-	struct list_head *l;
 	int retval = 0;
 
 	/* if already enabled, just skip */
 	if (slot->flags & SLOT_POWEREDON)
 		goto err_exit;
 
-	list_for_each (l, &slot->funcs) {
-		func = list_entry(l, struct acpiphp_func, sibling);
-
+	list_for_each_entry(func, &slot->funcs, sibling) {
 		if (func->flags & FUNC_HAS_PS0) {
 			dbg("%s: executing _PS0\n", __func__);
 			status = acpi_evaluate_object(func->handle, "_PS0", NULL, NULL);
@@ -829,7 +632,6 @@
 {
 	acpi_status status;
 	struct acpiphp_func *func;
-	struct list_head *l;
 
 	int retval = 0;
 
@@ -837,9 +639,7 @@
 	if ((slot->flags & SLOT_POWEREDON) == 0)
 		goto err_exit;
 
-	list_for_each (l, &slot->funcs) {
-		func = list_entry(l, struct acpiphp_func, sibling);
-
+	list_for_each_entry(func, &slot->funcs, sibling) {
 		if (func->flags & FUNC_HAS_PS3) {
 			status = acpi_evaluate_object(func->handle, "_PS3", NULL, NULL);
 			if (ACPI_FAILURE(status)) {
@@ -966,7 +766,6 @@
 {
 	struct pci_dev *dev;
 	struct pci_bus *bus = slot->bridge->pci_bus;
-	struct list_head *l;
 	struct acpiphp_func *func;
 	int retval = 0;
 	int num, max, pass;
@@ -1006,21 +805,16 @@
 		}
 	}
 
-	list_for_each (l, &slot->funcs) {
-		func = list_entry(l, struct acpiphp_func, sibling);
+	list_for_each_entry(func, &slot->funcs, sibling)
 		acpiphp_bus_add(func);
-	}
 
 	pci_bus_assign_resources(bus);
 	acpiphp_sanitize_bus(bus);
 	acpiphp_set_hpp_values(bus);
-	list_for_each_entry(func, &slot->funcs, sibling)
-		acpiphp_configure_ioapics(func->handle);
 	pci_enable_bridges(bus);
 	pci_bus_add_devices(bus);
 
-	list_for_each (l, &slot->funcs) {
-		func = list_entry(l, struct acpiphp_func, sibling);
+	list_for_each_entry(func, &slot->funcs, sibling) {
 		dev = pci_get_slot(bus, PCI_DEVFN(slot->device,
 						  func->function));
 		if (!dev)
@@ -1091,7 +885,6 @@
 	}
 
 	list_for_each_entry(func, &slot->funcs, sibling) {
-		acpiphp_unconfigure_ioapics(func->handle);
 		acpiphp_bus_trim(func->handle);
 	}
 
@@ -1119,12 +912,9 @@
 	acpi_status status;
 	unsigned long long sta = 0;
 	u32 dvid;
-	struct list_head *l;
 	struct acpiphp_func *func;
 
-	list_for_each (l, &slot->funcs) {
-		func = list_entry(l, struct acpiphp_func, sibling);
-
+	list_for_each_entry(func, &slot->funcs, sibling) {
 		if (func->flags & FUNC_HAS_STA) {
 			status = acpi_evaluate_integer(func->handle, "_STA", NULL, &sta);
 			if (ACPI_SUCCESS(status) && sta)
@@ -1152,13 +942,10 @@
 {
 	acpi_status status;
 	struct acpiphp_func *func;
-	struct list_head *l;
 	struct acpi_object_list arg_list;
 	union acpi_object arg;
 
-	list_for_each (l, &slot->funcs) {
-		func = list_entry(l, struct acpiphp_func, sibling);
-
+	list_for_each_entry(func, &slot->funcs, sibling) {
 		/* We don't want to call _EJ0 on non-existing functions. */
 		if ((func->flags & FUNC_HAS_EJ0)) {
 			/* _EJ0 method take one argument */
@@ -1275,7 +1062,6 @@
 	acpiphp_sanitize_bus(bus);
 	acpiphp_set_hpp_values(bus);
 	pci_enable_bridges(bus);
-	acpiphp_configure_ioapics(handle);
 	return 0;
 }
 
@@ -1542,7 +1328,7 @@
 	struct acpiphp_bridge *bridge;
 	int num_slots = 0;
 
-	list_for_each_entry (bridge, &bridge_list, list) {
+	list_for_each_entry(bridge, &bridge_list, list) {
 		dbg("Bus %04x:%02x has %d slot%s\n",
 				pci_domain_nr(bridge->pci_bus),
 				bridge->pci_bus->number, bridge->nr_slots,
diff --git a/drivers/pci/hotplug/ibmphp_hpc.c b/drivers/pci/hotplug/ibmphp_hpc.c
index 83f337c..c7084f0 100644
--- a/drivers/pci/hotplug/ibmphp_hpc.c
+++ b/drivers/pci/hotplug/ibmphp_hpc.c
@@ -890,7 +890,7 @@
 			msleep(POLL_INTERVAL_SEC * 1000);
 
 			if (kthread_should_stop())
-				break;
+				goto out_sleep;
 			
 			down (&semOperations);
 			
@@ -904,6 +904,7 @@
 		/* give up the hardware semaphore */
 		up (&semOperations);
 		/* sleep for a short time just for good measure */
+out_sleep:
 		msleep(100);
 	}
 	up (&sem_exit);
diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c
index 0325d98..38183a5 100644
--- a/drivers/pci/hotplug/pci_hotplug_core.c
+++ b/drivers/pci/hotplug/pci_hotplug_core.c
@@ -68,26 +68,26 @@
 static char *pci_bus_speed_strings[] = {
 	"33 MHz PCI",		/* 0x00 */
 	"66 MHz PCI",		/* 0x01 */
-	"66 MHz PCIX", 		/* 0x02 */
-	"100 MHz PCIX",		/* 0x03 */
-	"133 MHz PCIX",		/* 0x04 */
+	"66 MHz PCI-X",		/* 0x02 */
+	"100 MHz PCI-X",	/* 0x03 */
+	"133 MHz PCI-X",	/* 0x04 */
 	NULL,			/* 0x05 */
 	NULL,			/* 0x06 */
 	NULL,			/* 0x07 */
 	NULL,			/* 0x08 */
-	"66 MHz PCIX 266",	/* 0x09 */
-	"100 MHz PCIX 266",	/* 0x0a */
-	"133 MHz PCIX 266",	/* 0x0b */
+	"66 MHz PCI-X 266",	/* 0x09 */
+	"100 MHz PCI-X 266",	/* 0x0a */
+	"133 MHz PCI-X 266",	/* 0x0b */
 	NULL,			/* 0x0c */
 	NULL,			/* 0x0d */
 	NULL,			/* 0x0e */
 	NULL,			/* 0x0f */
 	NULL,			/* 0x10 */
-	"66 MHz PCIX 533",	/* 0x11 */
-	"100 MHz PCIX 533",	/* 0x12 */
-	"133 MHz PCIX 533",	/* 0x13 */
-	"2.5 GT/s PCI-E",	/* 0x14 */
-	"5.0 GT/s PCI-E",	/* 0x15 */
+	"66 MHz PCI-X 533",	/* 0x11 */
+	"100 MHz PCI-X 533",	/* 0x12 */
+	"133 MHz PCI-X 533",	/* 0x13 */
+	"2.5 GT/s PCIe",	/* 0x14 */
+	"5.0 GT/s PCIe",	/* 0x15 */
 };
 
 #ifdef CONFIG_HOTPLUG_PCI_CPCI
diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h
index 3070f77..4ed76b4 100644
--- a/drivers/pci/hotplug/pciehp.h
+++ b/drivers/pci/hotplug/pciehp.h
@@ -91,7 +91,6 @@
 	struct slot *slot;
 	wait_queue_head_t queue;	/* sleep & wake process */
 	u32 slot_cap;
-	u8 cap_base;
 	struct timer_list poll_timer;
 	unsigned int cmd_busy:1;
 	unsigned int no_cmd_complete:1;
diff --git a/drivers/pci/hotplug/pciehp_acpi.c b/drivers/pci/hotplug/pciehp_acpi.c
index 37c8d3d..b09b083 100644
--- a/drivers/pci/hotplug/pciehp_acpi.c
+++ b/drivers/pci/hotplug/pciehp_acpi.c
@@ -87,7 +87,8 @@
 	/* Note: pciehp_detect_mode != PCIEHP_DETECT_ACPI here */
 	if (pciehp_get_hp_hw_control_from_firmware(pdev))
 		return -ENODEV;
-	if (!(pos = pci_find_capability(pdev, PCI_CAP_ID_EXP)))
+	pos = pci_pcie_cap(pdev);
+	if (!pos)
 		return -ENODEV;
 	pci_read_config_dword(pdev, pos + PCI_EXP_SLTCAP, &slot_cap);
 	slot = kzalloc(sizeof(*slot), GFP_KERNEL);
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c
index bc23471..5674b20 100644
--- a/drivers/pci/hotplug/pciehp_core.c
+++ b/drivers/pci/hotplug/pciehp_core.c
@@ -72,18 +72,6 @@
 static int get_max_bus_speed	(struct hotplug_slot *slot, enum pci_bus_speed *value);
 static int get_cur_bus_speed	(struct hotplug_slot *slot, enum pci_bus_speed *value);
 
-static struct hotplug_slot_ops pciehp_hotplug_slot_ops = {
-	.set_attention_status =	set_attention_status,
-	.enable_slot =		enable_slot,
-	.disable_slot =		disable_slot,
-	.get_power_status =	get_power_status,
-	.get_attention_status =	get_attention_status,
-	.get_latch_status =	get_latch_status,
-	.get_adapter_status =	get_adapter_status,
-  	.get_max_bus_speed =	get_max_bus_speed,
-  	.get_cur_bus_speed =	get_cur_bus_speed,
-};
-
 /**
  * release_slot - free up the memory used by a slot
  * @hotplug_slot: slot to free
@@ -95,6 +83,7 @@
 	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
 		 __func__, hotplug_slot_name(hotplug_slot));
 
+	kfree(hotplug_slot->ops);
 	kfree(hotplug_slot->info);
 	kfree(hotplug_slot);
 }
@@ -104,6 +93,7 @@
 	struct slot *slot = ctrl->slot;
 	struct hotplug_slot *hotplug = NULL;
 	struct hotplug_slot_info *info = NULL;
+	struct hotplug_slot_ops *ops = NULL;
 	char name[SLOT_NAME_SIZE];
 	int retval = -ENOMEM;
 
@@ -115,11 +105,28 @@
 	if (!info)
 		goto out;
 
+	/* Setup hotplug slot ops */
+	ops = kzalloc(sizeof(*ops), GFP_KERNEL);
+	if (!ops)
+		goto out;
+	ops->enable_slot = enable_slot;
+	ops->disable_slot = disable_slot;
+	ops->get_power_status = get_power_status;
+	ops->get_adapter_status = get_adapter_status;
+	ops->get_max_bus_speed = get_max_bus_speed;
+	ops->get_cur_bus_speed = get_cur_bus_speed;
+	if (MRL_SENS(ctrl))
+		ops->get_latch_status = get_latch_status;
+	if (ATTN_LED(ctrl)) {
+		ops->get_attention_status = get_attention_status;
+		ops->set_attention_status = set_attention_status;
+	}
+
 	/* register this slot with the hotplug pci core */
 	hotplug->info = info;
 	hotplug->private = slot;
 	hotplug->release = &release_slot;
-	hotplug->ops = &pciehp_hotplug_slot_ops;
+	hotplug->ops = ops;
 	slot->hotplug_slot = hotplug;
 	snprintf(name, SLOT_NAME_SIZE, "%u", PSN(ctrl));
 
@@ -128,17 +135,12 @@
 		 ctrl->pcie->port->subordinate->number, PSN(ctrl));
 	retval = pci_hp_register(hotplug,
 				 ctrl->pcie->port->subordinate, 0, name);
-	if (retval) {
+	if (retval)
 		ctrl_err(ctrl,
 			 "pci_hp_register failed with error %d\n", retval);
-		goto out;
-	}
-	get_power_status(hotplug, &info->power_status);
-	get_attention_status(hotplug, &info->attention_status);
-	get_latch_status(hotplug, &info->latch_status);
-	get_adapter_status(hotplug, &info->adapter_status);
 out:
 	if (retval) {
+		kfree(ops);
 		kfree(info);
 		kfree(hotplug);
 	}
@@ -160,12 +162,7 @@
 	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
 		  __func__, slot_name(slot));
 
-	hotplug_slot->info->attention_status = status;
-
-	if (ATTN_LED(slot->ctrl))
-		pciehp_set_attention_status(slot, status);
-
-	return 0;
+	return pciehp_set_attention_status(slot, status);
 }
 
 
@@ -193,92 +190,62 @@
 static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
 	struct slot *slot = hotplug_slot->private;
-	int retval;
 
 	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
 		  __func__, slot_name(slot));
 
-	retval = pciehp_get_power_status(slot, value);
-	if (retval < 0)
-		*value = hotplug_slot->info->power_status;
-
-	return 0;
+	return pciehp_get_power_status(slot, value);
 }
 
 static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
 	struct slot *slot = hotplug_slot->private;
-	int retval;
 
 	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
 		  __func__, slot_name(slot));
 
-	retval = pciehp_get_attention_status(slot, value);
-	if (retval < 0)
-		*value = hotplug_slot->info->attention_status;
-
-	return 0;
+	return pciehp_get_attention_status(slot, value);
 }
 
 static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
 	struct slot *slot = hotplug_slot->private;
-	int retval;
 
 	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
 		 __func__, slot_name(slot));
 
-	retval = pciehp_get_latch_status(slot, value);
-	if (retval < 0)
-		*value = hotplug_slot->info->latch_status;
-
-	return 0;
+	return pciehp_get_latch_status(slot, value);
 }
 
 static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
 {
 	struct slot *slot = hotplug_slot->private;
-	int retval;
 
 	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
 		 __func__, slot_name(slot));
 
-	retval = pciehp_get_adapter_status(slot, value);
-	if (retval < 0)
-		*value = hotplug_slot->info->adapter_status;
-
-	return 0;
+	return pciehp_get_adapter_status(slot, value);
 }
 
 static int get_max_bus_speed(struct hotplug_slot *hotplug_slot,
 				enum pci_bus_speed *value)
 {
 	struct slot *slot = hotplug_slot->private;
-	int retval;
 
 	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
 		 __func__, slot_name(slot));
 
-	retval = pciehp_get_max_link_speed(slot, value);
-	if (retval < 0)
-		*value = PCI_SPEED_UNKNOWN;
-
-	return 0;
+	return pciehp_get_max_link_speed(slot, value);
 }
 
 static int get_cur_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
 {
 	struct slot *slot = hotplug_slot->private;
-	int retval;
 
 	ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n",
 		 __func__, slot_name(slot));
 
-	retval = pciehp_get_cur_link_speed(slot, value);
-	if (retval < 0)
-		*value = PCI_SPEED_UNKNOWN;
-
-	return 0;
+	return pciehp_get_cur_link_speed(slot, value);
 }
 
 static int pciehp_probe(struct pcie_device *dev)
@@ -286,14 +253,13 @@
 	int rc;
 	struct controller *ctrl;
 	struct slot *slot;
-	u8 value;
-	struct pci_dev *pdev = dev->port;
+	u8 occupied, poweron;
 
 	if (pciehp_force)
 		dev_info(&dev->device,
 			 "Bypassing BIOS check for pciehp use on %s\n",
-			 pci_name(pdev));
-	else if (pciehp_get_hp_hw_control_from_firmware(pdev))
+			 pci_name(dev->port));
+	else if (pciehp_get_hp_hw_control_from_firmware(dev->port))
 		goto err_out_none;
 
 	ctrl = pcie_init(dev);
@@ -318,23 +284,18 @@
 	rc = pcie_init_notification(ctrl);
 	if (rc) {
 		ctrl_err(ctrl, "Notification initialization failed\n");
-		goto err_out_release_ctlr;
+		goto err_out_free_ctrl_slot;
 	}
 
 	/* Check if slot is occupied */
 	slot = ctrl->slot;
-	pciehp_get_adapter_status(slot, &value);
-	if (value) {
-		if (pciehp_force)
-			pciehp_enable_slot(slot);
-	} else {
-		/* Power off slot if not occupied */
-		if (POWER_CTRL(ctrl)) {
-			rc = pciehp_power_off_slot(slot);
-			if (rc)
-				goto err_out_free_ctrl_slot;
-		}
-	}
+	pciehp_get_adapter_status(slot, &occupied);
+	pciehp_get_power_status(slot, &poweron);
+	if (occupied && pciehp_force)
+		pciehp_enable_slot(slot);
+	/* If empty slot's power status is on, turn power off */
+	if (!occupied && poweron && POWER_CTRL(ctrl))
+		pciehp_power_off_slot(slot);
 
 	return 0;
 
diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c
index 84487d1..d6ac1b2 100644
--- a/drivers/pci/hotplug/pciehp_ctrl.c
+++ b/drivers/pci/hotplug/pciehp_ctrl.c
@@ -142,23 +142,9 @@
 
 	/* power fault */
 	ctrl_dbg(ctrl, "Power fault interrupt received\n");
-
-	if (!pciehp_query_power_fault(p_slot)) {
-		/*
-		 * power fault Cleared
-		 */
-		ctrl_info(ctrl, "Power fault cleared on Slot(%s)\n",
-			  slot_name(p_slot));
-		event_type = INT_POWER_FAULT_CLEAR;
-	} else {
-		/*
-		 *   power fault
-		 */
-		ctrl_info(ctrl, "Power fault on Slot(%s)\n", slot_name(p_slot));
-		event_type = INT_POWER_FAULT;
-		ctrl_info(ctrl, "Power fault bit %x set\n", 0);
-	}
-
+	ctrl_err(ctrl, "Power fault on slot %s\n", slot_name(p_slot));
+	event_type = INT_POWER_FAULT;
+	ctrl_info(ctrl, "Power fault bit %x set\n", 0);
 	queue_interrupt_event(p_slot, event_type);
 
 	return 1;
@@ -224,13 +210,12 @@
 	retval = pciehp_check_link_status(ctrl);
 	if (retval) {
 		ctrl_err(ctrl, "Failed to check link status\n");
-		set_slot_off(ctrl, p_slot);
-		return retval;
+		goto err_exit;
 	}
 
 	/* Check for a power fault */
-	if (pciehp_query_power_fault(p_slot)) {
-		ctrl_dbg(ctrl, "Power fault detected\n");
+	if (ctrl->power_fault_detected || pciehp_query_power_fault(p_slot)) {
+		ctrl_err(ctrl, "Power fault on slot %s\n", slot_name(p_slot));
 		retval = -EIO;
 		goto err_exit;
 	}
@@ -363,25 +348,6 @@
 	mutex_unlock(&p_slot->lock);
 }
 
-static int update_slot_info(struct slot *slot)
-{
-	struct hotplug_slot_info *info;
-	int result;
-
-	info = kmalloc(sizeof(*info), GFP_KERNEL);
-	if (!info)
-		return -ENOMEM;
-
-	pciehp_get_power_status(slot, &info->power_status);
-	pciehp_get_attention_status(slot, &info->attention_status);
-	pciehp_get_latch_status(slot, &info->latch_status);
-	pciehp_get_adapter_status(slot, &info->adapter_status);
-
-	result = pci_hp_change_slot_info(slot->hotplug_slot, info);
-	kfree (info);
-	return result;
-}
-
 /*
  * Note: This function must be called with slot->lock held
  */
@@ -442,7 +408,6 @@
 		 * to hot-add or hot-remove is undergoing
 		 */
 		ctrl_info(ctrl, "Button ignore on Slot(%s)\n", slot_name(p_slot));
-		update_slot_info(p_slot);
 		break;
 	default:
 		ctrl_warn(ctrl, "Not a valid state\n");
@@ -500,11 +465,9 @@
 		if (!HP_SUPR_RM(ctrl))
 			break;
 		ctrl_dbg(ctrl, "Surprise Removal\n");
-		update_slot_info(p_slot);
 		handle_surprise_event(p_slot);
 		break;
 	default:
-		update_slot_info(p_slot);
 		break;
 	}
 	mutex_unlock(&p_slot->lock);
@@ -547,9 +510,6 @@
 	if (rc) {
 		pciehp_get_latch_status(p_slot, &getstatus);
 	}
-
-	update_slot_info(p_slot);
-
 	return rc;
 }
 
@@ -590,10 +550,7 @@
 		}
 	}
 
-	ret = remove_board(p_slot);
-	update_slot_info(p_slot);
-
-	return ret;
+	return remove_board(p_slot);
 }
 
 int pciehp_sysfs_enable_slot(struct slot *p_slot)
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
index 9ef4605..10040d5 100644
--- a/drivers/pci/hotplug/pciehp_hpc.c
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -45,25 +45,25 @@
 static inline int pciehp_readw(struct controller *ctrl, int reg, u16 *value)
 {
 	struct pci_dev *dev = ctrl->pcie->port;
-	return pci_read_config_word(dev, ctrl->cap_base + reg, value);
+	return pci_read_config_word(dev, pci_pcie_cap(dev) + reg, value);
 }
 
 static inline int pciehp_readl(struct controller *ctrl, int reg, u32 *value)
 {
 	struct pci_dev *dev = ctrl->pcie->port;
-	return pci_read_config_dword(dev, ctrl->cap_base + reg, value);
+	return pci_read_config_dword(dev, pci_pcie_cap(dev) + reg, value);
 }
 
 static inline int pciehp_writew(struct controller *ctrl, int reg, u16 value)
 {
 	struct pci_dev *dev = ctrl->pcie->port;
-	return pci_write_config_word(dev, ctrl->cap_base + reg, value);
+	return pci_write_config_word(dev, pci_pcie_cap(dev) + reg, value);
 }
 
 static inline int pciehp_writel(struct controller *ctrl, int reg, u32 value)
 {
 	struct pci_dev *dev = ctrl->pcie->port;
-	return pci_write_config_dword(dev, ctrl->cap_base + reg, value);
+	return pci_write_config_dword(dev, pci_pcie_cap(dev) + reg, value);
 }
 
 /* Power Control Command */
@@ -318,8 +318,8 @@
 		return retval;
 	}
 
-	ctrl_dbg(ctrl, "%s: SLOTCTRL %x, value read %x\n",
-		 __func__, ctrl->cap_base + PCI_EXP_SLTCTL, slot_ctrl);
+	ctrl_dbg(ctrl, "%s: SLOTCTRL %x, value read %x\n", __func__,
+		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_ctrl);
 
 	atten_led_state = (slot_ctrl & PCI_EXP_SLTCTL_AIC) >> 6;
 
@@ -356,8 +356,8 @@
 		ctrl_err(ctrl, "%s: Cannot read SLOTCTRL register\n", __func__);
 		return retval;
 	}
-	ctrl_dbg(ctrl, "%s: SLOTCTRL %x value read %x\n",
-		 __func__, ctrl->cap_base + PCI_EXP_SLTCTL, slot_ctrl);
+	ctrl_dbg(ctrl, "%s: SLOTCTRL %x value read %x\n", __func__,
+		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_ctrl);
 
 	pwr_state = (slot_ctrl & PCI_EXP_SLTCTL_PCC) >> 10;
 
@@ -427,27 +427,24 @@
 	struct controller *ctrl = slot->ctrl;
 	u16 slot_cmd;
 	u16 cmd_mask;
-	int rc;
 
 	cmd_mask = PCI_EXP_SLTCTL_AIC;
 	switch (value) {
-		case 0 :	/* turn off */
-			slot_cmd = 0x00C0;
-			break;
-		case 1:		/* turn on */
-			slot_cmd = 0x0040;
-			break;
-		case 2:		/* turn blink */
-			slot_cmd = 0x0080;
-			break;
-		default:
-			return -1;
+	case 0 :	/* turn off */
+		slot_cmd = 0x00C0;
+		break;
+	case 1:		/* turn on */
+		slot_cmd = 0x0040;
+		break;
+	case 2:		/* turn blink */
+		slot_cmd = 0x0080;
+		break;
+	default:
+		return -EINVAL;
 	}
-	rc = pcie_write_cmd(ctrl, slot_cmd, cmd_mask);
-	ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n",
-		 __func__, ctrl->cap_base + PCI_EXP_SLTCTL, slot_cmd);
-
-	return rc;
+	ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
+		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd);
+	return pcie_write_cmd(ctrl, slot_cmd, cmd_mask);
 }
 
 void pciehp_green_led_on(struct slot *slot)
@@ -459,8 +456,8 @@
 	slot_cmd = 0x0100;
 	cmd_mask = PCI_EXP_SLTCTL_PIC;
 	pcie_write_cmd(ctrl, slot_cmd, cmd_mask);
-	ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n",
-		 __func__, ctrl->cap_base + PCI_EXP_SLTCTL, slot_cmd);
+	ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
+		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd);
 }
 
 void pciehp_green_led_off(struct slot *slot)
@@ -472,8 +469,8 @@
 	slot_cmd = 0x0300;
 	cmd_mask = PCI_EXP_SLTCTL_PIC;
 	pcie_write_cmd(ctrl, slot_cmd, cmd_mask);
-	ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n",
-		 __func__, ctrl->cap_base + PCI_EXP_SLTCTL, slot_cmd);
+	ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
+		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd);
 }
 
 void pciehp_green_led_blink(struct slot *slot)
@@ -485,8 +482,8 @@
 	slot_cmd = 0x0200;
 	cmd_mask = PCI_EXP_SLTCTL_PIC;
 	pcie_write_cmd(ctrl, slot_cmd, cmd_mask);
-	ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n",
-		 __func__, ctrl->cap_base + PCI_EXP_SLTCTL, slot_cmd);
+	ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
+		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd);
 }
 
 int pciehp_power_on_slot(struct slot * slot)
@@ -514,97 +511,38 @@
 			return retval;
 		}
 	}
+	ctrl->power_fault_detected = 0;
 
 	slot_cmd = POWER_ON;
 	cmd_mask = PCI_EXP_SLTCTL_PCC;
-	if (!pciehp_poll_mode) {
-		/* Enable power fault detection turned off at power off time */
-		slot_cmd |= PCI_EXP_SLTCTL_PFDE;
-		cmd_mask |= PCI_EXP_SLTCTL_PFDE;
-	}
-
 	retval = pcie_write_cmd(ctrl, slot_cmd, cmd_mask);
 	if (retval) {
 		ctrl_err(ctrl, "Write %x command failed!\n", slot_cmd);
 		return retval;
 	}
-	ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n",
-		 __func__, ctrl->cap_base + PCI_EXP_SLTCTL, slot_cmd);
+	ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
+		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd);
 
-	ctrl->power_fault_detected = 0;
 	return retval;
 }
 
-static inline int pcie_mask_bad_dllp(struct controller *ctrl)
-{
-	struct pci_dev *dev = ctrl->pcie->port;
-	int pos;
-	u32 reg;
-
-	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
-	if (!pos)
-		return 0;
-	pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, &reg);
-	if (reg & PCI_ERR_COR_BAD_DLLP)
-		return 0;
-	reg |= PCI_ERR_COR_BAD_DLLP;
-	pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, reg);
-	return 1;
-}
-
-static inline void pcie_unmask_bad_dllp(struct controller *ctrl)
-{
-	struct pci_dev *dev = ctrl->pcie->port;
-	u32 reg;
-	int pos;
-
-	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
-	if (!pos)
-		return;
-	pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, &reg);
-	if (!(reg & PCI_ERR_COR_BAD_DLLP))
-		return;
-	reg &= ~PCI_ERR_COR_BAD_DLLP;
-	pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, reg);
-}
-
 int pciehp_power_off_slot(struct slot * slot)
 {
 	struct controller *ctrl = slot->ctrl;
 	u16 slot_cmd;
 	u16 cmd_mask;
-	int retval = 0;
-	int changed;
-
-	/*
-	 * Set Bad DLLP Mask bit in Correctable Error Mask
-	 * Register. This is the workaround against Bad DLLP error
-	 * that sometimes happens during turning power off the slot
-	 * which conforms to PCI Express 1.0a spec.
-	 */
-	changed = pcie_mask_bad_dllp(ctrl);
+	int retval;
 
 	slot_cmd = POWER_OFF;
 	cmd_mask = PCI_EXP_SLTCTL_PCC;
-	if (!pciehp_poll_mode) {
-		/* Disable power fault detection */
-		slot_cmd &= ~PCI_EXP_SLTCTL_PFDE;
-		cmd_mask |= PCI_EXP_SLTCTL_PFDE;
-	}
-
 	retval = pcie_write_cmd(ctrl, slot_cmd, cmd_mask);
 	if (retval) {
 		ctrl_err(ctrl, "Write command failed!\n");
-		retval = -1;
-		goto out;
+		return retval;
 	}
-	ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n",
-		 __func__, ctrl->cap_base + PCI_EXP_SLTCTL, slot_cmd);
- out:
-	if (changed)
-		pcie_unmask_bad_dllp(ctrl);
-
-	return retval;
+	ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
+		 pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd);
+	return 0;
 }
 
 static irqreturn_t pcie_isr(int irq, void *dev_id)
@@ -840,11 +778,19 @@
 {
 	u16 cmd, mask;
 
+	/*
+	 * TBD: Power fault detected software notification support.
+	 *
+	 * Power fault detected software notification is not enabled
+	 * now, because it caused power fault detected interrupt storm
+	 * on some machines. On those machines, power fault detected
+	 * bit in the slot status register was set again immediately
+	 * when it is cleared in the interrupt service routine, and
+	 * next power fault detected interrupt was notified again.
+	 */
 	cmd = PCI_EXP_SLTCTL_PDCE;
 	if (ATTN_BUTTN(ctrl))
 		cmd |= PCI_EXP_SLTCTL_ABPE;
-	if (POWER_CTRL(ctrl))
-		cmd |= PCI_EXP_SLTCTL_PFDE;
 	if (MRL_SENS(ctrl))
 		cmd |= PCI_EXP_SLTCTL_MRLSCE;
 	if (!pciehp_poll_mode)
@@ -866,7 +812,8 @@
 	u16 mask;
 	mask = (PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE |
 		PCI_EXP_SLTCTL_MRLSCE | PCI_EXP_SLTCTL_PFDE |
-		PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE);
+		PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE |
+		PCI_EXP_SLTCTL_DLLSCE);
 	if (pcie_write_cmd(ctrl, 0, mask))
 		ctrl_warn(ctrl, "Cannot disable software notification\n");
 }
@@ -934,7 +881,8 @@
 		  pdev->subsystem_device);
 	ctrl_info(ctrl, "  Subsystem Vendor ID  : 0x%04x\n",
 		  pdev->subsystem_vendor);
-	ctrl_info(ctrl, "  PCIe Cap offset      : 0x%02x\n", ctrl->cap_base);
+	ctrl_info(ctrl, "  PCIe Cap offset      : 0x%02x\n",
+		  pci_pcie_cap(pdev));
 	for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
 		if (!pci_resource_len(pdev, i))
 			continue;
@@ -978,8 +926,7 @@
 		goto abort;
 	}
 	ctrl->pcie = dev;
-	ctrl->cap_base = pci_find_capability(pdev, PCI_CAP_ID_EXP);
-	if (!ctrl->cap_base) {
+	if (!pci_pcie_cap(pdev)) {
 		ctrl_err(ctrl, "Cannot find PCI Express capability\n");
 		goto abort_ctrl;
 	}
diff --git a/drivers/pci/hotplug/pcihp_slot.c b/drivers/pci/hotplug/pcihp_slot.c
index cc8ec3a..80b461c 100644
--- a/drivers/pci/hotplug/pcihp_slot.c
+++ b/drivers/pci/hotplug/pcihp_slot.c
@@ -43,7 +43,7 @@
 		 * Perhaps we *should* use default settings for PCIe, but
 		 * pciehp didn't, so we won't either.
 		 */
-		if (dev->is_pcie)
+		if (pci_is_pcie(dev))
 			return;
 		dev_info(&dev->dev, "using default PCI settings\n");
 		hpp = &pci_default_type0;
@@ -102,7 +102,7 @@
 		return;
 
 	/* Find PCI Express capability */
-	pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+	pos = pci_pcie_cap(dev);
 	if (!pos)
 		return;
 
diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c
index 9261327..8d61594 100644
--- a/drivers/pci/intel-iommu.c
+++ b/drivers/pci/intel-iommu.c
@@ -1611,7 +1611,7 @@
 			return ret;
 		parent = parent->bus->self;
 	}
-	if (tmp->is_pcie) /* this is a PCIE-to-PCI bridge */
+	if (pci_is_pcie(tmp)) /* this is a PCIE-to-PCI bridge */
 		return domain_context_mapping_one(domain,
 					pci_domain_nr(tmp->subordinate),
 					tmp->subordinate->number, 0,
@@ -1651,7 +1651,7 @@
 			return ret;
 		parent = parent->bus->self;
 	}
-	if (tmp->is_pcie)
+	if (pci_is_pcie(tmp))
 		return device_context_mapped(iommu, tmp->subordinate->number,
 					     0);
 	else
@@ -1821,7 +1821,7 @@
 
 	dev_tmp = pci_find_upstream_pcie_bridge(pdev);
 	if (dev_tmp) {
-		if (dev_tmp->is_pcie) {
+		if (pci_is_pcie(dev_tmp)) {
 			bus = dev_tmp->subordinate->number;
 			devfn = 0;
 		} else {
@@ -2182,7 +2182,7 @@
 	 * the 1:1 domain, just in _case_ one of their siblings turns out
 	 * not to be able to map all of memory.
 	 */
-	if (!pdev->is_pcie) {
+	if (!pci_is_pcie(pdev)) {
 		if (!pci_is_root_bus(pdev->bus))
 			return 0;
 		if (pdev->class >> 8 == PCI_CLASS_BRIDGE_PCI)
@@ -3319,7 +3319,7 @@
 					 parent->devfn);
 			parent = parent->bus->self;
 		}
-		if (tmp->is_pcie) /* this is a PCIE-to-PCI bridge */
+		if (pci_is_pcie(tmp)) /* this is a PCIE-to-PCI bridge */
 			iommu_detach_dev(iommu,
 				tmp->subordinate->number, 0);
 		else /* this is a legacy PCI bridge */
diff --git a/drivers/pci/intr_remapping.c b/drivers/pci/intr_remapping.c
index 3b36586..1487bf2 100644
--- a/drivers/pci/intr_remapping.c
+++ b/drivers/pci/intr_remapping.c
@@ -520,7 +520,7 @@
 		return -1;
 
 	/* PCIe device or Root Complex integrated PCI device */
-	if (dev->is_pcie || !dev->bus->parent) {
+	if (pci_is_pcie(dev) || !dev->bus->parent) {
 		set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16,
 			     (dev->bus->number << 8) | dev->devfn);
 		return 0;
@@ -528,7 +528,7 @@
 
 	bridge = pci_find_upstream_pcie_bridge(dev);
 	if (bridge) {
-		if (bridge->is_pcie) /* this is a PCIE-to-PCI/PCIX bridge */
+		if (pci_is_pcie(bridge))/* this is a PCIE-to-PCI/PCIX bridge */
 			set_irte_sid(irte, SVT_VERIFY_BUS, SQ_ALL_16,
 				(bridge->bus->number << 8) | dev->bus->number);
 		else /* this is a legacy PCI bridge */
diff --git a/drivers/pci/ioapic.c b/drivers/pci/ioapic.c
new file mode 100644
index 0000000..3e0d7b5
--- /dev/null
+++ b/drivers/pci/ioapic.c
@@ -0,0 +1,127 @@
+/*
+ * IOAPIC/IOxAPIC/IOSAPIC driver
+ *
+ * Copyright (C) 2009 Fujitsu Limited.
+ * (c) Copyright 2009 Hewlett-Packard Development Company, L.P.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*
+ * This driver manages PCI I/O APICs added by hotplug after boot.  We try to
+ * claim all I/O APIC PCI devices, but those present at boot were registered
+ * when we parsed the ACPI MADT, so we'll fail when we try to re-register
+ * them.
+ */
+
+#include <linux/pci.h>
+#include <linux/acpi.h>
+#include <acpi/acpi_bus.h>
+
+struct ioapic {
+	acpi_handle	handle;
+	u32		gsi_base;
+};
+
+static int ioapic_probe(struct pci_dev *dev, const struct pci_device_id *ent)
+{
+	acpi_handle handle;
+	acpi_status status;
+	unsigned long long gsb;
+	struct ioapic *ioapic;
+	u64 addr;
+	int ret;
+	char *type;
+
+	handle = DEVICE_ACPI_HANDLE(&dev->dev);
+	if (!handle)
+		return -EINVAL;
+
+	status = acpi_evaluate_integer(handle, "_GSB", NULL, &gsb);
+	if (ACPI_FAILURE(status))
+		return -EINVAL;
+
+	/*
+	 * The previous code in acpiphp evaluated _MAT if _GSB failed, but
+	 * ACPI spec 4.0 sec 6.2.2 requires _GSB for hot-pluggable I/O APICs.
+	 */
+
+	ioapic = kzalloc(sizeof(*ioapic), GFP_KERNEL);
+	if (!ioapic)
+		return -ENOMEM;
+
+	ioapic->handle = handle;
+	ioapic->gsi_base = (u32) gsb;
+
+	if (dev->class == PCI_CLASS_SYSTEM_PIC_IOAPIC)
+		type = "IOAPIC";
+	else
+		type = "IOxAPIC";
+
+	ret = pci_enable_device(dev);
+	if (ret < 0)
+		goto exit_free;
+
+	pci_set_master(dev);
+
+	if (pci_request_region(dev, 0, type))
+		goto exit_disable;
+
+	addr = pci_resource_start(dev, 0);
+	if (acpi_register_ioapic(ioapic->handle, addr, ioapic->gsi_base))
+		goto exit_release;
+
+	pci_set_drvdata(dev, ioapic);
+	dev_info(&dev->dev, "%s at %#llx, GSI %u\n", type, addr,
+		 ioapic->gsi_base);
+	return 0;
+
+exit_release:
+	pci_release_region(dev, 0);
+exit_disable:
+	pci_disable_device(dev);
+exit_free:
+	kfree(ioapic);
+	return -ENODEV;
+}
+
+static void ioapic_remove(struct pci_dev *dev)
+{
+	struct ioapic *ioapic = pci_get_drvdata(dev);
+
+	acpi_unregister_ioapic(ioapic->handle, ioapic->gsi_base);
+	pci_release_region(dev, 0);
+	pci_disable_device(dev);
+	kfree(ioapic);
+}
+
+
+static struct pci_device_id ioapic_devices[] = {
+	{ PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
+	  PCI_CLASS_SYSTEM_PIC_IOAPIC << 8, 0xffff00, },
+	{ PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
+	  PCI_CLASS_SYSTEM_PIC_IOXAPIC << 8, 0xffff00, },
+	{ }
+};
+
+static struct pci_driver ioapic_driver = {
+	.name		= "ioapic",
+	.id_table	= ioapic_devices,
+	.probe		= ioapic_probe,
+	.remove		= __devexit_p(ioapic_remove),
+};
+
+static int __init ioapic_init(void)
+{
+	return pci_register_driver(&ioapic_driver);
+}
+
+static void __exit ioapic_exit(void)
+{
+	pci_unregister_driver(&ioapic_driver);
+}
+
+module_init(ioapic_init);
+module_exit(ioapic_exit);
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index e03fe98..b2a448e 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -555,7 +555,7 @@
 {
 	int pos;
 
-	if (!dev->is_pcie)
+	if (!pci_is_pcie(dev))
 		return -ENODEV;
 
 	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV);
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index 33317df..cc617dd 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -116,7 +116,7 @@
 		int ret;
 
 		ret = acpi_pm_device_sleep_wake(&bridge->dev, enable);
-		if (!ret || bridge->is_pcie)
+		if (!ret || pci_is_pcie(bridge))
 			return;
 		bus = bus->parent;
 	}
@@ -131,7 +131,7 @@
 	if (acpi_pci_can_wakeup(dev))
 		return acpi_pm_device_sleep_wake(&dev->dev, enable);
 
-	if (!dev->is_pcie)
+	if (!pci_is_pcie(dev))
 		acpi_pci_propagate_wakeup_enable(dev->bus, enable);
 
 	return 0;
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index 0f6382f..c5df94e 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -74,7 +74,11 @@
 	const struct cpumask *mask;
 	int len;
 
+#ifdef CONFIG_NUMA
+	mask = cpumask_of_node(dev_to_node(dev));
+#else
 	mask = cpumask_of_pcibus(to_pci_dev(dev)->bus);
+#endif
 	len = cpumask_scnprintf(buf, PAGE_SIZE-2, mask);
 	buf[len++] = '\n';
 	buf[len] = '\0';
@@ -88,7 +92,11 @@
 	const struct cpumask *mask;
 	int len;
 
+#ifdef CONFIG_NUMA
+	mask = cpumask_of_node(dev_to_node(dev));
+#else
 	mask = cpumask_of_pcibus(to_pci_dev(dev)->bus);
+#endif
 	len = cpulist_scnprintf(buf, PAGE_SIZE-2, mask);
 	buf[len++] = '\n';
 	buf[len] = '\0';
@@ -176,6 +184,21 @@
 #endif
 
 static ssize_t
+dma_mask_bits_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+
+	return sprintf (buf, "%d\n", fls64(pdev->dma_mask));
+}
+
+static ssize_t
+consistent_dma_mask_bits_show(struct device *dev, struct device_attribute *attr,
+				 char *buf)
+{
+	return sprintf (buf, "%d\n", fls64(dev->coherent_dma_mask));
+}
+
+static ssize_t
 msi_bus_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
 	struct pci_dev *pdev = to_pci_dev(dev);
@@ -306,6 +329,8 @@
 #ifdef CONFIG_NUMA
 	__ATTR_RO(numa_node),
 #endif
+	__ATTR_RO(dma_mask_bits),
+	__ATTR_RO(consistent_dma_mask_bits),
 	__ATTR(enable, 0600, is_enabled_show, is_enabled_store),
 	__ATTR(broken_parity_status,(S_IRUGO|S_IWUSR),
 		broken_parity_status_show,broken_parity_status_store),
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 4e4c295..0bc27e0 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -47,6 +47,15 @@
 unsigned long pci_hotplug_io_size  = DEFAULT_HOTPLUG_IO_SIZE;
 unsigned long pci_hotplug_mem_size = DEFAULT_HOTPLUG_MEM_SIZE;
 
+/*
+ * The default CLS is used if arch didn't set CLS explicitly and not
+ * all pci devices agree on the same value.  Arch can override either
+ * the dfl or actual value as it sees fit.  Don't forget this is
+ * measured in 32-bit words, not bytes.
+ */
+u8 pci_dfl_cache_line_size __devinitdata = L1_CACHE_BYTES >> 2;
+u8 pci_cache_line_size;
+
 /**
  * pci_bus_max_busnr - returns maximum PCI bus number of given bus' children
  * @bus: pointer to PCI bus structure to search
@@ -373,8 +382,12 @@
 			continue;	/* Wrong type */
 		if (!((res->flags ^ r->flags) & IORESOURCE_PREFETCH))
 			return r;	/* Exact match */
-		if ((res->flags & IORESOURCE_PREFETCH) && !(r->flags & IORESOURCE_PREFETCH))
-			best = r;	/* Approximating prefetchable by non-prefetchable */
+		/* We can't insert a non-prefetch resource inside a prefetchable parent .. */
+		if (r->flags & IORESOURCE_PREFETCH)
+			continue;
+		/* .. but we can put a prefetchable resource inside a non-prefetchable one */
+		if (!best)
+			best = r;
 	}
 	return best;
 }
@@ -728,8 +741,8 @@
 	u16 *cap;
 	u16 flags;
 
-	pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
-	if (pos <= 0)
+	pos = pci_pcie_cap(dev);
+	if (!pos)
 		return 0;
 
 	save_state = pci_find_saved_cap(dev, PCI_CAP_ID_EXP);
@@ -837,7 +850,7 @@
 	int i;
 	/* XXX: 100% dword access ok here? */
 	for (i = 0; i < 16; i++)
-		pci_read_config_dword(dev, i * 4,&dev->saved_config_space[i]);
+		pci_read_config_dword(dev, i * 4, &dev->saved_config_space[i]);
 	dev->state_saved = true;
 	if ((i = pci_save_pcie_state(dev)) != 0)
 		return i;
@@ -1202,7 +1215,7 @@
 
 	pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr);
 
-	dev_printk(KERN_INFO, &dev->dev, "PME# %s\n",
+	dev_printk(KERN_DEBUG, &dev->dev, "PME# %s\n",
 			enable ? "enabled" : "disabled");
 }
 
@@ -1413,7 +1426,8 @@
 
 	pmc &= PCI_PM_CAP_PME_MASK;
 	if (pmc) {
-		dev_info(&dev->dev, "PME# supported from%s%s%s%s%s\n",
+		dev_printk(KERN_DEBUG, &dev->dev,
+			 "PME# supported from%s%s%s%s%s\n",
 			 (pmc & PCI_PM_CAP_PME_D0) ? " D0" : "",
 			 (pmc & PCI_PM_CAP_PME_D1) ? " D1" : "",
 			 (pmc & PCI_PM_CAP_PME_D2) ? " D2" : "",
@@ -1510,7 +1524,7 @@
 	u16 ctrl;
 	struct pci_dev *bridge;
 
-	if (!dev->is_pcie || dev->devfn)
+	if (!pci_is_pcie(dev) || dev->devfn)
 		return;
 
 	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ARI);
@@ -1518,10 +1532,10 @@
 		return;
 
 	bridge = dev->bus->self;
-	if (!bridge || !bridge->is_pcie)
+	if (!bridge || !pci_is_pcie(bridge))
 		return;
 
-	pos = pci_find_capability(bridge, PCI_CAP_ID_EXP);
+	pos = pci_pcie_cap(bridge);
 	if (!pos)
 		return;
 
@@ -1536,6 +1550,54 @@
 	bridge->ari_enabled = 1;
 }
 
+static int pci_acs_enable;
+
+/**
+ * pci_request_acs - ask for ACS to be enabled if supported
+ */
+void pci_request_acs(void)
+{
+	pci_acs_enable = 1;
+}
+
+/**
+ * pci_enable_acs - enable ACS if hardware support it
+ * @dev: the PCI device
+ */
+void pci_enable_acs(struct pci_dev *dev)
+{
+	int pos;
+	u16 cap;
+	u16 ctrl;
+
+	if (!pci_acs_enable)
+		return;
+
+	if (!pci_is_pcie(dev))
+		return;
+
+	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS);
+	if (!pos)
+		return;
+
+	pci_read_config_word(dev, pos + PCI_ACS_CAP, &cap);
+	pci_read_config_word(dev, pos + PCI_ACS_CTRL, &ctrl);
+
+	/* Source Validation */
+	ctrl |= (cap & PCI_ACS_SV);
+
+	/* P2P Request Redirect */
+	ctrl |= (cap & PCI_ACS_RR);
+
+	/* P2P Completion Redirect */
+	ctrl |= (cap & PCI_ACS_CR);
+
+	/* Upstream Forwarding */
+	ctrl |= (cap & PCI_ACS_UF);
+
+	pci_write_config_word(dev, pos + PCI_ACS_CTRL, ctrl);
+}
+
 /**
  * pci_swizzle_interrupt_pin - swizzle INTx for device behind bridge
  * @dev: the PCI device
@@ -1669,9 +1731,7 @@
 	return 0;
 
 err_out:
-	dev_warn(&pdev->dev, "BAR %d: can't reserve %s region %pR\n",
-		 bar,
-		 pci_resource_flags(pdev, bar) & IORESOURCE_IO ? "I/O" : "mem",
+	dev_warn(&pdev->dev, "BAR %d: can't reserve %pR\n", bar,
 		 &pdev->resource[bar]);
 	return -EBUSY;
 }
@@ -1866,31 +1926,6 @@
 	__pci_set_master(dev, false);
 }
 
-#ifdef PCI_DISABLE_MWI
-int pci_set_mwi(struct pci_dev *dev)
-{
-	return 0;
-}
-
-int pci_try_set_mwi(struct pci_dev *dev)
-{
-	return 0;
-}
-
-void pci_clear_mwi(struct pci_dev *dev)
-{
-}
-
-#else
-
-#ifndef PCI_CACHE_LINE_BYTES
-#define PCI_CACHE_LINE_BYTES L1_CACHE_BYTES
-#endif
-
-/* This can be overridden by arch code. */
-/* Don't forget this is measured in 32-bit words, not bytes */
-u8 pci_cache_line_size = PCI_CACHE_LINE_BYTES / 4;
-
 /**
  * pci_set_cacheline_size - ensure the CACHE_LINE_SIZE register is programmed
  * @dev: the PCI device for which MWI is to be enabled
@@ -1901,13 +1936,12 @@
  *
  * RETURNS: An appropriate -ERRNO error value on error, or zero for success.
  */
-static int
-pci_set_cacheline_size(struct pci_dev *dev)
+int pci_set_cacheline_size(struct pci_dev *dev)
 {
 	u8 cacheline_size;
 
 	if (!pci_cache_line_size)
-		return -EINVAL;		/* The system doesn't support MWI. */
+		return -EINVAL;
 
 	/* Validate current setting: the PCI_CACHE_LINE_SIZE must be
 	   equal to or multiple of the right value. */
@@ -1928,6 +1962,24 @@
 
 	return -EINVAL;
 }
+EXPORT_SYMBOL_GPL(pci_set_cacheline_size);
+
+#ifdef PCI_DISABLE_MWI
+int pci_set_mwi(struct pci_dev *dev)
+{
+	return 0;
+}
+
+int pci_try_set_mwi(struct pci_dev *dev)
+{
+	return 0;
+}
+
+void pci_clear_mwi(struct pci_dev *dev)
+{
+}
+
+#else
 
 /**
  * pci_set_mwi - enables memory-write-invalidate PCI transaction
@@ -2062,6 +2114,7 @@
 		return -EIO;
 
 	dev->dma_mask = mask;
+	dev_dbg(&dev->dev, "using %dbit DMA mask\n", fls64(mask));
 
 	return 0;
 }
@@ -2073,6 +2126,7 @@
 		return -EIO;
 
 	dev->dev.coherent_dma_mask = mask;
+	dev_dbg(&dev->dev, "using %dbit consistent DMA mask\n", fls64(mask));
 
 	return 0;
 }
@@ -2099,9 +2153,9 @@
 	int i;
 	int pos;
 	u32 cap;
-	u16 status;
+	u16 status, control;
 
-	pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+	pos = pci_pcie_cap(dev);
 	if (!pos)
 		return -ENOTTY;
 
@@ -2126,8 +2180,10 @@
 			"proceeding with reset anyway\n");
 
 clear:
-	pci_write_config_word(dev, pos + PCI_EXP_DEVCTL,
-				PCI_EXP_DEVCTL_BCR_FLR);
+	pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &control);
+	control |= PCI_EXP_DEVCTL_BCR_FLR;
+	pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, control);
+
 	msleep(100);
 
 	return 0;
@@ -2450,7 +2506,7 @@
 	int ret, cap;
 	u16 ctl;
 
-	cap = pci_find_capability(dev, PCI_CAP_ID_EXP);
+	cap = pci_pcie_cap(dev);
 	if (!cap)
 		return -EINVAL;
 
@@ -2480,7 +2536,7 @@
 
 	v = (ffs(rq) - 8) << 12;
 
-	cap = pci_find_capability(dev, PCI_CAP_ID_EXP);
+	cap = pci_pcie_cap(dev);
 	if (!cap)
 		goto out;
 
@@ -2540,7 +2596,7 @@
 			return reg;
 	}
 
-	dev_err(&dev->dev, "BAR: invalid resource #%d\n", resno);
+	dev_err(&dev->dev, "BAR %d: invalid resource\n", resno);
 	return 0;
 }
 
@@ -2590,7 +2646,7 @@
 
 #define RESOURCE_ALIGNMENT_PARAM_SIZE COMMAND_LINE_SIZE
 static char resource_alignment_param[RESOURCE_ALIGNMENT_PARAM_SIZE] = {0};
-spinlock_t resource_alignment_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(resource_alignment_lock);
 
 /**
  * pci_specified_resource_alignment - get resource alignment specified by user.
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index d92d195..33ed8e0 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -311,4 +311,6 @@
 	return resource_alignment(res);
 }
 
+extern void pci_enable_acs(struct pci_dev *dev);
+
 #endif /* DRIVERS_PCI_H */
diff --git a/drivers/pci/pcie/aer/aer_inject.c b/drivers/pci/pcie/aer/aer_inject.c
index 62d15f6..7fcd5331 100644
--- a/drivers/pci/pcie/aer/aer_inject.c
+++ b/drivers/pci/pcie/aer/aer_inject.c
@@ -23,6 +23,7 @@
 #include <linux/pci.h>
 #include <linux/fs.h>
 #include <linux/uaccess.h>
+#include <linux/stddef.h>
 #include "aerdrv.h"
 
 struct aer_error_inj {
@@ -35,10 +36,12 @@
 	u32 header_log1;
 	u32 header_log2;
 	u32 header_log3;
+	u16 domain;
 };
 
 struct aer_error {
 	struct list_head list;
+	u16 domain;
 	unsigned int bus;
 	unsigned int devfn;
 	int pos_cap_err;
@@ -66,22 +69,27 @@
 /* Protect einjected and pci_bus_ops_list */
 static DEFINE_SPINLOCK(inject_lock);
 
-static void aer_error_init(struct aer_error *err, unsigned int bus,
-			   unsigned int devfn, int pos_cap_err)
+static void aer_error_init(struct aer_error *err, u16 domain,
+			   unsigned int bus, unsigned int devfn,
+			   int pos_cap_err)
 {
 	INIT_LIST_HEAD(&err->list);
+	err->domain = domain;
 	err->bus = bus;
 	err->devfn = devfn;
 	err->pos_cap_err = pos_cap_err;
 }
 
 /* inject_lock must be held before calling */
-static struct aer_error *__find_aer_error(unsigned int bus, unsigned int devfn)
+static struct aer_error *__find_aer_error(u16 domain, unsigned int bus,
+					  unsigned int devfn)
 {
 	struct aer_error *err;
 
 	list_for_each_entry(err, &einjected, list) {
-		if (bus == err->bus && devfn == err->devfn)
+		if (domain == err->domain &&
+		    bus == err->bus &&
+		    devfn == err->devfn)
 			return err;
 	}
 	return NULL;
@@ -90,7 +98,10 @@
 /* inject_lock must be held before calling */
 static struct aer_error *__find_aer_error_by_dev(struct pci_dev *dev)
 {
-	return __find_aer_error(dev->bus->number, dev->devfn);
+	int domain = pci_domain_nr(dev->bus);
+	if (domain < 0)
+		return NULL;
+	return __find_aer_error((u16)domain, dev->bus->number, dev->devfn);
 }
 
 /* inject_lock must be held before calling */
@@ -172,11 +183,15 @@
 	struct aer_error *err;
 	unsigned long flags;
 	struct pci_ops *ops;
+	int domain;
 
 	spin_lock_irqsave(&inject_lock, flags);
 	if (size != sizeof(u32))
 		goto out;
-	err = __find_aer_error(bus->number, devfn);
+	domain = pci_domain_nr(bus);
+	if (domain < 0)
+		goto out;
+	err = __find_aer_error((u16)domain, bus->number, devfn);
 	if (!err)
 		goto out;
 
@@ -200,11 +215,15 @@
 	unsigned long flags;
 	int rw1cs;
 	struct pci_ops *ops;
+	int domain;
 
 	spin_lock_irqsave(&inject_lock, flags);
 	if (size != sizeof(u32))
 		goto out;
-	err = __find_aer_error(bus->number, devfn);
+	domain = pci_domain_nr(bus);
+	if (domain < 0)
+		goto out;
+	err = __find_aer_error((u16)domain, bus->number, devfn);
 	if (!err)
 		goto out;
 
@@ -262,7 +281,7 @@
 static struct pci_dev *pcie_find_root_port(struct pci_dev *dev)
 {
 	while (1) {
-		if (!dev->is_pcie)
+		if (!pci_is_pcie(dev))
 			break;
 		if (dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT)
 			return dev;
@@ -305,25 +324,25 @@
 	u32 sever;
 	int ret = 0;
 
-	dev = pci_get_bus_and_slot(einj->bus, devfn);
+	dev = pci_get_domain_bus_and_slot((int)einj->domain, einj->bus, devfn);
 	if (!dev)
-		return -EINVAL;
+		return -ENODEV;
 	rpdev = pcie_find_root_port(dev);
 	if (!rpdev) {
-		ret = -EINVAL;
+		ret = -ENOTTY;
 		goto out_put;
 	}
 
 	pos_cap_err = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
 	if (!pos_cap_err) {
-		ret = -EIO;
+		ret = -ENOTTY;
 		goto out_put;
 	}
 	pci_read_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_SEVER, &sever);
 
 	rp_pos_cap_err = pci_find_ext_capability(rpdev, PCI_EXT_CAP_ID_ERR);
 	if (!rp_pos_cap_err) {
-		ret = -EIO;
+		ret = -ENOTTY;
 		goto out_put;
 	}
 
@@ -344,7 +363,8 @@
 	if (!err) {
 		err = err_alloc;
 		err_alloc = NULL;
-		aer_error_init(err, einj->bus, devfn, pos_cap_err);
+		aer_error_init(err, einj->domain, einj->bus, devfn,
+			       pos_cap_err);
 		list_add(&err->list, &einjected);
 	}
 	err->uncor_status |= einj->uncor_status;
@@ -358,7 +378,8 @@
 	if (!rperr) {
 		rperr = rperr_alloc;
 		rperr_alloc = NULL;
-		aer_error_init(rperr, rpdev->bus->number, rpdev->devfn,
+		aer_error_init(rperr, pci_domain_nr(rpdev->bus),
+			       rpdev->bus->number, rpdev->devfn,
 			       rp_pos_cap_err);
 		list_add(&rperr->list, &einjected);
 	}
@@ -411,10 +432,11 @@
 
 	if (!capable(CAP_SYS_ADMIN))
 		return -EPERM;
-
-	if (usize != sizeof(struct aer_error_inj))
+	if (usize < offsetof(struct aer_error_inj, domain) ||
+	    usize > sizeof(einj))
 		return -EINVAL;
 
+	memset(&einj, 0, sizeof(einj));
 	if (copy_from_user(&einj, ubuf, usize))
 		return -EFAULT;
 
@@ -452,7 +474,7 @@
 	}
 
 	spin_lock_irqsave(&inject_lock, flags);
-	list_for_each_entry_safe(err, err_next, &pci_bus_ops_list, list) {
+	list_for_each_entry_safe(err, err_next, &einjected, list) {
 		list_del(&err->list);
 		kfree(err);
 	}
diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c
index 40c3cc5..97a3459 100644
--- a/drivers/pci/pcie/aer/aerdrv.c
+++ b/drivers/pci/pcie/aer/aerdrv.c
@@ -53,7 +53,7 @@
 
 static struct pcie_port_service_driver aerdriver = {
 	.name		= "aer",
-	.port_type	= PCIE_RC_PORT,
+	.port_type	= PCI_EXP_TYPE_ROOT_PORT,
 	.service	= PCIE_PORT_SERVICE_AER,
 
 	.probe		= aer_probe,
@@ -295,7 +295,7 @@
 	u16 reg16;
 
 	/* Clean up Root device status */
-	pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+	pos = pci_pcie_cap(dev);
 	pci_read_config_word(dev, pos + PCI_EXP_DEVSTA, &reg16);
 	pci_write_config_word(dev, pos + PCI_EXP_DEVSTA, reg16);
 
diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c
index 9f5ccbe..ae672ca 100644
--- a/drivers/pci/pcie/aer/aerdrv_core.c
+++ b/drivers/pci/pcie/aer/aerdrv_core.c
@@ -35,11 +35,14 @@
 	u16 reg16 = 0;
 	int pos;
 
+	if (dev->aer_firmware_first)
+		return -EIO;
+
 	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
 	if (!pos)
 		return -EIO;
 
-	pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+	pos = pci_pcie_cap(dev);
 	if (!pos)
 		return -EIO;
 
@@ -60,7 +63,10 @@
 	u16 reg16 = 0;
 	int pos;
 
-	pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+	if (dev->aer_firmware_first)
+		return -EIO;
+
+	pos = pci_pcie_cap(dev);
 	if (!pos)
 		return -EIO;
 
@@ -78,48 +84,27 @@
 int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev)
 {
 	int pos;
-	u32 status, mask;
-
-	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
-	if (!pos)
-		return -EIO;
-
-	pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status);
-	pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &mask);
-	if (dev->error_state == pci_channel_io_normal)
-		status &= ~mask; /* Clear corresponding nonfatal bits */
-	else
-		status &= mask; /* Clear corresponding fatal bits */
-	pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status);
-
-	return 0;
-}
-EXPORT_SYMBOL_GPL(pci_cleanup_aer_uncorrect_error_status);
-
-#if 0
-int pci_cleanup_aer_correct_error_status(struct pci_dev *dev)
-{
-	int pos;
 	u32 status;
 
 	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
 	if (!pos)
 		return -EIO;
 
-	pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &status);
-	pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS, status);
+	pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status);
+	if (status)
+		pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status);
 
 	return 0;
 }
-#endif  /*  0  */
+EXPORT_SYMBOL_GPL(pci_cleanup_aer_uncorrect_error_status);
 
 static int set_device_error_reporting(struct pci_dev *dev, void *data)
 {
 	bool enable = *((bool *)data);
 
-	if (dev->pcie_type == PCIE_RC_PORT ||
-	    dev->pcie_type == PCIE_SW_UPSTREAM_PORT ||
-	    dev->pcie_type == PCIE_SW_DOWNSTREAM_PORT) {
+	if ((dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT) ||
+	    (dev->pcie_type == PCI_EXP_TYPE_UPSTREAM) ||
+	    (dev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM)) {
 		if (enable)
 			pci_enable_pcie_error_reporting(dev);
 		else
@@ -218,7 +203,7 @@
 	 */
 	if (atomic_read(&dev->enable_cnt) == 0)
 		return 0;
-	pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+	pos = pci_pcie_cap(dev);
 	if (!pos)
 		return 0;
 	/* Check if AER is enabled */
@@ -431,10 +416,9 @@
 	result = (struct find_aer_service_data *) data;
 
 	if (device->bus == &pcie_port_bus_type) {
-		struct pcie_port_data *port_data;
+		struct pcie_device *pcie = to_pcie_device(device);
 
-		port_data = pci_get_drvdata(to_pcie_device(device)->port);
-		if (port_data->port_type == PCIE_SW_DOWNSTREAM_PORT)
+		if (pcie->port->pcie_type == PCI_EXP_TYPE_DOWNSTREAM)
 			result->is_downstream = 1;
 
 		driver = device->driver;
@@ -612,7 +596,7 @@
 	u16 reg16;
 	u32 reg32;
 
-	pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+	pos = pci_pcie_cap(pdev);
 	/* Clear PCIE Capability's Device Status */
 	pci_read_config_word(pdev, pos+PCI_EXP_DEVSTA, &reg16);
 	pci_write_config_word(pdev, pos+PCI_EXP_DEVSTA, reg16);
@@ -874,8 +858,22 @@
  */
 int aer_init(struct pcie_device *dev)
 {
-	if (aer_osc_setup(dev) && !forceload)
-		return -ENXIO;
+	if (dev->port->aer_firmware_first) {
+		dev_printk(KERN_DEBUG, &dev->device,
+			   "PCIe errors handled by platform firmware.\n");
+		goto out;
+	}
+
+	if (aer_osc_setup(dev))
+		goto out;
 
 	return 0;
+out:
+	if (forceload) {
+		dev_printk(KERN_DEBUG, &dev->device,
+			   "aerdrv forceload requested.\n");
+		dev->port->aer_firmware_first = 0;
+		return 0;
+	}
+	return -ENXIO;
 }
diff --git a/drivers/pci/pcie/aer/ecrc.c b/drivers/pci/pcie/aer/ecrc.c
index a928d8a..a2747a6 100644
--- a/drivers/pci/pcie/aer/ecrc.c
+++ b/drivers/pci/pcie/aer/ecrc.c
@@ -51,7 +51,7 @@
 	int pos;
 	u32 reg32;
 
-	if (!dev->is_pcie)
+	if (!pci_is_pcie(dev))
 		return -ENODEV;
 
 	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
@@ -79,7 +79,7 @@
 	int pos;
 	u32 reg32;
 
-	if (!dev->is_pcie)
+	if (!pci_is_pcie(dev))
 		return -ENODEV;
 
 	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index 5b7056c..5a01fc7 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -122,7 +122,7 @@
 	struct pci_bus *linkbus = link->pdev->subordinate;
 
 	list_for_each_entry(child, &linkbus->devices, bus_list) {
-		pos = pci_find_capability(child, PCI_CAP_ID_EXP);
+		pos = pci_pcie_cap(child);
 		if (!pos)
 			return;
 		pci_read_config_word(child, pos + PCI_EXP_LNKCTL, &reg16);
@@ -156,7 +156,7 @@
 
 	/* All functions should have the same cap and state, take the worst */
 	list_for_each_entry(child, &linkbus->devices, bus_list) {
-		pos = pci_find_capability(child, PCI_CAP_ID_EXP);
+		pos = pci_pcie_cap(child);
 		if (!pos)
 			return;
 		pci_read_config_dword(child, pos + PCI_EXP_LNKCAP, &reg32);
@@ -191,23 +191,23 @@
 	 * Configuration, so just check one function
 	 */
 	child = list_entry(linkbus->devices.next, struct pci_dev, bus_list);
-	BUG_ON(!child->is_pcie);
+	BUG_ON(!pci_is_pcie(child));
 
 	/* Check downstream component if bit Slot Clock Configuration is 1 */
-	cpos = pci_find_capability(child, PCI_CAP_ID_EXP);
+	cpos = pci_pcie_cap(child);
 	pci_read_config_word(child, cpos + PCI_EXP_LNKSTA, &reg16);
 	if (!(reg16 & PCI_EXP_LNKSTA_SLC))
 		same_clock = 0;
 
 	/* Check upstream component if bit Slot Clock Configuration is 1 */
-	ppos = pci_find_capability(parent, PCI_CAP_ID_EXP);
+	ppos = pci_pcie_cap(parent);
 	pci_read_config_word(parent, ppos + PCI_EXP_LNKSTA, &reg16);
 	if (!(reg16 & PCI_EXP_LNKSTA_SLC))
 		same_clock = 0;
 
 	/* Configure downstream component, all functions */
 	list_for_each_entry(child, &linkbus->devices, bus_list) {
-		cpos = pci_find_capability(child, PCI_CAP_ID_EXP);
+		cpos = pci_pcie_cap(child);
 		pci_read_config_word(child, cpos + PCI_EXP_LNKCTL, &reg16);
 		child_reg[PCI_FUNC(child->devfn)] = reg16;
 		if (same_clock)
@@ -247,7 +247,7 @@
 	dev_printk(KERN_ERR, &parent->dev,
 		   "ASPM: Could not configure common clock\n");
 	list_for_each_entry(child, &linkbus->devices, bus_list) {
-		cpos = pci_find_capability(child, PCI_CAP_ID_EXP);
+		cpos = pci_pcie_cap(child);
 		pci_write_config_word(child, cpos + PCI_EXP_LNKCTL,
 				      child_reg[PCI_FUNC(child->devfn)]);
 	}
@@ -300,7 +300,7 @@
 	u16 reg16;
 	u32 reg32;
 
-	pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+	pos = pci_pcie_cap(pdev);
 	pci_read_config_dword(pdev, pos + PCI_EXP_LNKCAP, &reg32);
 	info->support = (reg32 & PCI_EXP_LNKCAP_ASPMS) >> 10;
 	info->latency_encoding_l0s = (reg32 & PCI_EXP_LNKCAP_L0SEL) >> 12;
@@ -420,7 +420,7 @@
 		    child->pcie_type != PCI_EXP_TYPE_LEG_END)
 			continue;
 
-		pos = pci_find_capability(child, PCI_CAP_ID_EXP);
+		pos = pci_pcie_cap(child);
 		pci_read_config_dword(child, pos + PCI_EXP_DEVCAP, &reg32);
 		/* Calculate endpoint L0s acceptable latency */
 		encoding = (reg32 & PCI_EXP_DEVCAP_L0S) >> 6;
@@ -436,7 +436,7 @@
 static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 val)
 {
 	u16 reg16;
-	int pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+	int pos = pci_pcie_cap(pdev);
 
 	pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, &reg16);
 	reg16 &= ~0x3;
@@ -503,7 +503,7 @@
 	 * very strange. Disable ASPM for the whole slot
 	 */
 	list_for_each_entry(child, &pdev->subordinate->devices, bus_list) {
-		pos = pci_find_capability(child, PCI_CAP_ID_EXP);
+		pos = pci_pcie_cap(child);
 		if (!pos)
 			return -EINVAL;
 		/*
@@ -563,7 +563,7 @@
 	struct pcie_link_state *link;
 	int blacklist = !!pcie_aspm_sanity_check(pdev);
 
-	if (aspm_disabled || !pdev->is_pcie || pdev->link_state)
+	if (aspm_disabled || !pci_is_pcie(pdev) || pdev->link_state)
 		return;
 	if (pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT &&
 	    pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM)
@@ -629,7 +629,8 @@
 	struct pci_dev *parent = pdev->bus->self;
 	struct pcie_link_state *link, *root, *parent_link;
 
-	if (aspm_disabled || !pdev->is_pcie || !parent || !parent->link_state)
+	if (aspm_disabled || !pci_is_pcie(pdev) ||
+	    !parent || !parent->link_state)
 		return;
 	if ((parent->pcie_type != PCI_EXP_TYPE_ROOT_PORT) &&
 	    (parent->pcie_type != PCI_EXP_TYPE_DOWNSTREAM))
@@ -670,7 +671,7 @@
 {
 	struct pcie_link_state *link = pdev->link_state;
 
-	if (aspm_disabled || !pdev->is_pcie || !link)
+	if (aspm_disabled || !pci_is_pcie(pdev) || !link)
 		return;
 	if ((pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT) &&
 	    (pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM))
@@ -696,7 +697,7 @@
 	struct pci_dev *parent = pdev->bus->self;
 	struct pcie_link_state *link;
 
-	if (aspm_disabled || !pdev->is_pcie)
+	if (aspm_disabled || !pci_is_pcie(pdev))
 		return;
 	if (pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT ||
 	    pdev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM)
@@ -841,8 +842,9 @@
 {
 	struct pcie_link_state *link_state = pdev->link_state;
 
-	if (!pdev->is_pcie || (pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT &&
-		pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM) || !link_state)
+	if (!pci_is_pcie(pdev) ||
+	    (pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT &&
+	     pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM) || !link_state)
 		return;
 
 	if (link_state->aspm_support)
@@ -857,8 +859,9 @@
 {
 	struct pcie_link_state *link_state = pdev->link_state;
 
-	if (!pdev->is_pcie || (pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT &&
-		pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM) || !link_state)
+	if (!pci_is_pcie(pdev) ||
+	    (pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT &&
+	     pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM) || !link_state)
 		return;
 
 	if (link_state->aspm_support)
diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h
index 17ad538..aaeb9d2 100644
--- a/drivers/pci/pcie/portdrv.h
+++ b/drivers/pci/pcie/portdrv.h
@@ -11,31 +11,16 @@
 
 #include <linux/compiler.h>
 
-#if !defined(PCI_CAP_ID_PME)
-#define PCI_CAP_ID_PME			1
-#endif
-
-#if !defined(PCI_CAP_ID_EXP)
-#define PCI_CAP_ID_EXP			0x10
-#endif
-
-#define PORT_TYPE_MASK			0xf
-#define PORT_TO_SLOT_MASK		0x100
-#define SLOT_HP_CAPABLE_MASK		0x40
-#define PCIE_CAPABILITIES_REG		0x2
-#define PCIE_SLOT_CAPABILITIES_REG	0x14
-#define PCIE_PORT_DEVICE_MAXSERVICES	4
-#define PCIE_PORT_MSI_VECTOR_MASK	0x1f
+#define PCIE_PORT_DEVICE_MAXSERVICES   4
 /*
- * According to the PCI Express Base Specification 2.0, the indices of the MSI-X
- * table entires used by port services must not exceed 31
+ * According to the PCI Express Base Specification 2.0, the indices of
+ * the MSI-X table entires used by port services must not exceed 31
  */
 #define PCIE_PORT_MAX_MSIX_ENTRIES	32
 
 #define get_descriptor_id(type, service) (((type - 4) << 4) | service)
 
 extern struct bus_type pcie_port_bus_type;
-extern int pcie_port_device_probe(struct pci_dev *dev);
 extern int pcie_port_device_register(struct pci_dev *dev);
 #ifdef CONFIG_PM
 extern int pcie_port_device_suspend(struct device *dev);
diff --git a/drivers/pci/pcie/portdrv_bus.c b/drivers/pci/pcie/portdrv_bus.c
index ef3a4ee..18bf90f 100644
--- a/drivers/pci/pcie/portdrv_bus.c
+++ b/drivers/pci/pcie/portdrv_bus.c
@@ -26,7 +26,6 @@
 static int pcie_port_bus_match(struct device *dev, struct device_driver *drv)
 {
 	struct pcie_device *pciedev;
-	struct pcie_port_data *port_data;
 	struct pcie_port_service_driver *driver;
 
 	if (drv->bus != &pcie_port_bus_type || dev->bus != &pcie_port_bus_type)
@@ -38,10 +37,8 @@
 	if (driver->service != pciedev->service)
 		return 0;
 
-	port_data = pci_get_drvdata(pciedev->port);
-
-	if (driver->port_type != PCIE_ANY_PORT
-	     && driver->port_type != port_data->port_type)
+	if ((driver->port_type != PCIE_ANY_PORT) &&
+	    (driver->port_type != pciedev->port->pcie_type))
 		return 0;
 
 	return 1;
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c
index 52f84fc..413262e 100644
--- a/drivers/pci/pcie/portdrv_core.c
+++ b/drivers/pci/pcie/portdrv_core.c
@@ -108,9 +108,9 @@
 		 * the value in this field indicates which MSI-X Table entry is
 		 * used to generate the interrupt message."
 		 */
-		pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
-		pci_read_config_word(dev, pos + PCIE_CAPABILITIES_REG, &reg16);
-		entry = (reg16 >> 9) & PCIE_PORT_MSI_VECTOR_MASK;
+		pos = pci_pcie_cap(dev);
+		pci_read_config_word(dev, pos + PCI_EXP_FLAGS, &reg16);
+		entry = (reg16 & PCI_EXP_FLAGS_IRQ) >> 9;
 		if (entry >= nr_entries)
 			goto Error;
 
@@ -177,37 +177,40 @@
 }
 
 /**
- * assign_interrupt_mode - choose interrupt mode for PCI Express port services
- *                         (INTx, MSI-X, MSI) and set up vectors
+ * init_service_irqs - initialize irqs for PCI Express port services
  * @dev: PCI Express port to handle
- * @vectors: Array of interrupt vectors to populate
+ * @irqs: Array of irqs to populate
  * @mask: Bitmask of port capabilities returned by get_port_device_capability()
  *
  * Return value: Interrupt mode associated with the port
  */
-static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask)
+static int init_service_irqs(struct pci_dev *dev, int *irqs, int mask)
 {
-	int irq, interrupt_mode = PCIE_PORT_NO_IRQ;
-	int i;
+	int i, irq;
 
 	/* Try to use MSI-X if supported */
-	if (!pcie_port_enable_msix(dev, vectors, mask))
-		return PCIE_PORT_MSIX_MODE;
-
+	if (!pcie_port_enable_msix(dev, irqs, mask))
+		return 0;
 	/* We're not going to use MSI-X, so try MSI and fall back to INTx */
-	if (!pci_enable_msi(dev))
-		interrupt_mode = PCIE_PORT_MSI_MODE;
+	irq = -1;
+	if (!pci_enable_msi(dev) || dev->pin)
+		irq = dev->irq;
 
-	if (interrupt_mode == PCIE_PORT_NO_IRQ && dev->pin)
-		interrupt_mode = PCIE_PORT_INTx_MODE;
-
-	irq = interrupt_mode != PCIE_PORT_NO_IRQ ? dev->irq : -1;
 	for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
-		vectors[i] = irq;
+		irqs[i] = irq;
+	irqs[PCIE_PORT_SERVICE_VC_SHIFT] = -1;
 
-	vectors[PCIE_PORT_SERVICE_VC_SHIFT] = -1;
+	if (irq < 0)
+		return -ENODEV;
+	return 0;
+}
 
-	return interrupt_mode;
+static void cleanup_service_irqs(struct pci_dev *dev)
+{
+	if (dev->msix_enabled)
+		pci_disable_msix(dev);
+	else if (dev->msi_enabled)
+		pci_disable_msi(dev);
 }
 
 /**
@@ -226,13 +229,12 @@
 	u16 reg16;
 	u32 reg32;
 
-	pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
-	pci_read_config_word(dev, pos + PCIE_CAPABILITIES_REG, &reg16);
+	pos = pci_pcie_cap(dev);
+	pci_read_config_word(dev, pos + PCI_EXP_FLAGS, &reg16);
 	/* Hot-Plug Capable */
-	if (reg16 & PORT_TO_SLOT_MASK) {
-		pci_read_config_dword(dev, 
-			pos + PCIE_SLOT_CAPABILITIES_REG, &reg32);
-		if (reg32 & SLOT_HP_CAPABLE_MASK)
+	if (reg16 & PCI_EXP_FLAGS_SLOT) {
+		pci_read_config_dword(dev, pos + PCI_EXP_SLTCAP, &reg32);
+		if (reg32 & PCI_EXP_SLTCAP_HPC)
 			services |= PCIE_PORT_SERVICE_HP;
 	}
 	/* AER capable */
@@ -241,80 +243,47 @@
 	/* VC support */
 	if (pci_find_ext_capability(dev, PCI_EXT_CAP_ID_VC))
 		services |= PCIE_PORT_SERVICE_VC;
+	/* Root ports are capable of generating PME too */
+	if (dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT)
+		services |= PCIE_PORT_SERVICE_PME;
 
 	return services;
 }
 
 /**
- * pcie_device_init - initialize PCI Express port service device
- * @dev: Port service device to initialize
- * @parent: PCI Express port to associate the service device with
- * @port_type: Type of the port
- * @service_type: Type of service to associate with the service device
+ * pcie_device_init - allocate and initialize PCI Express port service device
+ * @pdev: PCI Express port to associate the service device with
+ * @service: Type of service to associate with the service device
  * @irq: Interrupt vector to associate with the service device
  */
-static void pcie_device_init(struct pci_dev *parent, struct pcie_device *dev, 
-	int service_type, int irq)
+static int pcie_device_init(struct pci_dev *pdev, int service, int irq)
 {
-	struct pcie_port_data *port_data = pci_get_drvdata(parent);
+	int retval;
+	struct pcie_device *pcie;
 	struct device *device;
-	int port_type = port_data->port_type;
 
-	dev->port = parent;
-	dev->irq = irq;
-	dev->service = service_type;
+	pcie = kzalloc(sizeof(*pcie), GFP_KERNEL);
+	if (!pcie)
+		return -ENOMEM;
+	pcie->port = pdev;
+	pcie->irq = irq;
+	pcie->service = service;
 
 	/* Initialize generic device interface */
-	device = &dev->device;
-	memset(device, 0, sizeof(struct device));
+	device = &pcie->device;
 	device->bus = &pcie_port_bus_type;
-	device->driver = NULL;
-	dev_set_drvdata(device, NULL);
 	device->release = release_pcie_device;	/* callback to free pcie dev */
 	dev_set_name(device, "%s:pcie%02x",
-		 pci_name(parent), get_descriptor_id(port_type, service_type));
-	device->parent = &parent->dev;
-}
+		     pci_name(pdev),
+		     get_descriptor_id(pdev->pcie_type, service));
+	device->parent = &pdev->dev;
 
-/**
- * alloc_pcie_device - allocate PCI Express port service device structure
- * @parent: PCI Express port to associate the service device with
- * @port_type: Type of the port
- * @service_type: Type of service to associate with the service device
- * @irq: Interrupt vector to associate with the service device
- */
-static struct pcie_device* alloc_pcie_device(struct pci_dev *parent,
-	int service_type, int irq)
-{
-	struct pcie_device *device;
-
-	device = kzalloc(sizeof(struct pcie_device), GFP_KERNEL);
-	if (!device)
-		return NULL;
-
-	pcie_device_init(parent, device, service_type, irq);
-	return device;
-}
-
-/**
- * pcie_port_device_probe - check if device is a PCI Express port
- * @dev: Device to check
- */
-int pcie_port_device_probe(struct pci_dev *dev)
-{
-	int pos, type;
-	u16 reg;
-
-	if (!(pos = pci_find_capability(dev, PCI_CAP_ID_EXP)))
-		return -ENODEV;
-
-	pci_read_config_word(dev, pos + PCIE_CAPABILITIES_REG, &reg);
-	type = (reg >> 4) & PORT_TYPE_MASK;
-	if (	type == PCIE_RC_PORT || type == PCIE_SW_UPSTREAM_PORT ||
-		type == PCIE_SW_DOWNSTREAM_PORT )
-		return 0;
-
-	return -ENODEV;
+	retval = device_register(device);
+	if (retval)
+		kfree(pcie);
+	else
+		get_device(device);
+	return retval;
 }
 
 /**
@@ -326,77 +295,49 @@
  */
 int pcie_port_device_register(struct pci_dev *dev)
 {
-	struct pcie_port_data *port_data;
-	int status, capabilities, irq_mode, i, nr_serv;
-	int vectors[PCIE_PORT_DEVICE_MAXSERVICES];
-	u16 reg16;
+	int status, capabilities, i, nr_service;
+	int irqs[PCIE_PORT_DEVICE_MAXSERVICES];
 
-	port_data = kzalloc(sizeof(*port_data), GFP_KERNEL);
-	if (!port_data)
-		return -ENOMEM;
-	pci_set_drvdata(dev, port_data);
-
-	/* Get port type */
-	pci_read_config_word(dev,
-		pci_find_capability(dev, PCI_CAP_ID_EXP) +
-		PCIE_CAPABILITIES_REG, &reg16);
-	port_data->port_type = (reg16 >> 4) & PORT_TYPE_MASK;
-
+	/* Get and check PCI Express port services */
 	capabilities = get_port_device_capability(dev);
-	/* Root ports are capable of generating PME too */
-	if (port_data->port_type == PCIE_RC_PORT)
-		capabilities |= PCIE_PORT_SERVICE_PME;
+	if (!capabilities)
+		return -ENODEV;
 
-	irq_mode = assign_interrupt_mode(dev, vectors, capabilities);
-	if (irq_mode == PCIE_PORT_NO_IRQ) {
-		/*
-		 * Don't use service devices that require interrupts if there is
-		 * no way to generate them.
-		 */
-		if (!(capabilities & PCIE_PORT_SERVICE_VC)) {
-			status = -ENODEV;
-			goto Error;
-		}
-		capabilities = PCIE_PORT_SERVICE_VC;
-	}
-	port_data->port_irq_mode = irq_mode;
-
+	/* Enable PCI Express port device */
 	status = pci_enable_device(dev);
 	if (status)
-		goto Error;
+		return status;
 	pci_set_master(dev);
+	/*
+	 * Initialize service irqs. Don't use service devices that
+	 * require interrupts if there is no way to generate them.
+	 */
+	status = init_service_irqs(dev, irqs, capabilities);
+	if (status) {
+		capabilities &= PCIE_PORT_SERVICE_VC;
+		if (!capabilities)
+			goto error_disable;
+	}
 
 	/* Allocate child services if any */
-	for (i = 0, nr_serv = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
-		struct pcie_device *child;
+	status = -ENODEV;
+	nr_service = 0;
+	for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
 		int service = 1 << i;
-
 		if (!(capabilities & service))
 			continue;
-
-		child = alloc_pcie_device(dev, service, vectors[i]);
-		if (!child)
-			continue;
-
-		status = device_register(&child->device);
-		if (status) {
-			kfree(child);
-			continue;
-		}
-
-		get_device(&child->device);
-		nr_serv++;
+		if (!pcie_device_init(dev, service, irqs[i]))
+			nr_service++;
 	}
-	if (!nr_serv) {
-		pci_disable_device(dev);
-		status = -ENODEV;
-		goto Error;
-	}
+	if (!nr_service)
+		goto error_cleanup_irqs;
 
 	return 0;
 
- Error:
-	kfree(port_data);
+error_cleanup_irqs:
+	cleanup_service_irqs(dev);
+error_disable:
+	pci_disable_device(dev);
 	return status;
 }
 
@@ -464,21 +405,9 @@
  */
 void pcie_port_device_remove(struct pci_dev *dev)
 {
-	struct pcie_port_data *port_data = pci_get_drvdata(dev);
-
 	device_for_each_child(&dev->dev, NULL, remove_iter);
+	cleanup_service_irqs(dev);
 	pci_disable_device(dev);
-
-	switch (port_data->port_irq_mode) {
-	case PCIE_PORT_MSIX_MODE:
-		pci_disable_msix(dev);
-		break;
-	case PCIE_PORT_MSI_MODE:
-		pci_disable_msi(dev);
-		break;
-	}
-
-	kfree(port_data);
 }
 
 /**
diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c
index f635e47..ce52ea3 100644
--- a/drivers/pci/pcie/portdrv_pci.c
+++ b/drivers/pci/pcie/portdrv_pci.c
@@ -67,14 +67,16 @@
  * this port device.
  *
  */
-static int __devinit pcie_portdrv_probe (struct pci_dev *dev, 
-				const struct pci_device_id *id )
+static int __devinit pcie_portdrv_probe(struct pci_dev *dev,
+					const struct pci_device_id *id)
 {
-	int			status;
+	int status;
 
-	status = pcie_port_device_probe(dev);
-	if (status)
-		return status;
+	if (!pci_is_pcie(dev) ||
+	    ((dev->pcie_type != PCI_EXP_TYPE_ROOT_PORT) &&
+	     (dev->pcie_type != PCI_EXP_TYPE_UPSTREAM) &&
+	     (dev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM)))
+		return -ENODEV;
 
         if (!dev->irq && dev->pin) {
 		dev_warn(&dev->dev, "device [%04x:%04x] has invalid IRQ; "
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 8105e32..98ffb2d 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -10,6 +10,7 @@
 #include <linux/module.h>
 #include <linux/cpumask.h>
 #include <linux/pci-aspm.h>
+#include <acpi/acpi_hest.h>
 #include "pci.h"
 
 #define CARDBUS_LATENCY_TIMER	176	/* secondary latency timer */
@@ -163,12 +164,12 @@
 {
 	u32 l, sz, mask;
 
-	mask = type ? ~PCI_ROM_ADDRESS_ENABLE : ~0;
+	mask = type ? PCI_ROM_ADDRESS_MASK : ~0;
 
 	res->name = pci_name(dev);
 
 	pci_read_config_dword(dev, pos, &l);
-	pci_write_config_dword(dev, pos, mask);
+	pci_write_config_dword(dev, pos, l | mask);
 	pci_read_config_dword(dev, pos, &sz);
 	pci_write_config_dword(dev, pos, l);
 
@@ -223,9 +224,13 @@
 			goto fail;
 
 		if ((sizeof(resource_size_t) < 8) && (sz64 > 0x100000000ULL)) {
-			dev_err(&dev->dev, "can't handle 64-bit BAR\n");
+			dev_err(&dev->dev, "reg %x: can't handle 64-bit BAR\n",
+				pos);
 			goto fail;
-		} else if ((sizeof(resource_size_t) < 8) && l) {
+		}
+
+		res->flags |= IORESOURCE_MEM_64;
+		if ((sizeof(resource_size_t) < 8) && l) {
 			/* Address above 32-bit boundary; disable the BAR */
 			pci_write_config_dword(dev, pos, 0);
 			pci_write_config_dword(dev, pos + 4, 0);
@@ -234,14 +239,9 @@
 		} else {
 			res->start = l64;
 			res->end = l64 + sz64;
-			dev_printk(KERN_DEBUG, &dev->dev,
-				"reg %x %s: %pR\n", pos,
-				 (res->flags & IORESOURCE_PREFETCH) ?
-					"64bit mmio pref" : "64bit mmio",
-				 res);
+			dev_printk(KERN_DEBUG, &dev->dev, "reg %x: %pR\n",
+				   pos, res);
 		}
-
-		res->flags |= IORESOURCE_MEM_64;
 	} else {
 		sz = pci_size(l, sz, mask);
 
@@ -251,11 +251,7 @@
 		res->start = l;
 		res->end = l + sz;
 
-		dev_printk(KERN_DEBUG, &dev->dev, "reg %x %s: %pR\n", pos,
-			(res->flags & IORESOURCE_IO) ? "io port" :
-			 ((res->flags & IORESOURCE_PREFETCH) ?
-				 "32bit mmio pref" : "32bit mmio"),
-			res);
+		dev_printk(KERN_DEBUG, &dev->dev, "reg %x: %pR\n", pos, res);
 	}
 
  out:
@@ -297,8 +293,11 @@
 	if (pci_is_root_bus(child))	/* It's a host bus, nothing to read */
 		return;
 
+	dev_info(&dev->dev, "PCI bridge to [bus %02x-%02x]%s\n",
+		 child->secondary, child->subordinate,
+		 dev->transparent ? " (subtractive decode)": "");
+
 	if (dev->transparent) {
-		dev_info(&dev->dev, "transparent bridge\n");
 		for(i = 3; i < PCI_BUS_NUM_RESOURCES; i++)
 			child->resource[i] = child->parent->resource[i - 3];
 	}
@@ -323,7 +322,7 @@
 			res->start = base;
 		if (!res->end)
 			res->end = limit + 0xfff;
-		dev_printk(KERN_DEBUG, &dev->dev, "bridge io port: %pR\n", res);
+		dev_printk(KERN_DEBUG, &dev->dev, "  bridge window %pR\n", res);
 	}
 
 	res = child->resource[1];
@@ -335,8 +334,7 @@
 		res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM;
 		res->start = base;
 		res->end = limit + 0xfffff;
-		dev_printk(KERN_DEBUG, &dev->dev, "bridge 32bit mmio: %pR\n",
-			res);
+		dev_printk(KERN_DEBUG, &dev->dev, "  bridge window %pR\n", res);
 	}
 
 	res = child->resource[2];
@@ -375,9 +373,7 @@
 			res->flags |= IORESOURCE_MEM_64;
 		res->start = base;
 		res->end = limit + 0xfffff;
-		dev_printk(KERN_DEBUG, &dev->dev, "bridge %sbit mmio pref: %pR\n",
-			(res->flags & PCI_PREF_RANGE_TYPE_64) ? "64" : "32",
-			res);
+		dev_printk(KERN_DEBUG, &dev->dev, "  bridge window %pR\n", res);
 	}
 }
 
@@ -651,13 +647,14 @@
 		    (child->number > bus->subordinate) ||
 		    (child->number < bus->number) ||
 		    (child->subordinate < bus->number)) {
-			pr_debug("PCI: Bus #%02x (-#%02x) is %s "
-				"hidden behind%s bridge #%02x (-#%02x)\n",
+			dev_info(&child->dev, "[bus %02x-%02x] %s "
+				"hidden behind%s bridge %s [bus %02x-%02x]\n",
 				child->number, child->subordinate,
 				(bus->number > child->subordinate &&
 				 bus->subordinate < child->number) ?
 					"wholly" : "partially",
 				bus->self->transparent ? " transparent" : "",
+				dev_name(&bus->dev),
 				bus->number, bus->subordinate);
 		}
 		bus = bus->parent;
@@ -693,6 +690,7 @@
 	if (!pos)
 		return;
 	pdev->is_pcie = 1;
+	pdev->pcie_cap = pos;
 	pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, &reg16);
 	pdev->pcie_type = (reg16 & PCI_EXP_FLAGS_TYPE) >> 4;
 }
@@ -703,7 +701,7 @@
 	u16 reg16;
 	u32 reg32;
 
-	pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+	pos = pci_pcie_cap(pdev);
 	if (!pos)
 		return;
 	pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, &reg16);
@@ -714,6 +712,12 @@
 		pdev->is_hotplug_bridge = 1;
 }
 
+static void set_pci_aer_firmware_first(struct pci_dev *pdev)
+{
+	if (acpi_hest_firmware_first_pci(pdev))
+		pdev->aer_firmware_first = 1;
+}
+
 #define LEGACY_IO_RESOURCE	(IORESOURCE_IO | IORESOURCE_PCI_FIXED)
 
 /**
@@ -731,6 +735,7 @@
 	u32 class;
 	u8 hdr_type;
 	struct pci_slot *slot;
+	int pos = 0;
 
 	if (pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr_type))
 		return -EIO;
@@ -742,6 +747,7 @@
 	dev->multifunction = !!(hdr_type & 0x80);
 	dev->error_state = pci_channel_io_normal;
 	set_pcie_port_type(dev);
+	set_pci_aer_firmware_first(dev);
 
 	list_for_each_entry(slot, &dev->bus->slots, list)
 		if (PCI_SLOT(dev->devfn) == slot->number)
@@ -822,6 +828,11 @@
 		dev->transparent = ((dev->class & 0xff) == 1);
 		pci_read_bases(dev, 2, PCI_ROM_ADDRESS1);
 		set_pcie_hotplug_bridge(dev);
+		pos = pci_find_capability(dev, PCI_CAP_ID_SSVID);
+		if (pos) {
+			pci_read_config_word(dev, pos + PCI_SSVID_VENDOR_ID, &dev->subsystem_vendor);
+			pci_read_config_word(dev, pos + PCI_SSVID_DEVICE_ID, &dev->subsystem_device);
+		}
 		break;
 
 	case PCI_HEADER_TYPE_CARDBUS:		    /* CardBus bridge header */
@@ -907,7 +918,7 @@
 	if (class == PCI_CLASS_BRIDGE_HOST)
 		return pci_cfg_space_size_ext(dev);
 
-	pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+	pos = pci_pcie_cap(dev);
 	if (!pos) {
 		pos = pci_find_capability(dev, PCI_CAP_ID_PCIX);
 		if (!pos)
@@ -1014,6 +1025,9 @@
 
 	/* Single Root I/O Virtualization */
 	pci_iov_init(dev);
+
+	/* Enable ACS P2P upstream forwarding */
+	pci_enable_acs(dev);
 }
 
 void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
@@ -1110,7 +1124,7 @@
 	unsigned int devfn, pass, max = bus->secondary;
 	struct pci_dev *dev;
 
-	pr_debug("PCI: Scanning bus %04x:%02x\n", pci_domain_nr(bus), bus->number);
+	dev_dbg(&bus->dev, "scanning bus\n");
 
 	/* Go find them, Rover! */
 	for (devfn = 0; devfn < 0x100; devfn += 8)
@@ -1124,8 +1138,7 @@
 	 * all PCI-to-PCI bridges on this bus.
 	 */
 	if (!bus->is_added) {
-		pr_debug("PCI: Fixups for bus %04x:%02x\n",
-			 pci_domain_nr(bus), bus->number);
+		dev_dbg(&bus->dev, "fixups for bus\n");
 		pcibios_fixup_bus(bus);
 		if (pci_is_root_bus(bus))
 			bus->is_added = 1;
@@ -1145,8 +1158,7 @@
 	 *
 	 * Return how far we've got finding sub-buses.
 	 */
-	pr_debug("PCI: Bus scan for %04x:%02x returning with max=%02x\n",
-		pci_domain_nr(bus), bus->number, max);
+	dev_dbg(&bus->dev, "bus scan returning with max=%02x\n", max);
 	return max;
 }
 
@@ -1154,7 +1166,7 @@
 		int bus, struct pci_ops *ops, void *sysdata)
 {
 	int error;
-	struct pci_bus *b;
+	struct pci_bus *b, *b2;
 	struct device *dev;
 
 	b = pci_alloc_bus();
@@ -1170,9 +1182,10 @@
 	b->sysdata = sysdata;
 	b->ops = ops;
 
-	if (pci_find_bus(pci_domain_nr(b), bus)) {
+	b2 = pci_find_bus(pci_domain_nr(b), bus);
+	if (b2) {
 		/* If we already got to this bus through a different bridge, ignore it */
-		pr_debug("PCI: Bus %04x:%02x already known\n", pci_domain_nr(b), bus);
+		dev_dbg(&b2->dev, "bus already known\n");
 		goto err_out;
 	}
 
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 245d2cd..7cfa7c3 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -357,7 +357,7 @@
 		pcibios_bus_to_resource(dev, res, &bus_region);
 
 		pci_claim_resource(dev, nr);
-		dev_info(&dev->dev, "quirk: region %04x-%04x claimed by %s\n", region, region + size - 1, name);
+		dev_info(&dev->dev, "quirk: %pR claimed by %s\n", res, name);
 	}
 }	
 
@@ -1680,6 +1680,7 @@
  */
 #define AMD_813X_MISC			0x40
 #define AMD_813X_NOIOAMODE		(1<<0)
+#define AMD_813X_REV_B1			0x12
 #define AMD_813X_REV_B2			0x13
 
 static void quirk_disable_amd_813x_boot_interrupt(struct pci_dev *dev)
@@ -1688,7 +1689,8 @@
 
 	if (noioapicquirk)
 		return;
-	if (dev->revision == AMD_813X_REV_B2)
+	if ((dev->revision == AMD_813X_REV_B1) ||
+	    (dev->revision == AMD_813X_REV_B2))
 		return;
 
 	pci_read_config_dword(dev, AMD_813X_MISC, &pci_config_dword);
@@ -1698,8 +1700,10 @@
 	dev_info(&dev->dev, "disabled boot interrupts on device [%04x:%04x]\n",
 		 dev->vendor, dev->device);
 }
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD,   PCI_DEVICE_ID_AMD_8131_BRIDGE, 	quirk_disable_amd_813x_boot_interrupt);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD,   PCI_DEVICE_ID_AMD_8132_BRIDGE, 	quirk_disable_amd_813x_boot_interrupt);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD,	PCI_DEVICE_ID_AMD_8131_BRIDGE,	quirk_disable_amd_813x_boot_interrupt);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD,	PCI_DEVICE_ID_AMD_8131_BRIDGE,	quirk_disable_amd_813x_boot_interrupt);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD,	PCI_DEVICE_ID_AMD_8132_BRIDGE,	quirk_disable_amd_813x_boot_interrupt);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD,	PCI_DEVICE_ID_AMD_8132_BRIDGE,	quirk_disable_amd_813x_boot_interrupt);
 
 #define AMD_8111_PCI_IRQ_ROUTING	0x56
 
@@ -2595,9 +2599,37 @@
 static int __init pci_apply_final_quirks(void)
 {
 	struct pci_dev *dev = NULL;
+	u8 cls = 0;
+	u8 tmp;
+
+	if (pci_cache_line_size)
+		printk(KERN_DEBUG "PCI: CLS %u bytes\n",
+		       pci_cache_line_size << 2);
 
 	while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
 		pci_fixup_device(pci_fixup_final, dev);
+		/*
+		 * If arch hasn't set it explicitly yet, use the CLS
+		 * value shared by all PCI devices.  If there's a
+		 * mismatch, fall back to the default value.
+		 */
+		if (!pci_cache_line_size) {
+			pci_read_config_byte(dev, PCI_CACHE_LINE_SIZE, &tmp);
+			if (!cls)
+				cls = tmp;
+			if (!tmp || cls == tmp)
+				continue;
+
+			printk(KERN_DEBUG "PCI: CLS mismatch (%u != %u), "
+			       "using %u bytes\n", cls << 2, tmp << 2,
+			       pci_dfl_cache_line_size << 2);
+			pci_cache_line_size = pci_dfl_cache_line_size;
+		}
+	}
+	if (!pci_cache_line_size) {
+		printk(KERN_DEBUG "PCI: CLS %u bytes, default %u\n",
+		       cls << 2, pci_dfl_cache_line_size << 2);
+		pci_cache_line_size = cls;
 	}
 
 	return 0;
diff --git a/drivers/pci/search.c b/drivers/pci/search.c
index ec41535..6dae871 100644
--- a/drivers/pci/search.c
+++ b/drivers/pci/search.c
@@ -26,14 +26,14 @@
 {
 	struct pci_dev *tmp = NULL;
 
-	if (pdev->is_pcie)
+	if (pci_is_pcie(pdev))
 		return NULL;
 	while (1) {
 		if (pci_is_root_bus(pdev->bus))
 			break;
 		pdev = pdev->bus->self;
 		/* a p2p bridge */
-		if (!pdev->is_pcie) {
+		if (!pci_is_pcie(pdev)) {
 			tmp = pdev;
 			continue;
 		}
@@ -149,32 +149,33 @@
 }
 
 /**
- * pci_get_bus_and_slot - locate PCI device from a given PCI bus & slot
- * @bus: number of PCI bus on which desired PCI device resides
- * @devfn: encodes number of PCI slot in which the desired PCI
- * device resides and the logical device number within that slot
- * in case of multi-function devices.
+ * pci_get_domain_bus_and_slot - locate PCI device for a given PCI domain (segment), bus, and slot
+ * @domain: PCI domain/segment on which the PCI device resides.
+ * @bus: PCI bus on which desired PCI device resides
+ * @devfn: encodes number of PCI slot in which the desired PCI device
+ * resides and the logical device number within that slot in case of
+ * multi-function devices.
  *
- * Note: the bus/slot search is limited to PCI domain (segment) 0.
- *
- * Given a PCI bus and slot/function number, the desired PCI device
- * is located in system global list of PCI devices.  If the device
- * is found, a pointer to its data structure is returned.  If no
- * device is found, %NULL is returned. The returned device has its
- * reference count bumped by one.
+ * Given a PCI domain, bus, and slot/function number, the desired PCI
+ * device is located in the list of PCI devices. If the device is
+ * found, its reference count is increased and this function returns a
+ * pointer to its data structure.  The caller must decrement the
+ * reference count by calling pci_dev_put().  If no device is found,
+ * %NULL is returned.
  */
-
-struct pci_dev * pci_get_bus_and_slot(unsigned int bus, unsigned int devfn)
+struct pci_dev *pci_get_domain_bus_and_slot(int domain, unsigned int bus,
+					    unsigned int devfn)
 {
 	struct pci_dev *dev = NULL;
 
 	while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
-		if (pci_domain_nr(dev->bus) == 0 &&
-		   (dev->bus->number == bus && dev->devfn == devfn))
+		if (pci_domain_nr(dev->bus) == domain &&
+		    (dev->bus->number == bus && dev->devfn == devfn))
 			return dev;
 	}
 	return NULL;
 }
+EXPORT_SYMBOL(pci_get_domain_bus_and_slot);
 
 static int match_pci_dev_by_id(struct device *dev, void *data)
 {
@@ -354,5 +355,4 @@
 EXPORT_SYMBOL(pci_get_device);
 EXPORT_SYMBOL(pci_get_subsys);
 EXPORT_SYMBOL(pci_get_slot);
-EXPORT_SYMBOL(pci_get_bus_and_slot);
 EXPORT_SYMBOL(pci_get_class);
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index cb1a027..c48cd37 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -71,53 +71,50 @@
 void pci_setup_cardbus(struct pci_bus *bus)
 {
 	struct pci_dev *bridge = bus->self;
+	struct resource *res;
 	struct pci_bus_region region;
 
-	dev_info(&bridge->dev, "CardBus bridge, secondary bus %04x:%02x\n",
-		 pci_domain_nr(bus), bus->number);
+	dev_info(&bridge->dev, "CardBus bridge to [bus %02x-%02x]\n",
+		 bus->secondary, bus->subordinate);
 
-	pcibios_resource_to_bus(bridge, &region, bus->resource[0]);
-	if (bus->resource[0]->flags & IORESOURCE_IO) {
+	res = bus->resource[0];
+	pcibios_resource_to_bus(bridge, &region, res);
+	if (res->flags & IORESOURCE_IO) {
 		/*
 		 * The IO resource is allocated a range twice as large as it
 		 * would normally need.  This allows us to set both IO regs.
 		 */
-		dev_info(&bridge->dev, "  IO window: %#08lx-%#08lx\n",
-		       (unsigned long)region.start,
-		       (unsigned long)region.end);
+		dev_info(&bridge->dev, "  bridge window %pR\n", res);
 		pci_write_config_dword(bridge, PCI_CB_IO_BASE_0,
 					region.start);
 		pci_write_config_dword(bridge, PCI_CB_IO_LIMIT_0,
 					region.end);
 	}
 
-	pcibios_resource_to_bus(bridge, &region, bus->resource[1]);
-	if (bus->resource[1]->flags & IORESOURCE_IO) {
-		dev_info(&bridge->dev, "  IO window: %#08lx-%#08lx\n",
-		       (unsigned long)region.start,
-		       (unsigned long)region.end);
+	res = bus->resource[1];
+	pcibios_resource_to_bus(bridge, &region, res);
+	if (res->flags & IORESOURCE_IO) {
+		dev_info(&bridge->dev, "  bridge window %pR\n", res);
 		pci_write_config_dword(bridge, PCI_CB_IO_BASE_1,
 					region.start);
 		pci_write_config_dword(bridge, PCI_CB_IO_LIMIT_1,
 					region.end);
 	}
 
-	pcibios_resource_to_bus(bridge, &region, bus->resource[2]);
-	if (bus->resource[2]->flags & IORESOURCE_MEM) {
-		dev_info(&bridge->dev, "  PREFETCH window: %#08lx-%#08lx\n",
-		       (unsigned long)region.start,
-		       (unsigned long)region.end);
+	res = bus->resource[2];
+	pcibios_resource_to_bus(bridge, &region, res);
+	if (res->flags & IORESOURCE_MEM) {
+		dev_info(&bridge->dev, "  bridge window %pR\n", res);
 		pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_0,
 					region.start);
 		pci_write_config_dword(bridge, PCI_CB_MEMORY_LIMIT_0,
 					region.end);
 	}
 
-	pcibios_resource_to_bus(bridge, &region, bus->resource[3]);
-	if (bus->resource[3]->flags & IORESOURCE_MEM) {
-		dev_info(&bridge->dev, "  MEM window: %#08lx-%#08lx\n",
-		       (unsigned long)region.start,
-		       (unsigned long)region.end);
+	res = bus->resource[3];
+	pcibios_resource_to_bus(bridge, &region, res);
+	if (res->flags & IORESOURCE_MEM) {
+		dev_info(&bridge->dev, "  bridge window %pR\n", res);
 		pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_1,
 					region.start);
 		pci_write_config_dword(bridge, PCI_CB_MEMORY_LIMIT_1,
@@ -140,34 +137,33 @@
 static void pci_setup_bridge(struct pci_bus *bus)
 {
 	struct pci_dev *bridge = bus->self;
+	struct resource *res;
 	struct pci_bus_region region;
 	u32 l, bu, lu, io_upper16;
-	int pref_mem64;
 
 	if (pci_is_enabled(bridge))
 		return;
 
-	dev_info(&bridge->dev, "PCI bridge, secondary bus %04x:%02x\n",
-		 pci_domain_nr(bus), bus->number);
+	dev_info(&bridge->dev, "PCI bridge to [bus %02x-%02x]\n",
+		 bus->secondary, bus->subordinate);
 
 	/* Set up the top and bottom of the PCI I/O segment for this bus. */
-	pcibios_resource_to_bus(bridge, &region, bus->resource[0]);
-	if (bus->resource[0]->flags & IORESOURCE_IO) {
+	res = bus->resource[0];
+	pcibios_resource_to_bus(bridge, &region, res);
+	if (res->flags & IORESOURCE_IO) {
 		pci_read_config_dword(bridge, PCI_IO_BASE, &l);
 		l &= 0xffff0000;
 		l |= (region.start >> 8) & 0x00f0;
 		l |= region.end & 0xf000;
 		/* Set up upper 16 bits of I/O base/limit. */
 		io_upper16 = (region.end & 0xffff0000) | (region.start >> 16);
-		dev_info(&bridge->dev, "  IO window: %#04lx-%#04lx\n",
-		    (unsigned long)region.start,
-		    (unsigned long)region.end);
+		dev_info(&bridge->dev, "  bridge window %pR\n", res);
 	}
 	else {
 		/* Clear upper 16 bits of I/O base/limit. */
 		io_upper16 = 0;
 		l = 0x00f0;
-		dev_info(&bridge->dev, "  IO window: disabled\n");
+		dev_info(&bridge->dev, "  bridge window [io  disabled]\n");
 	}
 	/* Temporarily disable the I/O range before updating PCI_IO_BASE. */
 	pci_write_config_dword(bridge, PCI_IO_BASE_UPPER16, 0x0000ffff);
@@ -178,17 +174,16 @@
 
 	/* Set up the top and bottom of the PCI Memory segment
 	   for this bus. */
-	pcibios_resource_to_bus(bridge, &region, bus->resource[1]);
-	if (bus->resource[1]->flags & IORESOURCE_MEM) {
+	res = bus->resource[1];
+	pcibios_resource_to_bus(bridge, &region, res);
+	if (res->flags & IORESOURCE_MEM) {
 		l = (region.start >> 16) & 0xfff0;
 		l |= region.end & 0xfff00000;
-		dev_info(&bridge->dev, "  MEM window: %#08lx-%#08lx\n",
-		    (unsigned long)region.start,
-		    (unsigned long)region.end);
+		dev_info(&bridge->dev, "  bridge window %pR\n", res);
 	}
 	else {
 		l = 0x0000fff0;
-		dev_info(&bridge->dev, "  MEM window: disabled\n");
+		dev_info(&bridge->dev, "  bridge window [mem disabled]\n");
 	}
 	pci_write_config_dword(bridge, PCI_MEMORY_BASE, l);
 
@@ -198,34 +193,27 @@
 	pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, 0);
 
 	/* Set up PREF base/limit. */
-	pref_mem64 = 0;
 	bu = lu = 0;
-	pcibios_resource_to_bus(bridge, &region, bus->resource[2]);
-	if (bus->resource[2]->flags & IORESOURCE_PREFETCH) {
-		int width = 8;
+	res = bus->resource[2];
+	pcibios_resource_to_bus(bridge, &region, res);
+	if (res->flags & IORESOURCE_PREFETCH) {
 		l = (region.start >> 16) & 0xfff0;
 		l |= region.end & 0xfff00000;
-		if (bus->resource[2]->flags & IORESOURCE_MEM_64) {
-			pref_mem64 = 1;
+		if (res->flags & IORESOURCE_MEM_64) {
 			bu = upper_32_bits(region.start);
 			lu = upper_32_bits(region.end);
-			width = 16;
 		}
-		dev_info(&bridge->dev, "  PREFETCH window: %#0*llx-%#0*llx\n",
-				width, (unsigned long long)region.start,
-				width, (unsigned long long)region.end);
+		dev_info(&bridge->dev, "  bridge window %pR\n", res);
 	}
 	else {
 		l = 0x0000fff0;
-		dev_info(&bridge->dev, "  PREFETCH window: disabled\n");
+		dev_info(&bridge->dev, "  bridge window [mem pref disabled]\n");
 	}
 	pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, l);
 
-	if (pref_mem64) {
-		/* Set the upper 32 bits of PREF base & limit. */
-		pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, bu);
-		pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, lu);
-	}
+	/* Set the upper 32 bits of PREF base & limit. */
+	pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, bu);
+	pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, lu);
 
 	pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, bus->bridge_ctl);
 }
@@ -345,6 +333,10 @@
 #endif
 	size = ALIGN(size + size1, 4096);
 	if (!size) {
+		if (b_res->start || b_res->end)
+			dev_info(&bus->self->dev, "disabling bridge window "
+				 "%pR to [bus %02x-%02x] (unused)\n", b_res,
+				 bus->secondary, bus->subordinate);
 		b_res->flags = 0;
 		return;
 	}
@@ -390,8 +382,9 @@
 			align = pci_resource_alignment(dev, r);
 			order = __ffs(align) - 20;
 			if (order > 11) {
-				dev_warn(&dev->dev, "BAR %d bad alignment %llx: "
-					 "%pR\n", i, (unsigned long long)align, r);
+				dev_warn(&dev->dev, "disabling BAR %d: %pR "
+					 "(bad alignment %#llx)\n", i, r,
+					 (unsigned long long) align);
 				r->flags = 0;
 				continue;
 			}
@@ -425,6 +418,10 @@
 	}
 	size = ALIGN(size, min_align);
 	if (!size) {
+		if (b_res->start || b_res->end)
+			dev_info(&bus->self->dev, "disabling bridge window "
+				 "%pR to [bus %02x-%02x] (unused)\n", b_res,
+				 bus->secondary, bus->subordinate);
 		b_res->flags = 0;
 		return 1;
 	}
@@ -582,10 +579,7 @@
                 if (!res || !res->end)
                         continue;
 
-		dev_printk(KERN_DEBUG, &bus->dev, "resource %d %s %pR\n", i,
-			   (res->flags & IORESOURCE_IO) ? "io: " :
-			    ((res->flags & IORESOURCE_PREFETCH)? "pref mem":"mem:"),
-			   res);
+		dev_printk(KERN_DEBUG, &bus->dev, "resource %d %pR\n", i, res);
         }
 }
 
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
index c54526b..7d678bb 100644
--- a/drivers/pci/setup-res.c
+++ b/drivers/pci/setup-res.c
@@ -51,12 +51,6 @@
 
 	pcibios_resource_to_bus(dev, &region, res);
 
-	dev_dbg(&dev->dev, "BAR %d: got res %pR bus [%#llx-%#llx] "
-		"flags %#lx\n", resno, res,
-		 (unsigned long long)region.start,
-		 (unsigned long long)region.end,
-		 (unsigned long)res->flags);
-
 	new = region.start | (res->flags & PCI_REGION_FLAG_MASK);
 	if (res->flags & IORESOURCE_IO)
 		mask = (u32)PCI_BASE_ADDRESS_IO_MASK;
@@ -91,9 +85,9 @@
 		}
 	}
 	res->flags &= ~IORESOURCE_UNSET;
-	dev_dbg(&dev->dev, "BAR %d: moved to bus [%#llx-%#llx] flags %#lx\n",
-		resno, (unsigned long long)region.start,
-		(unsigned long long)region.end, res->flags);
+	dev_info(&dev->dev, "BAR %d: set to %pR (PCI address [%#llx-%#llx]\n",
+		 resno, res, (unsigned long long)region.start,
+		 (unsigned long long)region.end);
 }
 
 int pci_claim_resource(struct pci_dev *dev, int resource)
@@ -103,20 +97,17 @@
 	int err;
 
 	root = pci_find_parent_resource(dev, res);
-
-	err = -EINVAL;
-	if (root != NULL)
-		err = request_resource(root, res);
-
-	if (err) {
-		const char *dtype = resource < PCI_BRIDGE_RESOURCES ? "device" : "bridge";
-		dev_err(&dev->dev, "BAR %d: %s of %s %pR\n",
-			resource,
-			root ? "address space collision on" :
-				"no parent found for",
-			dtype, res);
+	if (!root) {
+		dev_err(&dev->dev, "no compatible bridge window for %pR\n",
+			res);
+		return -EINVAL;
 	}
 
+	err = request_resource(root, res);
+	if (err)
+		dev_err(&dev->dev,
+			"address space collision: %pR already in use\n", res);
+
 	return err;
 }
 EXPORT_SYMBOL(pci_claim_resource);
@@ -124,7 +115,7 @@
 #ifdef CONFIG_PCI_QUIRKS
 void pci_disable_bridge_window(struct pci_dev *dev)
 {
-	dev_dbg(&dev->dev, "Disabling bridge window.\n");
+	dev_info(&dev->dev, "disabling bridge mem windows\n");
 
 	/* MMIO Base/Limit */
 	pci_write_config_dword(dev, PCI_MEMORY_BASE, 0x0000fff0);
@@ -165,6 +156,7 @@
 
 	if (!ret) {
 		res->flags &= ~IORESOURCE_STARTALIGN;
+		dev_info(&dev->dev, "BAR %d: assigned %pR\n", resno, res);
 		if (resno < PCI_BRIDGE_RESOURCES)
 			pci_update_resource(dev, resno);
 	}
@@ -178,12 +170,12 @@
 	resource_size_t align;
 	struct pci_bus *bus;
 	int ret;
+	char *type;
 
 	align = pci_resource_alignment(dev, res);
 	if (!align) {
-		dev_info(&dev->dev, "BAR %d: can't allocate resource (bogus "
-			"alignment) %pR flags %#lx\n",
-			resno, res, res->flags);
+		dev_info(&dev->dev, "BAR %d: can't assign %pR "
+			 "(bogus alignment)\n", resno, res);
 		return -EINVAL;
 	}
 
@@ -198,9 +190,20 @@
 		break;
 	}
 
-	if (ret)
-		dev_info(&dev->dev, "BAR %d: can't allocate %s resource %pR\n",
-			resno, res->flags & IORESOURCE_IO ? "I/O" : "mem", res);
+	if (ret) {
+		if (res->flags & IORESOURCE_MEM)
+			if (res->flags & IORESOURCE_PREFETCH)
+				type = "mem pref";
+			else
+				type = "mem";
+		else if (res->flags & IORESOURCE_IO)
+			type = "io";
+		else
+			type = "unknown";
+		dev_info(&dev->dev,
+			 "BAR %d: can't assign %s (size %#llx)\n",
+			 resno, type, (unsigned long long) resource_size(res));
+	}
 
 	return ret;
 }
@@ -225,9 +228,8 @@
 
 		r_align = pci_resource_alignment(dev, r);
 		if (!r_align) {
-			dev_warn(&dev->dev, "BAR %d: bogus alignment "
-				"%pR flags %#lx\n",
-				i, r, r->flags);
+			dev_warn(&dev->dev, "BAR %d: %pR has bogus alignment\n",
+				 i, r);
 			continue;
 		}
 		for (list = head; ; list = list->next) {
@@ -274,8 +276,8 @@
 			continue;
 
 		if (!r->parent) {
-			dev_err(&dev->dev, "device not available because of "
-				"BAR %d %pR collisions\n", i, r);
+			dev_err(&dev->dev, "device not available "
+				"(can't reserve %pR)\n", r);
 			return -EINVAL;
 		}
 
diff --git a/drivers/pcmcia/cardbus.c b/drivers/pcmcia/cardbus.c
index 4cd70d0..a73b040 100644
--- a/drivers/pcmcia/cardbus.c
+++ b/drivers/pcmcia/cardbus.c
@@ -184,26 +184,33 @@
     
 =====================================================================*/
 
-/*
- * Since there is only one interrupt available to CardBus
- * devices, all devices downstream of this device must
- * be using this IRQ.
- */
-static void cardbus_assign_irqs(struct pci_bus *bus, int irq)
+static void cardbus_config_irq_and_cls(struct pci_bus *bus, int irq)
 {
 	struct pci_dev *dev;
 
 	list_for_each_entry(dev, &bus->devices, bus_list) {
 		u8 irq_pin;
 
+		/*
+		 * Since there is only one interrupt available to
+		 * CardBus devices, all devices downstream of this
+		 * device must be using this IRQ.
+		 */
 		pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irq_pin);
 		if (irq_pin) {
 			dev->irq = irq;
 			pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq);
 		}
 
+		/*
+		 * Some controllers transfer very slowly with 0 CLS.
+		 * Configure it.  This may fail as CLS configuration
+		 * is mandatory only for MWI.
+		 */
+		pci_set_cacheline_size(dev);
+
 		if (dev->subordinate)
-			cardbus_assign_irqs(dev->subordinate, irq);
+			cardbus_config_irq_and_cls(dev->subordinate, irq);
 	}
 }
 
@@ -228,7 +235,7 @@
 	 */
 	pci_bus_size_bridges(bus);
 	pci_bus_assign_resources(bus);
-	cardbus_assign_irqs(bus, s->pci_irq);
+	cardbus_config_irq_and_cls(bus, s->pci_irq);
 
 	/* socket specific tune function */
 	if (s->tune_bridge)
diff --git a/drivers/pnp/quirks.c b/drivers/pnp/quirks.c
index 8473fe5..dfbd5a6 100644
--- a/drivers/pnp/quirks.c
+++ b/drivers/pnp/quirks.c
@@ -285,15 +285,10 @@
 				 * the PCI region, and that might prevent a PCI
 				 * driver from requesting its resources.
 				 */
-				dev_warn(&dev->dev, "%s resource "
-					"(0x%llx-0x%llx) overlaps %s BAR %d "
-					"(0x%llx-0x%llx), disabling\n",
-					pnp_resource_type_name(res),
-					(unsigned long long) pnp_start,
-					(unsigned long long) pnp_end,
-					pci_name(pdev), i,
-					(unsigned long long) pci_start,
-					(unsigned long long) pci_end);
+				dev_warn(&dev->dev,
+					 "disabling %pR because it overlaps "
+					 "%s BAR %d %pR\n", res,
+					 pci_name(pdev), i, &pdev->resource[i]);
 				res->flags |= IORESOURCE_DISABLED;
 			}
 		}
diff --git a/drivers/pnp/resource.c b/drivers/pnp/resource.c
index ba97654..64d0596 100644
--- a/drivers/pnp/resource.c
+++ b/drivers/pnp/resource.c
@@ -517,7 +517,7 @@
 	res->start = irq;
 	res->end = irq;
 
-	pnp_dbg(&dev->dev, "  add irq %d flags %#x\n", irq, flags);
+	pnp_dbg(&dev->dev, "  add %pr\n", res);
 	return pnp_res;
 }
 
@@ -538,7 +538,7 @@
 	res->start = dma;
 	res->end = dma;
 
-	pnp_dbg(&dev->dev, "  add dma %d flags %#x\n", dma, flags);
+	pnp_dbg(&dev->dev, "  add %pr\n", res);
 	return pnp_res;
 }
 
@@ -562,8 +562,7 @@
 	res->start = start;
 	res->end = end;
 
-	pnp_dbg(&dev->dev, "  add io  %#llx-%#llx flags %#x\n",
-		(unsigned long long) start, (unsigned long long) end, flags);
+	pnp_dbg(&dev->dev, "  add %pr\n", res);
 	return pnp_res;
 }
 
@@ -587,8 +586,7 @@
 	res->start = start;
 	res->end = end;
 
-	pnp_dbg(&dev->dev, "  add mem %#llx-%#llx flags %#x\n",
-		(unsigned long long) start, (unsigned long long) end, flags);
+	pnp_dbg(&dev->dev, "  add %pr\n", res);
 	return pnp_res;
 }
 
diff --git a/drivers/pnp/support.c b/drivers/pnp/support.c
index 63087d5..9585c1c 100644
--- a/drivers/pnp/support.c
+++ b/drivers/pnp/support.c
@@ -75,47 +75,14 @@
 
 void dbg_pnp_show_resources(struct pnp_dev *dev, char *desc)
 {
-	char buf[128];
-	int len;
 	struct pnp_resource *pnp_res;
-	struct resource *res;
 
-	if (list_empty(&dev->resources)) {
+	if (list_empty(&dev->resources))
 		pnp_dbg(&dev->dev, "%s: no current resources\n", desc);
-		return;
-	}
-
-	pnp_dbg(&dev->dev, "%s: current resources:\n", desc);
-	list_for_each_entry(pnp_res, &dev->resources, list) {
-		res = &pnp_res->res;
-		len = 0;
-
-		len += scnprintf(buf + len, sizeof(buf) - len, "  %-3s ",
-				 pnp_resource_type_name(res));
-
-		if (res->flags & IORESOURCE_DISABLED) {
-			pnp_dbg(&dev->dev, "%sdisabled\n", buf);
-			continue;
-		}
-
-		switch (pnp_resource_type(res)) {
-		case IORESOURCE_IO:
-		case IORESOURCE_MEM:
-			len += scnprintf(buf + len, sizeof(buf) - len,
-					 "%#llx-%#llx flags %#lx",
-					 (unsigned long long) res->start,
-					 (unsigned long long) res->end,
-					 res->flags);
-			break;
-		case IORESOURCE_IRQ:
-		case IORESOURCE_DMA:
-			len += scnprintf(buf + len, sizeof(buf) - len,
-					 "%lld flags %#lx",
-					 (unsigned long long) res->start,
-					 res->flags);
-			break;
-		}
-		pnp_dbg(&dev->dev, "%s\n", buf);
+	else {
+		pnp_dbg(&dev->dev, "%s: current resources:\n", desc);
+		list_for_each_entry(pnp_res, &dev->resources, list)
+			pnp_dbg(&dev->dev, "%pr\n", &pnp_res->res);
 	}
 }
 
diff --git a/drivers/pnp/system.c b/drivers/pnp/system.c
index 59b9092..49c1720 100644
--- a/drivers/pnp/system.c
+++ b/drivers/pnp/system.c
@@ -22,11 +22,11 @@
 	{"", 0}
 };
 
-static void reserve_range(struct pnp_dev *dev, resource_size_t start,
-			  resource_size_t end, int port)
+static void reserve_range(struct pnp_dev *dev, struct resource *r, int port)
 {
 	char *regionid;
 	const char *pnpid = dev_name(&dev->dev);
+	resource_size_t start = r->start, end = r->end;
 	struct resource *res;
 
 	regionid = kmalloc(16, GFP_KERNEL);
@@ -48,10 +48,8 @@
 	 * example do reserve stuff they know about too, so we may well
 	 * have double reservations.
 	 */
-	dev_info(&dev->dev, "%s range 0x%llx-0x%llx %s reserved\n",
-		port ? "ioport" : "iomem",
-		(unsigned long long) start, (unsigned long long) end,
-		res ? "has been" : "could not be");
+	dev_info(&dev->dev, "%pR %s reserved\n", r,
+		 res ? "has been" : "could not be");
 }
 
 static void reserve_resources_of_dev(struct pnp_dev *dev)
@@ -77,14 +75,14 @@
 		if (res->end < res->start)
 			continue;	/* invalid */
 
-		reserve_range(dev, res->start, res->end, 1);
+		reserve_range(dev, res, 1);
 	}
 
 	for (i = 0; (res = pnp_get_resource(dev, IORESOURCE_MEM, i)); i++) {
 		if (res->flags & IORESOURCE_DISABLED)
 			continue;
 
-		reserve_range(dev, res->start, res->end, 0);
+		reserve_range(dev, res, 0);
 	}
 }
 
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index dfcd75c..80e71fc 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -103,6 +103,8 @@
 
 source "drivers/gpu/drm/radeon/Kconfig"
 
+source "drivers/gpu/drm/nouveau/Kconfig"
+
 source "drivers/staging/octeon/Kconfig"
 
 source "drivers/staging/serqt_usb2/Kconfig"
diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c
index 91a68e9..603598f 100644
--- a/drivers/video/xen-fbfront.c
+++ b/drivers/video/xen-fbfront.c
@@ -25,7 +25,10 @@
 #include <linux/module.h>
 #include <linux/vmalloc.h>
 #include <linux/mm.h>
+
 #include <asm/xen/hypervisor.h>
+
+#include <xen/xen.h>
 #include <xen/events.h>
 #include <xen/page.h>
 #include <xen/interface/io/fbif.h>
diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c
index 4204336..f6738d8 100644
--- a/drivers/xen/balloon.c
+++ b/drivers/xen/balloon.c
@@ -52,6 +52,8 @@
 
 #include <asm/xen/hypervisor.h>
 #include <asm/xen/hypercall.h>
+
+#include <xen/xen.h>
 #include <xen/interface/xen.h>
 #include <xen/interface/memory.h>
 #include <xen/xenbus.h>
diff --git a/drivers/xen/cpu_hotplug.c b/drivers/xen/cpu_hotplug.c
index 0f765a9..14e2d99 100644
--- a/drivers/xen/cpu_hotplug.c
+++ b/drivers/xen/cpu_hotplug.c
@@ -1,5 +1,6 @@
 #include <linux/notifier.h>
 
+#include <xen/xen.h>
 #include <xen/xenbus.h>
 
 #include <asm/xen/hypervisor.h>
diff --git a/drivers/xen/evtchn.c b/drivers/xen/evtchn.c
index 79bedba..f70a4f4 100644
--- a/drivers/xen/evtchn.c
+++ b/drivers/xen/evtchn.c
@@ -48,6 +48,8 @@
 #include <linux/gfp.h>
 #include <linux/mutex.h>
 #include <linux/cpu.h>
+
+#include <xen/xen.h>
 #include <xen/events.h>
 #include <xen/evtchn.h>
 #include <asm/xen/hypervisor.h>
diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c
index 7d8f531..4c6c0bd 100644
--- a/drivers/xen/grant-table.c
+++ b/drivers/xen/grant-table.c
@@ -37,6 +37,7 @@
 #include <linux/vmalloc.h>
 #include <linux/uaccess.h>
 
+#include <xen/xen.h>
 #include <xen/interface/xen.h>
 #include <xen/page.h>
 #include <xen/grant_table.h>
diff --git a/drivers/xen/sys-hypervisor.c b/drivers/xen/sys-hypervisor.c
index 88a60e0..ae5cb05 100644
--- a/drivers/xen/sys-hypervisor.c
+++ b/drivers/xen/sys-hypervisor.c
@@ -14,6 +14,7 @@
 #include <asm/xen/hypervisor.h>
 #include <asm/xen/hypercall.h>
 
+#include <xen/xen.h>
 #include <xen/xenbus.h>
 #include <xen/interface/xen.h>
 #include <xen/interface/version.h>
diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c
index 649fcdf..2f7aaa9 100644
--- a/drivers/xen/xenbus/xenbus_probe.c
+++ b/drivers/xen/xenbus/xenbus_probe.c
@@ -49,6 +49,8 @@
 #include <asm/page.h>
 #include <asm/pgtable.h>
 #include <asm/xen/hypervisor.h>
+
+#include <xen/xen.h>
 #include <xen/xenbus.h>
 #include <xen/events.h>
 #include <xen/page.h>
diff --git a/drivers/xen/xenfs/super.c b/drivers/xen/xenfs/super.c
index 6559e0c..8924d93 100644
--- a/drivers/xen/xenfs/super.c
+++ b/drivers/xen/xenfs/super.c
@@ -13,6 +13,8 @@
 #include <linux/fs.h>
 #include <linux/magic.h>
 
+#include <xen/xen.h>
+
 #include "xenfs.h"
 
 #include <asm/xen/hypervisor.h>
diff --git a/fs/nilfs2/alloc.c b/fs/nilfs2/alloc.c
index d69e6ae..3f959f1 100644
--- a/fs/nilfs2/alloc.c
+++ b/fs/nilfs2/alloc.c
@@ -142,29 +142,75 @@
 	}
 }
 
+static int nilfs_palloc_get_block(struct inode *inode, unsigned long blkoff,
+				  int create,
+				  void (*init_block)(struct inode *,
+						     struct buffer_head *,
+						     void *),
+				  struct buffer_head **bhp,
+				  struct nilfs_bh_assoc *prev,
+				  spinlock_t *lock)
+{
+	int ret;
+
+	spin_lock(lock);
+	if (prev->bh && blkoff == prev->blkoff) {
+		get_bh(prev->bh);
+		*bhp = prev->bh;
+		spin_unlock(lock);
+		return 0;
+	}
+	spin_unlock(lock);
+
+	ret = nilfs_mdt_get_block(inode, blkoff, create, init_block, bhp);
+	if (!ret) {
+		spin_lock(lock);
+		/*
+		 * The following code must be safe for change of the
+		 * cache contents during the get block call.
+		 */
+		brelse(prev->bh);
+		get_bh(*bhp);
+		prev->bh = *bhp;
+		prev->blkoff = blkoff;
+		spin_unlock(lock);
+	}
+	return ret;
+}
+
 static int nilfs_palloc_get_desc_block(struct inode *inode,
 				       unsigned long group,
 				       int create, struct buffer_head **bhp)
 {
-	return nilfs_mdt_get_block(inode,
-				   nilfs_palloc_desc_blkoff(inode, group),
-				   create, nilfs_palloc_desc_block_init, bhp);
+	struct nilfs_palloc_cache *cache = NILFS_MDT(inode)->mi_palloc_cache;
+
+	return nilfs_palloc_get_block(inode,
+				      nilfs_palloc_desc_blkoff(inode, group),
+				      create, nilfs_palloc_desc_block_init,
+				      bhp, &cache->prev_desc, &cache->lock);
 }
 
 static int nilfs_palloc_get_bitmap_block(struct inode *inode,
 					 unsigned long group,
 					 int create, struct buffer_head **bhp)
 {
-	return nilfs_mdt_get_block(inode,
-				   nilfs_palloc_bitmap_blkoff(inode, group),
-				   create, NULL, bhp);
+	struct nilfs_palloc_cache *cache = NILFS_MDT(inode)->mi_palloc_cache;
+
+	return nilfs_palloc_get_block(inode,
+				      nilfs_palloc_bitmap_blkoff(inode, group),
+				      create, NULL, bhp,
+				      &cache->prev_bitmap, &cache->lock);
 }
 
 int nilfs_palloc_get_entry_block(struct inode *inode, __u64 nr,
 				 int create, struct buffer_head **bhp)
 {
-	return nilfs_mdt_get_block(inode, nilfs_palloc_entry_blkoff(inode, nr),
-				   create, NULL, bhp);
+	struct nilfs_palloc_cache *cache = NILFS_MDT(inode)->mi_palloc_cache;
+
+	return nilfs_palloc_get_block(inode,
+				      nilfs_palloc_entry_blkoff(inode, nr),
+				      create, NULL, bhp,
+				      &cache->prev_entry, &cache->lock);
 }
 
 static struct nilfs_palloc_group_desc *
@@ -176,13 +222,6 @@
 		group % nilfs_palloc_groups_per_desc_block(inode);
 }
 
-static unsigned char *
-nilfs_palloc_block_get_bitmap(const struct inode *inode,
-			      const struct buffer_head *bh, void *kaddr)
-{
-	return (unsigned char *)(kaddr + bh_offset(bh));
-}
-
 void *nilfs_palloc_block_get_entry(const struct inode *inode, __u64 nr,
 				   const struct buffer_head *bh, void *kaddr)
 {
@@ -289,8 +328,7 @@
 				if (ret < 0)
 					goto out_desc;
 				bitmap_kaddr = kmap(bitmap_bh->b_page);
-				bitmap = nilfs_palloc_block_get_bitmap(
-					inode, bitmap_bh, bitmap_kaddr);
+				bitmap = bitmap_kaddr + bh_offset(bitmap_bh);
 				pos = nilfs_palloc_find_available_slot(
 					inode, group, group_offset, bitmap,
 					entries_per_group);
@@ -351,8 +389,7 @@
 	desc = nilfs_palloc_block_get_group_desc(inode, group,
 						 req->pr_desc_bh, desc_kaddr);
 	bitmap_kaddr = kmap(req->pr_bitmap_bh->b_page);
-	bitmap = nilfs_palloc_block_get_bitmap(inode, req->pr_bitmap_bh,
-					       bitmap_kaddr);
+	bitmap = bitmap_kaddr + bh_offset(req->pr_bitmap_bh);
 
 	if (!nilfs_clear_bit_atomic(nilfs_mdt_bgl_lock(inode, group),
 				    group_offset, bitmap))
@@ -385,8 +422,7 @@
 	desc = nilfs_palloc_block_get_group_desc(inode, group,
 						 req->pr_desc_bh, desc_kaddr);
 	bitmap_kaddr = kmap(req->pr_bitmap_bh->b_page);
-	bitmap = nilfs_palloc_block_get_bitmap(inode, req->pr_bitmap_bh,
-					       bitmap_kaddr);
+	bitmap = bitmap_kaddr + bh_offset(req->pr_bitmap_bh);
 	if (!nilfs_clear_bit_atomic(nilfs_mdt_bgl_lock(inode, group),
 				    group_offset, bitmap))
 		printk(KERN_WARNING "%s: entry numer %llu already freed\n",
@@ -472,8 +508,7 @@
 		desc = nilfs_palloc_block_get_group_desc(
 			inode, group, desc_bh, desc_kaddr);
 		bitmap_kaddr = kmap(bitmap_bh->b_page);
-		bitmap = nilfs_palloc_block_get_bitmap(
-			inode, bitmap_bh, bitmap_kaddr);
+		bitmap = bitmap_kaddr + bh_offset(bitmap_bh);
 		for (j = i, n = 0;
 		     (j < nitems) && nilfs_palloc_group_is_in(inode, group,
 							      entry_nrs[j]);
@@ -502,3 +537,30 @@
 	}
 	return 0;
 }
+
+void nilfs_palloc_setup_cache(struct inode *inode,
+			      struct nilfs_palloc_cache *cache)
+{
+	NILFS_MDT(inode)->mi_palloc_cache = cache;
+	spin_lock_init(&cache->lock);
+}
+
+void nilfs_palloc_clear_cache(struct inode *inode)
+{
+	struct nilfs_palloc_cache *cache = NILFS_MDT(inode)->mi_palloc_cache;
+
+	spin_lock(&cache->lock);
+	brelse(cache->prev_desc.bh);
+	brelse(cache->prev_bitmap.bh);
+	brelse(cache->prev_entry.bh);
+	cache->prev_desc.bh = NULL;
+	cache->prev_bitmap.bh = NULL;
+	cache->prev_entry.bh = NULL;
+	spin_unlock(&cache->lock);
+}
+
+void nilfs_palloc_destroy_cache(struct inode *inode)
+{
+	nilfs_palloc_clear_cache(inode);
+	NILFS_MDT(inode)->mi_palloc_cache = NULL;
+}
diff --git a/fs/nilfs2/alloc.h b/fs/nilfs2/alloc.h
index 4ace5475..f4543ac 100644
--- a/fs/nilfs2/alloc.h
+++ b/fs/nilfs2/alloc.h
@@ -69,4 +69,25 @@
 #define nilfs_clear_bit_atomic		ext2_clear_bit_atomic
 #define nilfs_find_next_zero_bit	ext2_find_next_zero_bit
 
+/*
+ * persistent object allocator cache
+ */
+
+struct nilfs_bh_assoc {
+	unsigned long blkoff;
+	struct buffer_head *bh;
+};
+
+struct nilfs_palloc_cache {
+	spinlock_t lock;
+	struct nilfs_bh_assoc prev_desc;
+	struct nilfs_bh_assoc prev_bitmap;
+	struct nilfs_bh_assoc prev_entry;
+};
+
+void nilfs_palloc_setup_cache(struct inode *inode,
+			      struct nilfs_palloc_cache *cache);
+void nilfs_palloc_clear_cache(struct inode *inode);
+void nilfs_palloc_destroy_cache(struct inode *inode);
+
 #endif	/* _NILFS_ALLOC_H */
diff --git a/fs/nilfs2/bmap.c b/fs/nilfs2/bmap.c
index 08834df..f4a14ea 100644
--- a/fs/nilfs2/bmap.c
+++ b/fs/nilfs2/bmap.c
@@ -402,19 +402,11 @@
 void nilfs_bmap_add_blocks(const struct nilfs_bmap *bmap, int n)
 {
 	inode_add_bytes(bmap->b_inode, (1 << bmap->b_inode->i_blkbits) * n);
-	if (NILFS_MDT(bmap->b_inode))
-		nilfs_mdt_mark_dirty(bmap->b_inode);
-	else
-		mark_inode_dirty(bmap->b_inode);
 }
 
 void nilfs_bmap_sub_blocks(const struct nilfs_bmap *bmap, int n)
 {
 	inode_sub_bytes(bmap->b_inode, (1 << bmap->b_inode->i_blkbits) * n);
-	if (NILFS_MDT(bmap->b_inode))
-		nilfs_mdt_mark_dirty(bmap->b_inode);
-	else
-		mark_inode_dirty(bmap->b_inode);
 }
 
 __u64 nilfs_bmap_data_get_key(const struct nilfs_bmap *bmap,
diff --git a/fs/nilfs2/btnode.c b/fs/nilfs2/btnode.c
index 84c2538..471e269 100644
--- a/fs/nilfs2/btnode.c
+++ b/fs/nilfs2/btnode.c
@@ -68,9 +68,34 @@
 	truncate_inode_pages(btnc, 0);
 }
 
+struct buffer_head *
+nilfs_btnode_create_block(struct address_space *btnc, __u64 blocknr)
+{
+	struct inode *inode = NILFS_BTNC_I(btnc);
+	struct buffer_head *bh;
+
+	bh = nilfs_grab_buffer(inode, btnc, blocknr, 1 << BH_NILFS_Node);
+	if (unlikely(!bh))
+		return NULL;
+
+	if (unlikely(buffer_mapped(bh) || buffer_uptodate(bh) ||
+		     buffer_dirty(bh))) {
+		brelse(bh);
+		BUG();
+	}
+	memset(bh->b_data, 0, 1 << inode->i_blkbits);
+	bh->b_bdev = NILFS_I_NILFS(inode)->ns_bdev;
+	bh->b_blocknr = blocknr;
+	set_buffer_mapped(bh);
+	set_buffer_uptodate(bh);
+
+	unlock_page(bh->b_page);
+	page_cache_release(bh->b_page);
+	return bh;
+}
+
 int nilfs_btnode_submit_block(struct address_space *btnc, __u64 blocknr,
-			      sector_t pblocknr, struct buffer_head **pbh,
-			      int newblk)
+			      sector_t pblocknr, struct buffer_head **pbh)
 {
 	struct buffer_head *bh;
 	struct inode *inode = NILFS_BTNC_I(btnc);
@@ -81,19 +106,6 @@
 		return -ENOMEM;
 
 	err = -EEXIST; /* internal code */
-	if (newblk) {
-		if (unlikely(buffer_mapped(bh) || buffer_uptodate(bh) ||
-			     buffer_dirty(bh))) {
-			brelse(bh);
-			BUG();
-		}
-		memset(bh->b_data, 0, 1 << inode->i_blkbits);
-		bh->b_bdev = NILFS_I_NILFS(inode)->ns_bdev;
-		bh->b_blocknr = blocknr;
-		set_buffer_mapped(bh);
-		set_buffer_uptodate(bh);
-		goto found;
-	}
 
 	if (buffer_uptodate(bh) || buffer_dirty(bh))
 		goto found;
@@ -135,27 +147,6 @@
 	return err;
 }
 
-int nilfs_btnode_get(struct address_space *btnc, __u64 blocknr,
-		     sector_t pblocknr, struct buffer_head **pbh, int newblk)
-{
-	struct buffer_head *bh;
-	int err;
-
-	err = nilfs_btnode_submit_block(btnc, blocknr, pblocknr, pbh, newblk);
-	if (err == -EEXIST) /* internal code (cache hit) */
-		return 0;
-	if (unlikely(err))
-		return err;
-
-	bh = *pbh;
-	wait_on_buffer(bh);
-	if (!buffer_uptodate(bh)) {
-		brelse(bh);
-		return -EIO;
-	}
-	return 0;
-}
-
 /**
  * nilfs_btnode_delete - delete B-tree node buffer
  * @bh: buffer to be deleted
@@ -244,12 +235,13 @@
 		unlock_page(obh->b_page);
 	}
 
-	err = nilfs_btnode_get(btnc, newkey, 0, &nbh, 1);
-	if (likely(!err)) {
-		BUG_ON(nbh == obh);
-		ctxt->newbh = nbh;
-	}
-	return err;
+	nbh = nilfs_btnode_create_block(btnc, newkey);
+	if (!nbh)
+		return -ENOMEM;
+
+	BUG_ON(nbh == obh);
+	ctxt->newbh = nbh;
+	return 0;
 
  failed_unlock:
 	unlock_page(obh->b_page);
diff --git a/fs/nilfs2/btnode.h b/fs/nilfs2/btnode.h
index 3e22751..07da83f 100644
--- a/fs/nilfs2/btnode.h
+++ b/fs/nilfs2/btnode.h
@@ -40,10 +40,10 @@
 void nilfs_btnode_cache_init_once(struct address_space *);
 void nilfs_btnode_cache_init(struct address_space *, struct backing_dev_info *);
 void nilfs_btnode_cache_clear(struct address_space *);
+struct buffer_head *nilfs_btnode_create_block(struct address_space *btnc,
+					      __u64 blocknr);
 int nilfs_btnode_submit_block(struct address_space *, __u64, sector_t,
-			      struct buffer_head **, int);
-int nilfs_btnode_get(struct address_space *, __u64, sector_t,
-		     struct buffer_head **, int);
+			      struct buffer_head **);
 void nilfs_btnode_delete(struct buffer_head *);
 int nilfs_btnode_prepare_change_key(struct address_space *,
 				    struct nilfs_btnode_chkey_ctxt *);
diff --git a/fs/nilfs2/btree.c b/fs/nilfs2/btree.c
index e25b507..7cdd98b 100644
--- a/fs/nilfs2/btree.c
+++ b/fs/nilfs2/btree.c
@@ -114,7 +114,18 @@
 {
 	struct address_space *btnc =
 		&NILFS_BMAP_I((struct nilfs_bmap *)btree)->i_btnode_cache;
-	return nilfs_btnode_get(btnc, ptr, 0, bhp, 0);
+	int err;
+
+	err = nilfs_btnode_submit_block(btnc, ptr, 0, bhp);
+	if (err)
+		return err == -EEXIST ? 0 : err;
+
+	wait_on_buffer(*bhp);
+	if (!buffer_uptodate(*bhp)) {
+		brelse(*bhp);
+		return -EIO;
+	}
+	return 0;
 }
 
 static int nilfs_btree_get_new_block(const struct nilfs_btree *btree,
@@ -122,12 +133,15 @@
 {
 	struct address_space *btnc =
 		&NILFS_BMAP_I((struct nilfs_bmap *)btree)->i_btnode_cache;
-	int ret;
+	struct buffer_head *bh;
 
-	ret = nilfs_btnode_get(btnc, ptr, 0, bhp, 1);
-	if (!ret)
-		set_buffer_nilfs_volatile(*bhp);
-	return ret;
+	bh = nilfs_btnode_create_block(btnc, ptr);
+	if (!bh)
+		return -ENOMEM;
+
+	set_buffer_nilfs_volatile(bh);
+	*bhp = bh;
+	return 0;
 }
 
 static inline int
@@ -444,6 +458,18 @@
 		nilfs_btree_get_nonroot_node(path, level);
 }
 
+static inline int
+nilfs_btree_bad_node(struct nilfs_btree_node *node, int level)
+{
+	if (unlikely(nilfs_btree_node_get_level(node) != level)) {
+		dump_stack();
+		printk(KERN_CRIT "NILFS: btree level mismatch: %d != %d\n",
+		       nilfs_btree_node_get_level(node), level);
+		return 1;
+	}
+	return 0;
+}
+
 static int nilfs_btree_do_lookup(const struct nilfs_btree *btree,
 				 struct nilfs_btree_path *path,
 				 __u64 key, __u64 *ptrp, int minlevel)
@@ -467,7 +493,8 @@
 		if (ret < 0)
 			return ret;
 		node = nilfs_btree_get_nonroot_node(path, level);
-		BUG_ON(level != nilfs_btree_node_get_level(node));
+		if (nilfs_btree_bad_node(node, level))
+			return -EINVAL;
 		if (!found)
 			found = nilfs_btree_node_lookup(node, key, &index);
 		else
@@ -512,7 +539,8 @@
 		if (ret < 0)
 			return ret;
 		node = nilfs_btree_get_nonroot_node(path, level);
-		BUG_ON(level != nilfs_btree_node_get_level(node));
+		if (nilfs_btree_bad_node(node, level))
+			return -EINVAL;
 		index = nilfs_btree_node_get_nchildren(node) - 1;
 		ptr = nilfs_btree_node_get_ptr(btree, node, index);
 		path[level].bp_index = index;
@@ -638,13 +666,11 @@
 {
 	if (level < nilfs_btree_height(btree) - 1) {
 		do {
-			lock_buffer(path[level].bp_bh);
 			nilfs_btree_node_set_key(
 				nilfs_btree_get_nonroot_node(path, level),
 				path[level].bp_index, key);
 			if (!buffer_dirty(path[level].bp_bh))
 				nilfs_btnode_mark_dirty(path[level].bp_bh);
-			unlock_buffer(path[level].bp_bh);
 		} while ((path[level].bp_index == 0) &&
 			 (++level < nilfs_btree_height(btree) - 1));
 	}
@@ -663,13 +689,11 @@
 	struct nilfs_btree_node *node;
 
 	if (level < nilfs_btree_height(btree) - 1) {
-		lock_buffer(path[level].bp_bh);
 		node = nilfs_btree_get_nonroot_node(path, level);
 		nilfs_btree_node_insert(btree, node, *keyp, *ptrp,
 					path[level].bp_index);
 		if (!buffer_dirty(path[level].bp_bh))
 			nilfs_btnode_mark_dirty(path[level].bp_bh);
-		unlock_buffer(path[level].bp_bh);
 
 		if (path[level].bp_index == 0)
 			nilfs_btree_promote_key(btree, path, level + 1,
@@ -689,9 +713,6 @@
 	struct nilfs_btree_node *node, *left;
 	int nchildren, lnchildren, n, move;
 
-	lock_buffer(path[level].bp_bh);
-	lock_buffer(path[level].bp_sib_bh);
-
 	node = nilfs_btree_get_nonroot_node(path, level);
 	left = nilfs_btree_get_sib_node(path, level);
 	nchildren = nilfs_btree_node_get_nchildren(node);
@@ -712,9 +733,6 @@
 	if (!buffer_dirty(path[level].bp_sib_bh))
 		nilfs_btnode_mark_dirty(path[level].bp_sib_bh);
 
-	unlock_buffer(path[level].bp_bh);
-	unlock_buffer(path[level].bp_sib_bh);
-
 	nilfs_btree_promote_key(btree, path, level + 1,
 				nilfs_btree_node_get_key(node, 0));
 
@@ -740,9 +758,6 @@
 	struct nilfs_btree_node *node, *right;
 	int nchildren, rnchildren, n, move;
 
-	lock_buffer(path[level].bp_bh);
-	lock_buffer(path[level].bp_sib_bh);
-
 	node = nilfs_btree_get_nonroot_node(path, level);
 	right = nilfs_btree_get_sib_node(path, level);
 	nchildren = nilfs_btree_node_get_nchildren(node);
@@ -763,9 +778,6 @@
 	if (!buffer_dirty(path[level].bp_sib_bh))
 		nilfs_btnode_mark_dirty(path[level].bp_sib_bh);
 
-	unlock_buffer(path[level].bp_bh);
-	unlock_buffer(path[level].bp_sib_bh);
-
 	path[level + 1].bp_index++;
 	nilfs_btree_promote_key(btree, path, level + 1,
 				nilfs_btree_node_get_key(right, 0));
@@ -794,9 +806,6 @@
 	__u64 newptr;
 	int nchildren, n, move;
 
-	lock_buffer(path[level].bp_bh);
-	lock_buffer(path[level].bp_sib_bh);
-
 	node = nilfs_btree_get_nonroot_node(path, level);
 	right = nilfs_btree_get_sib_node(path, level);
 	nchildren = nilfs_btree_node_get_nchildren(node);
@@ -815,9 +824,6 @@
 	if (!buffer_dirty(path[level].bp_sib_bh))
 		nilfs_btnode_mark_dirty(path[level].bp_sib_bh);
 
-	unlock_buffer(path[level].bp_bh);
-	unlock_buffer(path[level].bp_sib_bh);
-
 	newkey = nilfs_btree_node_get_key(right, 0);
 	newptr = path[level].bp_newreq.bpr_ptr;
 
@@ -852,8 +858,6 @@
 	struct nilfs_btree_node *root, *child;
 	int n;
 
-	lock_buffer(path[level].bp_sib_bh);
-
 	root = nilfs_btree_get_root(btree);
 	child = nilfs_btree_get_sib_node(path, level);
 
@@ -865,8 +869,6 @@
 	if (!buffer_dirty(path[level].bp_sib_bh))
 		nilfs_btnode_mark_dirty(path[level].bp_sib_bh);
 
-	unlock_buffer(path[level].bp_sib_bh);
-
 	path[level].bp_bh = path[level].bp_sib_bh;
 	path[level].bp_sib_bh = NULL;
 
@@ -1023,11 +1025,9 @@
 
 		stats->bs_nblocks++;
 
-		lock_buffer(bh);
 		nilfs_btree_node_init(btree,
 				      (struct nilfs_btree_node *)bh->b_data,
 				      0, level, 0, NULL, NULL);
-		unlock_buffer(bh);
 		path[level].bp_sib_bh = bh;
 		path[level].bp_op = nilfs_btree_split;
 	}
@@ -1052,10 +1052,8 @@
 	if (ret < 0)
 		goto err_out_curr_node;
 
-	lock_buffer(bh);
 	nilfs_btree_node_init(btree, (struct nilfs_btree_node *)bh->b_data,
 			      0, level, 0, NULL, NULL);
-	unlock_buffer(bh);
 	path[level].bp_sib_bh = bh;
 	path[level].bp_op = nilfs_btree_grow;
 
@@ -1154,13 +1152,11 @@
 	struct nilfs_btree_node *node;
 
 	if (level < nilfs_btree_height(btree) - 1) {
-		lock_buffer(path[level].bp_bh);
 		node = nilfs_btree_get_nonroot_node(path, level);
 		nilfs_btree_node_delete(btree, node, keyp, ptrp,
 					path[level].bp_index);
 		if (!buffer_dirty(path[level].bp_bh))
 			nilfs_btnode_mark_dirty(path[level].bp_bh);
-		unlock_buffer(path[level].bp_bh);
 		if (path[level].bp_index == 0)
 			nilfs_btree_promote_key(btree, path, level + 1,
 				nilfs_btree_node_get_key(node, 0));
@@ -1180,9 +1176,6 @@
 
 	nilfs_btree_do_delete(btree, path, level, keyp, ptrp);
 
-	lock_buffer(path[level].bp_bh);
-	lock_buffer(path[level].bp_sib_bh);
-
 	node = nilfs_btree_get_nonroot_node(path, level);
 	left = nilfs_btree_get_sib_node(path, level);
 	nchildren = nilfs_btree_node_get_nchildren(node);
@@ -1197,9 +1190,6 @@
 	if (!buffer_dirty(path[level].bp_sib_bh))
 		nilfs_btnode_mark_dirty(path[level].bp_sib_bh);
 
-	unlock_buffer(path[level].bp_bh);
-	unlock_buffer(path[level].bp_sib_bh);
-
 	nilfs_btree_promote_key(btree, path, level + 1,
 				nilfs_btree_node_get_key(node, 0));
 
@@ -1217,9 +1207,6 @@
 
 	nilfs_btree_do_delete(btree, path, level, keyp, ptrp);
 
-	lock_buffer(path[level].bp_bh);
-	lock_buffer(path[level].bp_sib_bh);
-
 	node = nilfs_btree_get_nonroot_node(path, level);
 	right = nilfs_btree_get_sib_node(path, level);
 	nchildren = nilfs_btree_node_get_nchildren(node);
@@ -1234,9 +1221,6 @@
 	if (!buffer_dirty(path[level].bp_sib_bh))
 		nilfs_btnode_mark_dirty(path[level].bp_sib_bh);
 
-	unlock_buffer(path[level].bp_bh);
-	unlock_buffer(path[level].bp_sib_bh);
-
 	path[level + 1].bp_index++;
 	nilfs_btree_promote_key(btree, path, level + 1,
 				nilfs_btree_node_get_key(right, 0));
@@ -1255,9 +1239,6 @@
 
 	nilfs_btree_do_delete(btree, path, level, keyp, ptrp);
 
-	lock_buffer(path[level].bp_bh);
-	lock_buffer(path[level].bp_sib_bh);
-
 	node = nilfs_btree_get_nonroot_node(path, level);
 	left = nilfs_btree_get_sib_node(path, level);
 
@@ -1268,9 +1249,6 @@
 	if (!buffer_dirty(path[level].bp_sib_bh))
 		nilfs_btnode_mark_dirty(path[level].bp_sib_bh);
 
-	unlock_buffer(path[level].bp_bh);
-	unlock_buffer(path[level].bp_sib_bh);
-
 	nilfs_btnode_delete(path[level].bp_bh);
 	path[level].bp_bh = path[level].bp_sib_bh;
 	path[level].bp_sib_bh = NULL;
@@ -1286,9 +1264,6 @@
 
 	nilfs_btree_do_delete(btree, path, level, keyp, ptrp);
 
-	lock_buffer(path[level].bp_bh);
-	lock_buffer(path[level].bp_sib_bh);
-
 	node = nilfs_btree_get_nonroot_node(path, level);
 	right = nilfs_btree_get_sib_node(path, level);
 
@@ -1299,9 +1274,6 @@
 	if (!buffer_dirty(path[level].bp_bh))
 		nilfs_btnode_mark_dirty(path[level].bp_bh);
 
-	unlock_buffer(path[level].bp_bh);
-	unlock_buffer(path[level].bp_sib_bh);
-
 	nilfs_btnode_delete(path[level].bp_sib_bh);
 	path[level].bp_sib_bh = NULL;
 	path[level + 1].bp_index++;
@@ -1316,7 +1288,6 @@
 
 	nilfs_btree_do_delete(btree, path, level, keyp, ptrp);
 
-	lock_buffer(path[level].bp_bh);
 	root = nilfs_btree_get_root(btree);
 	child = nilfs_btree_get_nonroot_node(path, level);
 
@@ -1324,7 +1295,6 @@
 	nilfs_btree_node_set_level(root, level);
 	n = nilfs_btree_node_get_nchildren(child);
 	nilfs_btree_node_move_left(btree, root, child, n);
-	unlock_buffer(path[level].bp_bh);
 
 	nilfs_btnode_delete(path[level].bp_bh);
 	path[level].bp_bh = NULL;
@@ -1699,7 +1669,6 @@
 		nilfs_bmap_commit_alloc_ptr(bmap, nreq, dat);
 
 		/* create child node at level 1 */
-		lock_buffer(bh);
 		node = (struct nilfs_btree_node *)bh->b_data;
 		nilfs_btree_node_init(btree, node, 0, 1, n, keys, ptrs);
 		nilfs_btree_node_insert(btree, node,
@@ -1709,7 +1678,6 @@
 		if (!nilfs_bmap_dirty(bmap))
 			nilfs_bmap_set_dirty(bmap);
 
-		unlock_buffer(bh);
 		brelse(bh);
 
 		/* create root node at level 2 */
@@ -2050,7 +2018,7 @@
 	for (level = NILFS_BTREE_LEVEL_NODE_MIN;
 	     level < NILFS_BTREE_LEVEL_MAX;
 	     level++)
-		list_splice(&lists[level], listp->prev);
+		list_splice_tail(&lists[level], listp);
 }
 
 static int nilfs_btree_assign_p(struct nilfs_btree *btree,
diff --git a/fs/nilfs2/btree.h b/fs/nilfs2/btree.h
index 0e72bbb..4b82d84 100644
--- a/fs/nilfs2/btree.h
+++ b/fs/nilfs2/btree.h
@@ -34,28 +34,6 @@
 struct nilfs_btree_path;
 
 /**
- * struct nilfs_btree_node - B-tree node
- * @bn_flags: flags
- * @bn_level: level
- * @bn_nchildren: number of children
- * @bn_pad: padding
- */
-struct nilfs_btree_node {
-	__u8 bn_flags;
-	__u8 bn_level;
-	__le16 bn_nchildren;
-	__le32 bn_pad;
-};
-
-/* flags */
-#define NILFS_BTREE_NODE_ROOT	0x01
-
-/* level */
-#define NILFS_BTREE_LEVEL_DATA		0
-#define NILFS_BTREE_LEVEL_NODE_MIN	(NILFS_BTREE_LEVEL_DATA + 1)
-#define NILFS_BTREE_LEVEL_MAX		14
-
-/**
  * struct nilfs_btree - B-tree structure
  * @bt_bmap: bmap base structure
  */
diff --git a/fs/nilfs2/cpfile.c b/fs/nilfs2/cpfile.c
index 3f5d5d0..d5ad54e 100644
--- a/fs/nilfs2/cpfile.c
+++ b/fs/nilfs2/cpfile.c
@@ -926,3 +926,29 @@
 	up_read(&NILFS_MDT(cpfile)->mi_sem);
 	return ret;
 }
+
+/**
+ * nilfs_cpfile_read - read cpfile inode
+ * @cpfile: cpfile inode
+ * @raw_inode: on-disk cpfile inode
+ */
+int nilfs_cpfile_read(struct inode *cpfile, struct nilfs_inode *raw_inode)
+{
+	return nilfs_read_inode_common(cpfile, raw_inode);
+}
+
+/**
+ * nilfs_cpfile_new - create cpfile
+ * @nilfs: nilfs object
+ * @cpsize: size of a checkpoint entry
+ */
+struct inode *nilfs_cpfile_new(struct the_nilfs *nilfs, size_t cpsize)
+{
+	struct inode *cpfile;
+
+	cpfile = nilfs_mdt_new(nilfs, NULL, NILFS_CPFILE_INO, 0);
+	if (cpfile)
+		nilfs_mdt_set_entry_size(cpfile, cpsize,
+					 sizeof(struct nilfs_cpfile_header));
+	return cpfile;
+}
diff --git a/fs/nilfs2/cpfile.h b/fs/nilfs2/cpfile.h
index debea89..bc0809e 100644
--- a/fs/nilfs2/cpfile.h
+++ b/fs/nilfs2/cpfile.h
@@ -40,4 +40,7 @@
 ssize_t nilfs_cpfile_get_cpinfo(struct inode *, __u64 *, int, void *, unsigned,
 				size_t);
 
+int nilfs_cpfile_read(struct inode *cpfile, struct nilfs_inode *raw_inode);
+struct inode *nilfs_cpfile_new(struct the_nilfs *nilfs, size_t cpsize);
+
 #endif	/* _NILFS_CPFILE_H */
diff --git a/fs/nilfs2/dat.c b/fs/nilfs2/dat.c
index 1ff8e15..187dd07 100644
--- a/fs/nilfs2/dat.c
+++ b/fs/nilfs2/dat.c
@@ -33,6 +33,16 @@
 #define NILFS_CNO_MIN	((__u64)1)
 #define NILFS_CNO_MAX	(~(__u64)0)
 
+struct nilfs_dat_info {
+	struct nilfs_mdt_info mi;
+	struct nilfs_palloc_cache palloc_cache;
+};
+
+static inline struct nilfs_dat_info *NILFS_DAT_I(struct inode *dat)
+{
+	return (struct nilfs_dat_info *)NILFS_MDT(dat);
+}
+
 static int nilfs_dat_prepare_entry(struct inode *dat,
 				   struct nilfs_palloc_req *req, int create)
 {
@@ -425,3 +435,40 @@
 
 	return nvi;
 }
+
+/**
+ * nilfs_dat_read - read dat inode
+ * @dat: dat inode
+ * @raw_inode: on-disk dat inode
+ */
+int nilfs_dat_read(struct inode *dat, struct nilfs_inode *raw_inode)
+{
+	return nilfs_read_inode_common(dat, raw_inode);
+}
+
+/**
+ * nilfs_dat_new - create dat file
+ * @nilfs: nilfs object
+ * @entry_size: size of a dat entry
+ */
+struct inode *nilfs_dat_new(struct the_nilfs *nilfs, size_t entry_size)
+{
+	static struct lock_class_key dat_lock_key;
+	struct inode *dat;
+	struct nilfs_dat_info *di;
+	int err;
+
+	dat = nilfs_mdt_new(nilfs, NULL, NILFS_DAT_INO, sizeof(*di));
+	if (dat) {
+		err = nilfs_palloc_init_blockgroup(dat, entry_size);
+		if (unlikely(err)) {
+			nilfs_mdt_destroy(dat);
+			return NULL;
+		}
+
+		di = NILFS_DAT_I(dat);
+		lockdep_set_class(&di->mi.mi_sem, &dat_lock_key);
+		nilfs_palloc_setup_cache(dat, &di->palloc_cache);
+	}
+	return dat;
+}
diff --git a/fs/nilfs2/dat.h b/fs/nilfs2/dat.h
index 406070d..d31c3aa 100644
--- a/fs/nilfs2/dat.h
+++ b/fs/nilfs2/dat.h
@@ -53,4 +53,7 @@
 int nilfs_dat_move(struct inode *, __u64, sector_t);
 ssize_t nilfs_dat_get_vinfo(struct inode *, void *, unsigned, size_t);
 
+int nilfs_dat_read(struct inode *dat, struct nilfs_inode *raw_inode);
+struct inode *nilfs_dat_new(struct the_nilfs *nilfs, size_t entry_size);
+
 #endif	/* _NILFS_DAT_H */
diff --git a/fs/nilfs2/dir.c b/fs/nilfs2/dir.c
index e097099..76d803e 100644
--- a/fs/nilfs2/dir.c
+++ b/fs/nilfs2/dir.c
@@ -99,9 +99,9 @@
 				 NULL, nilfs_get_block);
 }
 
-static int nilfs_commit_chunk(struct page *page,
-			      struct address_space *mapping,
-			      unsigned from, unsigned to)
+static void nilfs_commit_chunk(struct page *page,
+			       struct address_space *mapping,
+			       unsigned from, unsigned to)
 {
 	struct inode *dir = mapping->host;
 	struct nilfs_sb_info *sbi = NILFS_SB(dir->i_sb);
@@ -112,15 +112,13 @@
 
 	nr_dirty = nilfs_page_count_clean_buffers(page, from, to);
 	copied = block_write_end(NULL, mapping, pos, len, len, page, NULL);
-	if (pos + copied > dir->i_size) {
+	if (pos + copied > dir->i_size)
 		i_size_write(dir, pos + copied);
-		mark_inode_dirty(dir);
-	}
 	if (IS_DIRSYNC(dir))
 		nilfs_set_transaction_flag(NILFS_TI_SYNC);
 	err = nilfs_set_file_dirty(sbi, dir, nr_dirty);
+	WARN_ON(err); /* do not happen */
 	unlock_page(page);
-	return err;
 }
 
 static void nilfs_check_page(struct page *page)
@@ -455,11 +453,10 @@
 	BUG_ON(err);
 	de->inode = cpu_to_le64(inode->i_ino);
 	nilfs_set_de_type(de, inode);
-	err = nilfs_commit_chunk(page, mapping, from, to);
+	nilfs_commit_chunk(page, mapping, from, to);
 	nilfs_put_page(page);
 	dir->i_mtime = dir->i_ctime = CURRENT_TIME;
 /*	NILFS_I(dir)->i_flags &= ~NILFS_BTREE_FL; */
-	mark_inode_dirty(dir);
 }
 
 /*
@@ -548,10 +545,10 @@
 	memcpy(de->name, name, namelen);
 	de->inode = cpu_to_le64(inode->i_ino);
 	nilfs_set_de_type(de, inode);
-	err = nilfs_commit_chunk(page, page->mapping, from, to);
+	nilfs_commit_chunk(page, page->mapping, from, to);
 	dir->i_mtime = dir->i_ctime = CURRENT_TIME;
 /*	NILFS_I(dir)->i_flags &= ~NILFS_BTREE_FL; */
-	mark_inode_dirty(dir);
+	nilfs_mark_inode_dirty(dir);
 	/* OFFSET_CACHE */
 out_put:
 	nilfs_put_page(page);
@@ -595,10 +592,9 @@
 	if (pde)
 		pde->rec_len = cpu_to_le16(to - from);
 	dir->inode = 0;
-	err = nilfs_commit_chunk(page, mapping, from, to);
+	nilfs_commit_chunk(page, mapping, from, to);
 	inode->i_ctime = inode->i_mtime = CURRENT_TIME;
 /*	NILFS_I(inode)->i_flags &= ~NILFS_BTREE_FL; */
-	mark_inode_dirty(inode);
 out:
 	nilfs_put_page(page);
 	return err;
@@ -640,7 +636,7 @@
 	memcpy(de->name, "..\0", 4);
 	nilfs_set_de_type(de, inode);
 	kunmap_atomic(kaddr, KM_USER0);
-	err = nilfs_commit_chunk(page, mapping, 0, chunk_size);
+	nilfs_commit_chunk(page, mapping, 0, chunk_size);
 fail:
 	page_cache_release(page);
 	return err;
diff --git a/fs/nilfs2/gcdat.c b/fs/nilfs2/gcdat.c
index 93383c5..dd5f7e0 100644
--- a/fs/nilfs2/gcdat.c
+++ b/fs/nilfs2/gcdat.c
@@ -61,6 +61,8 @@
 
 	nilfs_bmap_commit_gcdat(gii->i_bmap, dii->i_bmap);
 
+	nilfs_palloc_clear_cache(dat);
+	nilfs_palloc_clear_cache(gcdat);
 	nilfs_clear_dirty_pages(mapping);
 	nilfs_copy_back_pages(mapping, gmapping);
 	/* note: mdt dirty flags should be cleared by segctor. */
@@ -79,6 +81,7 @@
 	gcdat->i_state = I_CLEAR;
 	gii->i_flags = 0;
 
+	nilfs_palloc_clear_cache(gcdat);
 	truncate_inode_pages(gcdat->i_mapping, 0);
 	truncate_inode_pages(&gii->i_btnode_cache, 0);
 }
diff --git a/fs/nilfs2/gcinode.c b/fs/nilfs2/gcinode.c
index e6de0a2..e16a666 100644
--- a/fs/nilfs2/gcinode.c
+++ b/fs/nilfs2/gcinode.c
@@ -149,7 +149,7 @@
 				   __u64 vbn, struct buffer_head **out_bh)
 {
 	int ret = nilfs_btnode_submit_block(&NILFS_I(inode)->i_btnode_cache,
-					    vbn ? : pbn, pbn, out_bh, 0);
+					    vbn ? : pbn, pbn, out_bh);
 	if (ret == -EEXIST) /* internal code (cache hit) */
 		ret = 0;
 	return ret;
@@ -212,9 +212,10 @@
 static struct inode *alloc_gcinode(struct the_nilfs *nilfs, ino_t ino,
 				   __u64 cno)
 {
-	struct inode *inode = nilfs_mdt_new_common(nilfs, NULL, ino, GFP_NOFS);
+	struct inode *inode;
 	struct nilfs_inode_info *ii;
 
+	inode = nilfs_mdt_new_common(nilfs, NULL, ino, GFP_NOFS, 0);
 	if (!inode)
 		return NULL;
 
@@ -265,7 +266,6 @@
  */
 void nilfs_clear_gcinode(struct inode *inode)
 {
-	nilfs_mdt_clear(inode);
 	nilfs_mdt_destroy(inode);
 }
 
diff --git a/fs/nilfs2/ifile.c b/fs/nilfs2/ifile.c
index de86401..922d9dd 100644
--- a/fs/nilfs2/ifile.c
+++ b/fs/nilfs2/ifile.c
@@ -29,6 +29,17 @@
 #include "alloc.h"
 #include "ifile.h"
 
+
+struct nilfs_ifile_info {
+	struct nilfs_mdt_info mi;
+	struct nilfs_palloc_cache palloc_cache;
+};
+
+static inline struct nilfs_ifile_info *NILFS_IFILE_I(struct inode *ifile)
+{
+	return (struct nilfs_ifile_info *)NILFS_MDT(ifile);
+}
+
 /**
  * nilfs_ifile_create_inode - create a new disk inode
  * @ifile: ifile inode
@@ -148,3 +159,27 @@
 	}
 	return err;
 }
+
+/**
+ * nilfs_ifile_new - create inode file
+ * @sbi: nilfs_sb_info struct
+ * @inode_size: size of an inode
+ */
+struct inode *nilfs_ifile_new(struct nilfs_sb_info *sbi, size_t inode_size)
+{
+	struct inode *ifile;
+	int err;
+
+	ifile = nilfs_mdt_new(sbi->s_nilfs, sbi->s_super, NILFS_IFILE_INO,
+			      sizeof(struct nilfs_ifile_info));
+	if (ifile) {
+		err = nilfs_palloc_init_blockgroup(ifile, inode_size);
+		if (unlikely(err)) {
+			nilfs_mdt_destroy(ifile);
+			return NULL;
+		}
+		nilfs_palloc_setup_cache(ifile,
+					 &NILFS_IFILE_I(ifile)->palloc_cache);
+	}
+	return ifile;
+}
diff --git a/fs/nilfs2/ifile.h b/fs/nilfs2/ifile.h
index ecc3ba76..cbca32e 100644
--- a/fs/nilfs2/ifile.h
+++ b/fs/nilfs2/ifile.h
@@ -49,4 +49,6 @@
 int nilfs_ifile_delete_inode(struct inode *, ino_t);
 int nilfs_ifile_get_inode_block(struct inode *, ino_t, struct buffer_head **);
 
+struct inode *nilfs_ifile_new(struct nilfs_sb_info *sbi, size_t inode_size);
+
 #endif	/* _NILFS_IFILE_H */
diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c
index 2a0a5a3..7868cc1 100644
--- a/fs/nilfs2/inode.c
+++ b/fs/nilfs2/inode.c
@@ -97,6 +97,7 @@
 			nilfs_transaction_abort(inode->i_sb);
 			goto out;
 		}
+		nilfs_mark_inode_dirty(inode);
 		nilfs_transaction_commit(inode->i_sb); /* never fails */
 		/* Error handling should be detailed */
 		set_buffer_new(bh_result);
@@ -322,7 +323,6 @@
 				    nilfs_init_acl(), proper cancellation of
 				    above jobs should be considered */
 
-	mark_inode_dirty(inode);
 	return inode;
 
  failed_acl:
@@ -525,7 +525,6 @@
 
 	raw_inode = nilfs_ifile_map_inode(sbi->s_ifile, ino, ibh);
 
-	/* The buffer is guarded with lock_buffer() by the caller */
 	if (test_and_clear_bit(NILFS_I_NEW, &ii->i_state))
 		memset(raw_inode, 0, NILFS_MDT(sbi->s_ifile)->mi_entry_size);
 	set_bit(NILFS_I_INODE_DIRTY, &ii->i_state);
@@ -599,6 +598,7 @@
 	if (IS_SYNC(inode))
 		nilfs_set_transaction_flag(NILFS_TI_SYNC);
 
+	nilfs_mark_inode_dirty(inode);
 	nilfs_set_file_dirty(NILFS_SB(sb), inode, 0);
 	nilfs_transaction_commit(sb);
 	/* May construct a logical segment and may fail in sync mode.
@@ -623,6 +623,7 @@
 		truncate_inode_pages(&inode->i_data, 0);
 
 	nilfs_truncate_bmap(ii, 0);
+	nilfs_mark_inode_dirty(inode);
 	nilfs_free_inode(inode);
 	/* nilfs_free_inode() marks inode buffer dirty */
 	if (IS_SYNC(inode))
@@ -745,9 +746,7 @@
 			      "failed to reget inode block.\n");
 		return err;
 	}
-	lock_buffer(ibh);
 	nilfs_update_inode(inode, ibh);
-	unlock_buffer(ibh);
 	nilfs_mdt_mark_buffer_dirty(ibh);
 	nilfs_mdt_mark_dirty(sbi->s_ifile);
 	brelse(ibh);
diff --git a/fs/nilfs2/mdt.c b/fs/nilfs2/mdt.c
index f632611..06713ff 100644
--- a/fs/nilfs2/mdt.c
+++ b/fs/nilfs2/mdt.c
@@ -186,7 +186,7 @@
 }
 
 static int nilfs_mdt_read_block(struct inode *inode, unsigned long block,
-				struct buffer_head **out_bh)
+				int readahead, struct buffer_head **out_bh)
 {
 	struct buffer_head *first_bh, *bh;
 	unsigned long blkoff;
@@ -200,16 +200,18 @@
 	if (unlikely(err))
 		goto failed;
 
-	blkoff = block + 1;
-	for (i = 0; i < nr_ra_blocks; i++, blkoff++) {
-		err = nilfs_mdt_submit_block(inode, blkoff, READA, &bh);
-		if (likely(!err || err == -EEXIST))
-			brelse(bh);
-		else if (err != -EBUSY)
-			break; /* abort readahead if bmap lookup failed */
-
-		if (!buffer_locked(first_bh))
-			goto out_no_wait;
+	if (readahead) {
+		blkoff = block + 1;
+		for (i = 0; i < nr_ra_blocks; i++, blkoff++) {
+			err = nilfs_mdt_submit_block(inode, blkoff, READA, &bh);
+			if (likely(!err || err == -EEXIST))
+				brelse(bh);
+			else if (err != -EBUSY)
+				break;
+				/* abort readahead if bmap lookup failed */
+			if (!buffer_locked(first_bh))
+				goto out_no_wait;
+		}
 	}
 
 	wait_on_buffer(first_bh);
@@ -263,7 +265,7 @@
 
 	/* Should be rewritten with merging nilfs_mdt_read_block() */
  retry:
-	ret = nilfs_mdt_read_block(inode, blkoff, out_bh);
+	ret = nilfs_mdt_read_block(inode, blkoff, !create, out_bh);
 	if (!create || ret != -ENOENT)
 		return ret;
 
@@ -371,7 +373,7 @@
 	struct buffer_head *bh;
 	int err;
 
-	err = nilfs_mdt_read_block(inode, block, &bh);
+	err = nilfs_mdt_read_block(inode, block, 0, &bh);
 	if (unlikely(err))
 		return err;
 	nilfs_mark_buffer_dirty(bh);
@@ -445,9 +447,17 @@
  * longer than those of the super block structs; they may continue for
  * several consecutive mounts/umounts.  This would need discussions.
  */
+/**
+ * nilfs_mdt_new_common - allocate a pseudo inode for metadata file
+ * @nilfs: nilfs object
+ * @sb: super block instance the metadata file belongs to
+ * @ino: inode number
+ * @gfp_mask: gfp mask for data pages
+ * @objsz: size of the private object attached to inode->i_private
+ */
 struct inode *
 nilfs_mdt_new_common(struct the_nilfs *nilfs, struct super_block *sb,
-		     ino_t ino, gfp_t gfp_mask)
+		     ino_t ino, gfp_t gfp_mask, size_t objsz)
 {
 	struct inode *inode = nilfs_alloc_inode_common(nilfs);
 
@@ -455,8 +465,9 @@
 		return NULL;
 	else {
 		struct address_space * const mapping = &inode->i_data;
-		struct nilfs_mdt_info *mi = kzalloc(sizeof(*mi), GFP_NOFS);
+		struct nilfs_mdt_info *mi;
 
+		mi = kzalloc(max(sizeof(*mi), objsz), GFP_NOFS);
 		if (!mi) {
 			nilfs_destroy_inode(inode);
 			return NULL;
@@ -513,11 +524,11 @@
 }
 
 struct inode *nilfs_mdt_new(struct the_nilfs *nilfs, struct super_block *sb,
-			    ino_t ino)
+			    ino_t ino, size_t objsz)
 {
-	struct inode *inode = nilfs_mdt_new_common(nilfs, sb, ino,
-						   NILFS_MDT_GFP);
+	struct inode *inode;
 
+	inode = nilfs_mdt_new_common(nilfs, sb, ino, NILFS_MDT_GFP, objsz);
 	if (!inode)
 		return NULL;
 
@@ -544,14 +555,15 @@
 		&NILFS_I(orig)->i_btnode_cache;
 }
 
-void nilfs_mdt_clear(struct inode *inode)
+static void nilfs_mdt_clear(struct inode *inode)
 {
 	struct nilfs_inode_info *ii = NILFS_I(inode);
 
 	invalidate_mapping_pages(inode->i_mapping, 0, -1);
 	truncate_inode_pages(inode->i_mapping, 0);
 
-	nilfs_bmap_clear(ii->i_bmap);
+	if (test_bit(NILFS_I_BMAP, &ii->i_state))
+		nilfs_bmap_clear(ii->i_bmap);
 	nilfs_btnode_cache_clear(&ii->i_btnode_cache);
 }
 
@@ -559,6 +571,10 @@
 {
 	struct nilfs_mdt_info *mdi = NILFS_MDT(inode);
 
+	if (mdi->mi_palloc_cache)
+		nilfs_palloc_destroy_cache(inode);
+	nilfs_mdt_clear(inode);
+
 	kfree(mdi->mi_bgl); /* kfree(NULL) is safe */
 	kfree(mdi);
 	nilfs_destroy_inode(inode);
diff --git a/fs/nilfs2/mdt.h b/fs/nilfs2/mdt.h
index 4315997..6c4bbb0 100644
--- a/fs/nilfs2/mdt.h
+++ b/fs/nilfs2/mdt.h
@@ -36,6 +36,7 @@
  * @mi_entry_size: size of an entry
  * @mi_first_entry_offset: offset to the first entry
  * @mi_entries_per_block: number of entries in a block
+ * @mi_palloc_cache: persistent object allocator cache
  * @mi_blocks_per_group: number of blocks in a group
  * @mi_blocks_per_desc_block: number of blocks per descriptor block
  */
@@ -46,6 +47,7 @@
 	unsigned		mi_entry_size;
 	unsigned		mi_first_entry_offset;
 	unsigned long		mi_entries_per_block;
+	struct nilfs_palloc_cache *mi_palloc_cache;
 	unsigned long		mi_blocks_per_group;
 	unsigned long		mi_blocks_per_desc_block;
 };
@@ -74,11 +76,11 @@
 int nilfs_mdt_mark_block_dirty(struct inode *, unsigned long);
 int nilfs_mdt_fetch_dirty(struct inode *);
 
-struct inode *nilfs_mdt_new(struct the_nilfs *, struct super_block *, ino_t);
+struct inode *nilfs_mdt_new(struct the_nilfs *, struct super_block *, ino_t,
+			    size_t);
 struct inode *nilfs_mdt_new_common(struct the_nilfs *, struct super_block *,
-				   ino_t, gfp_t);
+				   ino_t, gfp_t, size_t);
 void nilfs_mdt_destroy(struct inode *);
-void nilfs_mdt_clear(struct inode *);
 void nilfs_mdt_set_entry_size(struct inode *, unsigned, unsigned);
 void nilfs_mdt_set_shadow(struct inode *, struct inode *);
 
@@ -104,21 +106,4 @@
 #define nilfs_mdt_bgl_lock(inode, bg) \
 	(&NILFS_MDT(inode)->mi_bgl->locks[(bg) & (NR_BG_LOCKS-1)].lock)
 
-
-static inline int
-nilfs_mdt_read_inode_direct(struct inode *inode, struct buffer_head *bh,
-			    unsigned n)
-{
-	return nilfs_read_inode_common(
-		inode, (struct nilfs_inode *)(bh->b_data + n));
-}
-
-static inline void
-nilfs_mdt_write_inode_direct(struct inode *inode, struct buffer_head *bh,
-			     unsigned n)
-{
-	nilfs_write_inode_common(
-		inode, (struct nilfs_inode *)(bh->b_data + n), 1);
-}
-
 #endif /* _NILFS_MDT_H */
diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c
index ed02e88..07ba838 100644
--- a/fs/nilfs2/namei.c
+++ b/fs/nilfs2/namei.c
@@ -120,7 +120,7 @@
 		inode->i_op = &nilfs_file_inode_operations;
 		inode->i_fop = &nilfs_file_operations;
 		inode->i_mapping->a_ops = &nilfs_aops;
-		mark_inode_dirty(inode);
+		nilfs_mark_inode_dirty(inode);
 		err = nilfs_add_nondir(dentry, inode);
 	}
 	if (!err)
@@ -148,7 +148,7 @@
 	err = PTR_ERR(inode);
 	if (!IS_ERR(inode)) {
 		init_special_inode(inode, inode->i_mode, rdev);
-		mark_inode_dirty(inode);
+		nilfs_mark_inode_dirty(inode);
 		err = nilfs_add_nondir(dentry, inode);
 	}
 	if (!err)
@@ -188,7 +188,7 @@
 		goto out_fail;
 
 	/* mark_inode_dirty(inode); */
-	/* nilfs_new_inode() and page_symlink() do this */
+	/* page_symlink() do this */
 
 	err = nilfs_add_nondir(dentry, inode);
 out:
@@ -200,7 +200,8 @@
 	return err;
 
 out_fail:
-	inode_dec_link_count(inode);
+	drop_nlink(inode);
+	nilfs_mark_inode_dirty(inode);
 	iput(inode);
 	goto out;
 }
@@ -245,7 +246,7 @@
 	if (err)
 		return err;
 
-	inode_inc_link_count(dir);
+	inc_nlink(dir);
 
 	inode = nilfs_new_inode(dir, S_IFDIR | mode);
 	err = PTR_ERR(inode);
@@ -256,7 +257,7 @@
 	inode->i_fop = &nilfs_dir_operations;
 	inode->i_mapping->a_ops = &nilfs_aops;
 
-	inode_inc_link_count(inode);
+	inc_nlink(inode);
 
 	err = nilfs_make_empty(inode, dir);
 	if (err)
@@ -266,6 +267,7 @@
 	if (err)
 		goto out_fail;
 
+	nilfs_mark_inode_dirty(inode);
 	d_instantiate(dentry, inode);
 out:
 	if (!err)
@@ -276,26 +278,23 @@
 	return err;
 
 out_fail:
-	inode_dec_link_count(inode);
-	inode_dec_link_count(inode);
+	drop_nlink(inode);
+	drop_nlink(inode);
+	nilfs_mark_inode_dirty(inode);
 	iput(inode);
 out_dir:
-	inode_dec_link_count(dir);
+	drop_nlink(dir);
+	nilfs_mark_inode_dirty(dir);
 	goto out;
 }
 
-static int nilfs_unlink(struct inode *dir, struct dentry *dentry)
+static int nilfs_do_unlink(struct inode *dir, struct dentry *dentry)
 {
 	struct inode *inode;
 	struct nilfs_dir_entry *de;
 	struct page *page;
-	struct nilfs_transaction_info ti;
 	int err;
 
-	err = nilfs_transaction_begin(dir->i_sb, &ti, 0);
-	if (err)
-		return err;
-
 	err = -ENOENT;
 	de = nilfs_find_entry(dir, dentry, &page);
 	if (!de)
@@ -317,12 +316,28 @@
 		goto out;
 
 	inode->i_ctime = dir->i_ctime;
-	inode_dec_link_count(inode);
+	drop_nlink(inode);
 	err = 0;
 out:
-	if (!err)
+	return err;
+}
+
+static int nilfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+	struct nilfs_transaction_info ti;
+	int err;
+
+	err = nilfs_transaction_begin(dir->i_sb, &ti, 0);
+	if (err)
+		return err;
+
+	err = nilfs_do_unlink(dir, dentry);
+
+	if (!err) {
+		nilfs_mark_inode_dirty(dir);
+		nilfs_mark_inode_dirty(dentry->d_inode);
 		err = nilfs_transaction_commit(dir->i_sb);
-	else
+	} else
 		nilfs_transaction_abort(dir->i_sb);
 
 	return err;
@@ -340,11 +355,13 @@
 
 	err = -ENOTEMPTY;
 	if (nilfs_empty_dir(inode)) {
-		err = nilfs_unlink(dir, dentry);
+		err = nilfs_do_unlink(dir, dentry);
 		if (!err) {
 			inode->i_size = 0;
-			inode_dec_link_count(inode);
-			inode_dec_link_count(dir);
+			drop_nlink(inode);
+			nilfs_mark_inode_dirty(inode);
+			drop_nlink(dir);
+			nilfs_mark_inode_dirty(dir);
 		}
 	}
 	if (!err)
@@ -395,42 +412,48 @@
 		new_de = nilfs_find_entry(new_dir, new_dentry, &new_page);
 		if (!new_de)
 			goto out_dir;
-		inode_inc_link_count(old_inode);
+		inc_nlink(old_inode);
 		nilfs_set_link(new_dir, new_de, new_page, old_inode);
+		nilfs_mark_inode_dirty(new_dir);
 		new_inode->i_ctime = CURRENT_TIME;
 		if (dir_de)
 			drop_nlink(new_inode);
-		inode_dec_link_count(new_inode);
+		drop_nlink(new_inode);
+		nilfs_mark_inode_dirty(new_inode);
 	} else {
 		if (dir_de) {
 			err = -EMLINK;
 			if (new_dir->i_nlink >= NILFS_LINK_MAX)
 				goto out_dir;
 		}
-		inode_inc_link_count(old_inode);
+		inc_nlink(old_inode);
 		err = nilfs_add_link(new_dentry, old_inode);
 		if (err) {
-			inode_dec_link_count(old_inode);
+			drop_nlink(old_inode);
+			nilfs_mark_inode_dirty(old_inode);
 			goto out_dir;
 		}
-		if (dir_de)
-			inode_inc_link_count(new_dir);
+		if (dir_de) {
+			inc_nlink(new_dir);
+			nilfs_mark_inode_dirty(new_dir);
+		}
 	}
 
 	/*
 	 * Like most other Unix systems, set the ctime for inodes on a
 	 * rename.
-	 * inode_dec_link_count() will mark the inode dirty.
 	 */
 	old_inode->i_ctime = CURRENT_TIME;
 
 	nilfs_delete_entry(old_de, old_page);
-	inode_dec_link_count(old_inode);
+	drop_nlink(old_inode);
 
 	if (dir_de) {
 		nilfs_set_link(old_inode, dir_de, dir_page, new_dir);
-		inode_dec_link_count(old_dir);
+		drop_nlink(old_dir);
 	}
+	nilfs_mark_inode_dirty(old_dir);
+	nilfs_mark_inode_dirty(old_inode);
 
 	err = nilfs_transaction_commit(old_dir->i_sb);
 	return err;
diff --git a/fs/nilfs2/recovery.c b/fs/nilfs2/recovery.c
index 6dc8359..c9c96c7 100644
--- a/fs/nilfs2/recovery.c
+++ b/fs/nilfs2/recovery.c
@@ -770,14 +770,8 @@
 		nilfs_finish_roll_forward(nilfs, sbi, ri);
 	}
 
-	nilfs_detach_checkpoint(sbi);
-	return 0;
-
  failed:
 	nilfs_detach_checkpoint(sbi);
-	nilfs_mdt_clear(nilfs->ns_cpfile);
-	nilfs_mdt_clear(nilfs->ns_sufile);
-	nilfs_mdt_clear(nilfs->ns_dat);
 	return err;
 }
 
@@ -804,6 +798,7 @@
 	struct nilfs_segsum_info ssi;
 	sector_t pseg_start, pseg_end, sr_pseg_start = 0;
 	sector_t seg_start, seg_end; /* range of full segment (block number) */
+	sector_t b, end;
 	u64 seg_seq;
 	__u64 segnum, nextnum = 0;
 	__u64 cno;
@@ -819,6 +814,11 @@
 	/* Calculate range of segment */
 	nilfs_get_segment_range(nilfs, segnum, &seg_start, &seg_end);
 
+	/* Read ahead segment */
+	b = seg_start;
+	while (b <= seg_end)
+		sb_breadahead(sbi->s_super, b++);
+
 	for (;;) {
 		/* Load segment summary */
 		ret = load_segment_summary(sbi, pseg_start, seg_seq, &ssi, 1);
@@ -841,14 +841,20 @@
 		ri->ri_nextnum = nextnum;
 		empty_seg = 0;
 
+		if (!NILFS_SEG_HAS_SR(&ssi) && !scan_newer) {
+			/* This will never happen because a superblock
+			   (last_segment) always points to a pseg
+			   having a super root. */
+			ret = NILFS_SEG_FAIL_CONSISTENCY;
+			goto failed;
+		}
+
+		if (pseg_start == seg_start) {
+			nilfs_get_segment_range(nilfs, nextnum, &b, &end);
+			while (b <= end)
+				sb_breadahead(sbi->s_super, b++);
+		}
 		if (!NILFS_SEG_HAS_SR(&ssi)) {
-			if (!scan_newer) {
-				/* This will never happen because a superblock
-				   (last_segment) always points to a pseg
-				   having a super root. */
-				ret = NILFS_SEG_FAIL_CONSISTENCY;
-				goto failed;
-			}
 			if (!ri->ri_lsegs_start && NILFS_SEG_LOGBGN(&ssi)) {
 				ri->ri_lsegs_start = pseg_start;
 				ri->ri_lsegs_start_seq = seg_seq;
@@ -919,7 +925,7 @@
 
  super_root_found:
 	/* Updating pointers relating to the latest checkpoint */
-	list_splice(&segments, ri->ri_used_segments.prev);
+	list_splice_tail(&segments, &ri->ri_used_segments);
 	nilfs->ns_last_pseg = sr_pseg_start;
 	nilfs->ns_last_seq = nilfs->ns_seg_seq;
 	nilfs->ns_last_cno = ri->ri_cno;
diff --git a/fs/nilfs2/segbuf.c b/fs/nilfs2/segbuf.c
index e6d9e37..645c786 100644
--- a/fs/nilfs2/segbuf.c
+++ b/fs/nilfs2/segbuf.c
@@ -24,10 +24,22 @@
 #include <linux/buffer_head.h>
 #include <linux/writeback.h>
 #include <linux/crc32.h>
+#include <linux/backing-dev.h>
 #include "page.h"
 #include "segbuf.h"
 
 
+struct nilfs_write_info {
+	struct the_nilfs       *nilfs;
+	struct bio	       *bio;
+	int 			start, end; /* The region to be submitted */
+	int			rest_blocks;
+	int			max_pages;
+	int			nr_vecs;
+	sector_t		blocknr;
+};
+
+
 static struct kmem_cache *nilfs_segbuf_cachep;
 
 static void nilfs_segbuf_init_once(void *obj)
@@ -63,6 +75,11 @@
 	INIT_LIST_HEAD(&segbuf->sb_list);
 	INIT_LIST_HEAD(&segbuf->sb_segsum_buffers);
 	INIT_LIST_HEAD(&segbuf->sb_payload_buffers);
+
+	init_completion(&segbuf->sb_bio_event);
+	atomic_set(&segbuf->sb_err, 0);
+	segbuf->sb_nbio = 0;
+
 	return segbuf;
 }
 
@@ -83,6 +100,22 @@
 		segbuf->sb_fseg_end - segbuf->sb_pseg_start + 1;
 }
 
+/**
+ * nilfs_segbuf_map_cont - map a new log behind a given log
+ * @segbuf: new segment buffer
+ * @prev: segment buffer containing a log to be continued
+ */
+void nilfs_segbuf_map_cont(struct nilfs_segment_buffer *segbuf,
+			   struct nilfs_segment_buffer *prev)
+{
+	segbuf->sb_segnum = prev->sb_segnum;
+	segbuf->sb_fseg_start = prev->sb_fseg_start;
+	segbuf->sb_fseg_end = prev->sb_fseg_end;
+	segbuf->sb_pseg_start = prev->sb_pseg_start + prev->sb_sum.nblocks;
+	segbuf->sb_rest_blocks =
+		segbuf->sb_fseg_end - segbuf->sb_pseg_start + 1;
+}
+
 void nilfs_segbuf_set_next_segnum(struct nilfs_segment_buffer *segbuf,
 				  __u64 nextnum, struct the_nilfs *nilfs)
 {
@@ -132,8 +165,6 @@
 	segbuf->sb_sum.sumbytes = sizeof(struct nilfs_segment_summary);
 	segbuf->sb_sum.nfinfo = segbuf->sb_sum.nfileblk = 0;
 	segbuf->sb_sum.ctime = ctime;
-
-	segbuf->sb_io_error = 0;
 	return 0;
 }
 
@@ -219,7 +250,7 @@
 	raw_sum->ss_datasum = cpu_to_le32(crc);
 }
 
-void nilfs_release_buffers(struct list_head *list)
+static void nilfs_release_buffers(struct list_head *list)
 {
 	struct buffer_head *bh, *n;
 
@@ -241,13 +272,56 @@
 	}
 }
 
+static void nilfs_segbuf_clear(struct nilfs_segment_buffer *segbuf)
+{
+	nilfs_release_buffers(&segbuf->sb_segsum_buffers);
+	nilfs_release_buffers(&segbuf->sb_payload_buffers);
+}
+
+/*
+ * Iterators for segment buffers
+ */
+void nilfs_clear_logs(struct list_head *logs)
+{
+	struct nilfs_segment_buffer *segbuf;
+
+	list_for_each_entry(segbuf, logs, sb_list)
+		nilfs_segbuf_clear(segbuf);
+}
+
+void nilfs_truncate_logs(struct list_head *logs,
+			 struct nilfs_segment_buffer *last)
+{
+	struct nilfs_segment_buffer *n, *segbuf;
+
+	segbuf = list_prepare_entry(last, logs, sb_list);
+	list_for_each_entry_safe_continue(segbuf, n, logs, sb_list) {
+		list_del_init(&segbuf->sb_list);
+		nilfs_segbuf_clear(segbuf);
+		nilfs_segbuf_free(segbuf);
+	}
+}
+
+int nilfs_wait_on_logs(struct list_head *logs)
+{
+	struct nilfs_segment_buffer *segbuf;
+	int err;
+
+	list_for_each_entry(segbuf, logs, sb_list) {
+		err = nilfs_segbuf_wait(segbuf);
+		if (err)
+			return err;
+	}
+	return 0;
+}
+
 /*
  * BIO operations
  */
 static void nilfs_end_bio_write(struct bio *bio, int err)
 {
 	const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
-	struct nilfs_write_info *wi = bio->bi_private;
+	struct nilfs_segment_buffer *segbuf = bio->bi_private;
 
 	if (err == -EOPNOTSUPP) {
 		set_bit(BIO_EOPNOTSUPP, &bio->bi_flags);
@@ -256,21 +330,22 @@
 	}
 
 	if (!uptodate)
-		atomic_inc(&wi->err);
+		atomic_inc(&segbuf->sb_err);
 
 	bio_put(bio);
-	complete(&wi->bio_event);
+	complete(&segbuf->sb_bio_event);
 }
 
-static int nilfs_submit_seg_bio(struct nilfs_write_info *wi, int mode)
+static int nilfs_segbuf_submit_bio(struct nilfs_segment_buffer *segbuf,
+				   struct nilfs_write_info *wi, int mode)
 {
 	struct bio *bio = wi->bio;
 	int err;
 
-	if (wi->nbio > 0 && bdi_write_congested(wi->bdi)) {
-		wait_for_completion(&wi->bio_event);
-		wi->nbio--;
-		if (unlikely(atomic_read(&wi->err))) {
+	if (segbuf->sb_nbio > 0 && bdi_write_congested(wi->nilfs->ns_bdi)) {
+		wait_for_completion(&segbuf->sb_bio_event);
+		segbuf->sb_nbio--;
+		if (unlikely(atomic_read(&segbuf->sb_err))) {
 			bio_put(bio);
 			err = -EIO;
 			goto failed;
@@ -278,7 +353,7 @@
 	}
 
 	bio->bi_end_io = nilfs_end_bio_write;
-	bio->bi_private = wi;
+	bio->bi_private = segbuf;
 	bio_get(bio);
 	submit_bio(mode, bio);
 	if (bio_flagged(bio, BIO_EOPNOTSUPP)) {
@@ -286,7 +361,7 @@
 		err = -EOPNOTSUPP;
 		goto failed;
 	}
-	wi->nbio++;
+	segbuf->sb_nbio++;
 	bio_put(bio);
 
 	wi->bio = NULL;
@@ -301,17 +376,15 @@
 }
 
 /**
- * nilfs_alloc_seg_bio - allocate a bio for writing segment.
- * @sb: super block
- * @start: beginning disk block number of this BIO.
+ * nilfs_alloc_seg_bio - allocate a new bio for writing log
+ * @nilfs: nilfs object
+ * @start: start block number of the bio
  * @nr_vecs: request size of page vector.
  *
- * alloc_seg_bio() allocates a new BIO structure and initialize it.
- *
  * Return Value: On success, pointer to the struct bio is returned.
  * On error, NULL is returned.
  */
-static struct bio *nilfs_alloc_seg_bio(struct super_block *sb, sector_t start,
+static struct bio *nilfs_alloc_seg_bio(struct the_nilfs *nilfs, sector_t start,
 				       int nr_vecs)
 {
 	struct bio *bio;
@@ -322,36 +395,33 @@
 			bio = bio_alloc(GFP_NOIO, nr_vecs);
 	}
 	if (likely(bio)) {
-		bio->bi_bdev = sb->s_bdev;
-		bio->bi_sector = (sector_t)start << (sb->s_blocksize_bits - 9);
+		bio->bi_bdev = nilfs->ns_bdev;
+		bio->bi_sector = start << (nilfs->ns_blocksize_bits - 9);
 	}
 	return bio;
 }
 
-void nilfs_segbuf_prepare_write(struct nilfs_segment_buffer *segbuf,
-				struct nilfs_write_info *wi)
+static void nilfs_segbuf_prepare_write(struct nilfs_segment_buffer *segbuf,
+				       struct nilfs_write_info *wi)
 {
 	wi->bio = NULL;
 	wi->rest_blocks = segbuf->sb_sum.nblocks;
-	wi->max_pages = bio_get_nr_vecs(wi->sb->s_bdev);
+	wi->max_pages = bio_get_nr_vecs(wi->nilfs->ns_bdev);
 	wi->nr_vecs = min(wi->max_pages, wi->rest_blocks);
 	wi->start = wi->end = 0;
-	wi->nbio = 0;
 	wi->blocknr = segbuf->sb_pseg_start;
-
-	atomic_set(&wi->err, 0);
-	init_completion(&wi->bio_event);
 }
 
-static int nilfs_submit_bh(struct nilfs_write_info *wi, struct buffer_head *bh,
-			   int mode)
+static int nilfs_segbuf_submit_bh(struct nilfs_segment_buffer *segbuf,
+				  struct nilfs_write_info *wi,
+				  struct buffer_head *bh, int mode)
 {
 	int len, err;
 
 	BUG_ON(wi->nr_vecs <= 0);
  repeat:
 	if (!wi->bio) {
-		wi->bio = nilfs_alloc_seg_bio(wi->sb, wi->blocknr + wi->end,
+		wi->bio = nilfs_alloc_seg_bio(wi->nilfs, wi->blocknr + wi->end,
 					      wi->nr_vecs);
 		if (unlikely(!wi->bio))
 			return -ENOMEM;
@@ -363,76 +433,83 @@
 		return 0;
 	}
 	/* bio is FULL */
-	err = nilfs_submit_seg_bio(wi, mode);
+	err = nilfs_segbuf_submit_bio(segbuf, wi, mode);
 	/* never submit current bh */
 	if (likely(!err))
 		goto repeat;
 	return err;
 }
 
+/**
+ * nilfs_segbuf_write - submit write requests of a log
+ * @segbuf: buffer storing a log to be written
+ * @nilfs: nilfs object
+ *
+ * Return Value: On Success, 0 is returned. On Error, one of the following
+ * negative error code is returned.
+ *
+ * %-EIO - I/O error
+ *
+ * %-ENOMEM - Insufficient memory available.
+ */
 int nilfs_segbuf_write(struct nilfs_segment_buffer *segbuf,
-		       struct nilfs_write_info *wi)
+		       struct the_nilfs *nilfs)
 {
+	struct nilfs_write_info wi;
 	struct buffer_head *bh;
-	int res, rw = WRITE;
+	int res = 0, rw = WRITE;
+
+	wi.nilfs = nilfs;
+	nilfs_segbuf_prepare_write(segbuf, &wi);
 
 	list_for_each_entry(bh, &segbuf->sb_segsum_buffers, b_assoc_buffers) {
-		res = nilfs_submit_bh(wi, bh, rw);
+		res = nilfs_segbuf_submit_bh(segbuf, &wi, bh, rw);
 		if (unlikely(res))
 			goto failed_bio;
 	}
 
 	list_for_each_entry(bh, &segbuf->sb_payload_buffers, b_assoc_buffers) {
-		res = nilfs_submit_bh(wi, bh, rw);
+		res = nilfs_segbuf_submit_bh(segbuf, &wi, bh, rw);
 		if (unlikely(res))
 			goto failed_bio;
 	}
 
-	if (wi->bio) {
+	if (wi.bio) {
 		/*
 		 * Last BIO is always sent through the following
 		 * submission.
 		 */
 		rw |= (1 << BIO_RW_SYNCIO) | (1 << BIO_RW_UNPLUG);
-		res = nilfs_submit_seg_bio(wi, rw);
-		if (unlikely(res))
-			goto failed_bio;
+		res = nilfs_segbuf_submit_bio(segbuf, &wi, rw);
 	}
 
-	res = 0;
- out:
-	return res;
-
  failed_bio:
-	atomic_inc(&wi->err);
-	goto out;
+	return res;
 }
 
 /**
  * nilfs_segbuf_wait - wait for completion of requested BIOs
- * @wi: nilfs_write_info
+ * @segbuf: segment buffer
  *
  * Return Value: On Success, 0 is returned. On Error, one of the following
  * negative error code is returned.
  *
  * %-EIO - I/O error
  */
-int nilfs_segbuf_wait(struct nilfs_segment_buffer *segbuf,
-		      struct nilfs_write_info *wi)
+int nilfs_segbuf_wait(struct nilfs_segment_buffer *segbuf)
 {
 	int err = 0;
 
-	if (!wi->nbio)
+	if (!segbuf->sb_nbio)
 		return 0;
 
 	do {
-		wait_for_completion(&wi->bio_event);
-	} while (--wi->nbio > 0);
+		wait_for_completion(&segbuf->sb_bio_event);
+	} while (--segbuf->sb_nbio > 0);
 
-	if (unlikely(atomic_read(&wi->err) > 0)) {
+	if (unlikely(atomic_read(&segbuf->sb_err) > 0)) {
 		printk(KERN_ERR "NILFS: IO error writing segment\n");
 		err = -EIO;
-		segbuf->sb_io_error = 1;
 	}
 	return err;
 }
diff --git a/fs/nilfs2/segbuf.h b/fs/nilfs2/segbuf.h
index 0c3076f..6af1630 100644
--- a/fs/nilfs2/segbuf.h
+++ b/fs/nilfs2/segbuf.h
@@ -27,7 +27,6 @@
 #include <linux/buffer_head.h>
 #include <linux/bio.h>
 #include <linux/completion.h>
-#include <linux/backing-dev.h>
 
 /**
  * struct nilfs_segsum_info - On-memory segment summary
@@ -77,7 +76,9 @@
  * @sb_rest_blocks: Number of residual blocks in the current segment
  * @sb_segsum_buffers: List of buffers for segment summaries
  * @sb_payload_buffers: List of buffers for segment payload
- * @sb_io_error: I/O error status
+ * @sb_nbio: Number of flying bio requests
+ * @sb_err: I/O error status
+ * @sb_bio_event: Completion event of log writing
  */
 struct nilfs_segment_buffer {
 	struct super_block     *sb_super;
@@ -96,7 +97,9 @@
 	struct list_head	sb_payload_buffers; /* including super root */
 
 	/* io status */
-	int			sb_io_error;
+	int			sb_nbio;
+	atomic_t		sb_err;
+	struct completion	sb_bio_event;
 };
 
 #define NILFS_LIST_SEGBUF(head)  \
@@ -125,6 +128,8 @@
 void nilfs_segbuf_free(struct nilfs_segment_buffer *);
 void nilfs_segbuf_map(struct nilfs_segment_buffer *, __u64, unsigned long,
 		      struct the_nilfs *);
+void nilfs_segbuf_map_cont(struct nilfs_segment_buffer *segbuf,
+			   struct nilfs_segment_buffer *prev);
 void nilfs_segbuf_set_next_segnum(struct nilfs_segment_buffer *, __u64,
 				  struct the_nilfs *);
 int nilfs_segbuf_reset(struct nilfs_segment_buffer *, unsigned, time_t);
@@ -161,41 +166,18 @@
 	segbuf->sb_sum.nfileblk++;
 }
 
-void nilfs_release_buffers(struct list_head *);
+int nilfs_segbuf_write(struct nilfs_segment_buffer *segbuf,
+		       struct the_nilfs *nilfs);
+int nilfs_segbuf_wait(struct nilfs_segment_buffer *segbuf);
 
-static inline void nilfs_segbuf_clear(struct nilfs_segment_buffer *segbuf)
+void nilfs_clear_logs(struct list_head *logs);
+void nilfs_truncate_logs(struct list_head *logs,
+			 struct nilfs_segment_buffer *last);
+int nilfs_wait_on_logs(struct list_head *logs);
+
+static inline void nilfs_destroy_logs(struct list_head *logs)
 {
-	nilfs_release_buffers(&segbuf->sb_segsum_buffers);
-	nilfs_release_buffers(&segbuf->sb_payload_buffers);
+	nilfs_truncate_logs(logs, NULL);
 }
 
-struct nilfs_write_info {
-	struct bio	       *bio;
-	int 			start, end; /* The region to be submitted */
-	int			rest_blocks;
-	int			max_pages;
-	int			nr_vecs;
-	sector_t		blocknr;
-
-	int			nbio;
-	atomic_t		err;
-	struct completion	bio_event;
-				/* completion event of segment write */
-
-	/*
-	 * The following fields must be set explicitly
-	 */
-	struct super_block     *sb;
-	struct backing_dev_info *bdi; /* backing dev info */
-	struct buffer_head     *bh_sr;
-};
-
-
-void nilfs_segbuf_prepare_write(struct nilfs_segment_buffer *,
-				struct nilfs_write_info *);
-int nilfs_segbuf_write(struct nilfs_segment_buffer *,
-		       struct nilfs_write_info *);
-int nilfs_segbuf_wait(struct nilfs_segment_buffer *,
-		      struct nilfs_write_info *);
-
 #endif /* _NILFS_SEGBUF_H */
diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c
index 6eff66a..17584c5 100644
--- a/fs/nilfs2/segment.c
+++ b/fs/nilfs2/segment.c
@@ -974,12 +974,12 @@
 			      nilfs->ns_nongc_ctime : sci->sc_seg_ctime);
 	raw_sr->sr_flags = 0;
 
-	nilfs_mdt_write_inode_direct(
-		nilfs_dat_inode(nilfs), bh_sr, NILFS_SR_DAT_OFFSET(isz));
-	nilfs_mdt_write_inode_direct(
-		nilfs->ns_cpfile, bh_sr, NILFS_SR_CPFILE_OFFSET(isz));
-	nilfs_mdt_write_inode_direct(
-		nilfs->ns_sufile, bh_sr, NILFS_SR_SUFILE_OFFSET(isz));
+	nilfs_write_inode_common(nilfs_dat_inode(nilfs), (void *)raw_sr +
+				 NILFS_SR_DAT_OFFSET(isz), 1);
+	nilfs_write_inode_common(nilfs->ns_cpfile, (void *)raw_sr +
+				 NILFS_SR_CPFILE_OFFSET(isz), 1);
+	nilfs_write_inode_common(nilfs->ns_sufile, (void *)raw_sr +
+				 NILFS_SR_SUFILE_OFFSET(isz), 1);
 }
 
 static void nilfs_redirty_inodes(struct list_head *head)
@@ -1273,73 +1273,75 @@
 	return err;
 }
 
-static int nilfs_touch_segusage(struct inode *sufile, __u64 segnum)
-{
-	struct buffer_head *bh_su;
-	struct nilfs_segment_usage *raw_su;
-	int err;
-
-	err = nilfs_sufile_get_segment_usage(sufile, segnum, &raw_su, &bh_su);
-	if (unlikely(err))
-		return err;
-	nilfs_mdt_mark_buffer_dirty(bh_su);
-	nilfs_mdt_mark_dirty(sufile);
-	nilfs_sufile_put_segment_usage(sufile, segnum, bh_su);
-	return 0;
-}
-
+/**
+ * nilfs_segctor_begin_construction - setup segment buffer to make a new log
+ * @sci: nilfs_sc_info
+ * @nilfs: nilfs object
+ */
 static int nilfs_segctor_begin_construction(struct nilfs_sc_info *sci,
 					    struct the_nilfs *nilfs)
 {
-	struct nilfs_segment_buffer *segbuf, *n;
+	struct nilfs_segment_buffer *segbuf, *prev;
 	__u64 nextnum;
-	int err;
+	int err, alloc = 0;
 
-	if (list_empty(&sci->sc_segbufs)) {
-		segbuf = nilfs_segbuf_new(sci->sc_super);
-		if (unlikely(!segbuf))
-			return -ENOMEM;
-		list_add(&segbuf->sb_list, &sci->sc_segbufs);
-	} else
-		segbuf = NILFS_FIRST_SEGBUF(&sci->sc_segbufs);
+	segbuf = nilfs_segbuf_new(sci->sc_super);
+	if (unlikely(!segbuf))
+		return -ENOMEM;
 
-	nilfs_segbuf_map(segbuf, nilfs->ns_segnum, nilfs->ns_pseg_offset,
-			 nilfs);
+	if (list_empty(&sci->sc_write_logs)) {
+		nilfs_segbuf_map(segbuf, nilfs->ns_segnum,
+				 nilfs->ns_pseg_offset, nilfs);
+		if (segbuf->sb_rest_blocks < NILFS_PSEG_MIN_BLOCKS) {
+			nilfs_shift_to_next_segment(nilfs);
+			nilfs_segbuf_map(segbuf, nilfs->ns_segnum, 0, nilfs);
+		}
 
-	if (segbuf->sb_rest_blocks < NILFS_PSEG_MIN_BLOCKS) {
-		nilfs_shift_to_next_segment(nilfs);
-		nilfs_segbuf_map(segbuf, nilfs->ns_segnum, 0, nilfs);
-	}
-	sci->sc_segbuf_nblocks = segbuf->sb_rest_blocks;
-
-	err = nilfs_touch_segusage(nilfs->ns_sufile, segbuf->sb_segnum);
-	if (unlikely(err))
-		return err;
-
-	if (nilfs->ns_segnum == nilfs->ns_nextnum) {
-		/* Start from the head of a new full segment */
-		err = nilfs_sufile_alloc(nilfs->ns_sufile, &nextnum);
-		if (unlikely(err))
-			return err;
-	} else
+		segbuf->sb_sum.seg_seq = nilfs->ns_seg_seq;
 		nextnum = nilfs->ns_nextnum;
 
-	segbuf->sb_sum.seg_seq = nilfs->ns_seg_seq;
+		if (nilfs->ns_segnum == nilfs->ns_nextnum)
+			/* Start from the head of a new full segment */
+			alloc++;
+	} else {
+		/* Continue logs */
+		prev = NILFS_LAST_SEGBUF(&sci->sc_write_logs);
+		nilfs_segbuf_map_cont(segbuf, prev);
+		segbuf->sb_sum.seg_seq = prev->sb_sum.seg_seq;
+		nextnum = prev->sb_nextnum;
+
+		if (segbuf->sb_rest_blocks < NILFS_PSEG_MIN_BLOCKS) {
+			nilfs_segbuf_map(segbuf, prev->sb_nextnum, 0, nilfs);
+			segbuf->sb_sum.seg_seq++;
+			alloc++;
+		}
+	}
+
+	err = nilfs_sufile_mark_dirty(nilfs->ns_sufile, segbuf->sb_segnum);
+	if (err)
+		goto failed;
+
+	if (alloc) {
+		err = nilfs_sufile_alloc(nilfs->ns_sufile, &nextnum);
+		if (err)
+			goto failed;
+	}
 	nilfs_segbuf_set_next_segnum(segbuf, nextnum, nilfs);
 
-	/* truncating segment buffers */
-	list_for_each_entry_safe_continue(segbuf, n, &sci->sc_segbufs,
-					  sb_list) {
-		list_del_init(&segbuf->sb_list);
-		nilfs_segbuf_free(segbuf);
-	}
+	BUG_ON(!list_empty(&sci->sc_segbufs));
+	list_add_tail(&segbuf->sb_list, &sci->sc_segbufs);
+	sci->sc_segbuf_nblocks = segbuf->sb_rest_blocks;
 	return 0;
+
+ failed:
+	nilfs_segbuf_free(segbuf);
+	return err;
 }
 
 static int nilfs_segctor_extend_segments(struct nilfs_sc_info *sci,
 					 struct the_nilfs *nilfs, int nadd)
 {
-	struct nilfs_segment_buffer *segbuf, *prev, *n;
+	struct nilfs_segment_buffer *segbuf, *prev;
 	struct inode *sufile = nilfs->ns_sufile;
 	__u64 nextnextnum;
 	LIST_HEAD(list);
@@ -1352,7 +1354,7 @@
 	 * not be dirty.  The following call ensures that the buffer is dirty
 	 * and will pin the buffer on memory until the sufile is written.
 	 */
-	err = nilfs_touch_segusage(sufile, prev->sb_nextnum);
+	err = nilfs_sufile_mark_dirty(sufile, prev->sb_nextnum);
 	if (unlikely(err))
 		return err;
 
@@ -1378,33 +1380,33 @@
 		list_add_tail(&segbuf->sb_list, &list);
 		prev = segbuf;
 	}
-	list_splice(&list, sci->sc_segbufs.prev);
+	list_splice_tail(&list, &sci->sc_segbufs);
 	return 0;
 
  failed_segbuf:
 	nilfs_segbuf_free(segbuf);
  failed:
-	list_for_each_entry_safe(segbuf, n, &list, sb_list) {
+	list_for_each_entry(segbuf, &list, sb_list) {
 		ret = nilfs_sufile_free(sufile, segbuf->sb_nextnum);
 		WARN_ON(ret); /* never fails */
-		list_del_init(&segbuf->sb_list);
-		nilfs_segbuf_free(segbuf);
 	}
+	nilfs_destroy_logs(&list);
 	return err;
 }
 
-static void nilfs_segctor_free_incomplete_segments(struct nilfs_sc_info *sci,
-						   struct the_nilfs *nilfs)
+static void nilfs_free_incomplete_logs(struct list_head *logs,
+				       struct the_nilfs *nilfs)
 {
-	struct nilfs_segment_buffer *segbuf;
-	int ret, done = 0;
+	struct nilfs_segment_buffer *segbuf, *prev;
+	struct inode *sufile = nilfs->ns_sufile;
+	int ret;
 
-	segbuf = NILFS_FIRST_SEGBUF(&sci->sc_segbufs);
+	segbuf = NILFS_FIRST_SEGBUF(logs);
 	if (nilfs->ns_nextnum != segbuf->sb_nextnum) {
-		ret = nilfs_sufile_free(nilfs->ns_sufile, segbuf->sb_nextnum);
+		ret = nilfs_sufile_free(sufile, segbuf->sb_nextnum);
 		WARN_ON(ret); /* never fails */
 	}
-	if (segbuf->sb_io_error) {
+	if (atomic_read(&segbuf->sb_err)) {
 		/* Case 1: The first segment failed */
 		if (segbuf->sb_pseg_start != segbuf->sb_fseg_start)
 			/* Case 1a:  Partial segment appended into an existing
@@ -1413,106 +1415,54 @@
 						segbuf->sb_fseg_end);
 		else /* Case 1b:  New full segment */
 			set_nilfs_discontinued(nilfs);
-		done++;
 	}
 
-	list_for_each_entry_continue(segbuf, &sci->sc_segbufs, sb_list) {
-		ret = nilfs_sufile_free(nilfs->ns_sufile, segbuf->sb_nextnum);
-		WARN_ON(ret); /* never fails */
-		if (!done && segbuf->sb_io_error) {
-			if (segbuf->sb_segnum != nilfs->ns_nextnum)
-				/* Case 2: extended segment (!= next) failed */
-				nilfs_sufile_set_error(nilfs->ns_sufile,
-						       segbuf->sb_segnum);
-			done++;
+	prev = segbuf;
+	list_for_each_entry_continue(segbuf, logs, sb_list) {
+		if (prev->sb_nextnum != segbuf->sb_nextnum) {
+			ret = nilfs_sufile_free(sufile, segbuf->sb_nextnum);
+			WARN_ON(ret); /* never fails */
 		}
+		if (atomic_read(&segbuf->sb_err) &&
+		    segbuf->sb_segnum != nilfs->ns_nextnum)
+			/* Case 2: extended segment (!= next) failed */
+			nilfs_sufile_set_error(sufile, segbuf->sb_segnum);
+		prev = segbuf;
 	}
 }
 
-static void nilfs_segctor_clear_segment_buffers(struct nilfs_sc_info *sci)
-{
-	struct nilfs_segment_buffer *segbuf;
-
-	list_for_each_entry(segbuf, &sci->sc_segbufs, sb_list)
-		nilfs_segbuf_clear(segbuf);
-	sci->sc_super_root = NULL;
-}
-
-static void nilfs_segctor_destroy_segment_buffers(struct nilfs_sc_info *sci)
-{
-	struct nilfs_segment_buffer *segbuf;
-
-	while (!list_empty(&sci->sc_segbufs)) {
-		segbuf = NILFS_FIRST_SEGBUF(&sci->sc_segbufs);
-		list_del_init(&segbuf->sb_list);
-		nilfs_segbuf_free(segbuf);
-	}
-	/* sci->sc_curseg = NULL; */
-}
-
-static void nilfs_segctor_end_construction(struct nilfs_sc_info *sci,
-					   struct the_nilfs *nilfs, int err)
-{
-	if (unlikely(err)) {
-		nilfs_segctor_free_incomplete_segments(sci, nilfs);
-		if (sci->sc_stage.flags & NILFS_CF_SUFREED) {
-			int ret;
-
-			ret = nilfs_sufile_cancel_freev(nilfs->ns_sufile,
-							sci->sc_freesegs,
-							sci->sc_nfreesegs,
-							NULL);
-			WARN_ON(ret); /* do not happen */
-		}
-	}
-	nilfs_segctor_clear_segment_buffers(sci);
-}
-
 static void nilfs_segctor_update_segusage(struct nilfs_sc_info *sci,
 					  struct inode *sufile)
 {
 	struct nilfs_segment_buffer *segbuf;
-	struct buffer_head *bh_su;
-	struct nilfs_segment_usage *raw_su;
 	unsigned long live_blocks;
 	int ret;
 
 	list_for_each_entry(segbuf, &sci->sc_segbufs, sb_list) {
-		ret = nilfs_sufile_get_segment_usage(sufile, segbuf->sb_segnum,
-						     &raw_su, &bh_su);
-		WARN_ON(ret); /* always succeed because bh_su is dirty */
 		live_blocks = segbuf->sb_sum.nblocks +
 			(segbuf->sb_pseg_start - segbuf->sb_fseg_start);
-		raw_su->su_lastmod = cpu_to_le64(sci->sc_seg_ctime);
-		raw_su->su_nblocks = cpu_to_le32(live_blocks);
-		nilfs_sufile_put_segment_usage(sufile, segbuf->sb_segnum,
-					       bh_su);
+		ret = nilfs_sufile_set_segment_usage(sufile, segbuf->sb_segnum,
+						     live_blocks,
+						     sci->sc_seg_ctime);
+		WARN_ON(ret); /* always succeed because the segusage is dirty */
 	}
 }
 
-static void nilfs_segctor_cancel_segusage(struct nilfs_sc_info *sci,
-					  struct inode *sufile)
+static void nilfs_cancel_segusage(struct list_head *logs, struct inode *sufile)
 {
 	struct nilfs_segment_buffer *segbuf;
-	struct buffer_head *bh_su;
-	struct nilfs_segment_usage *raw_su;
 	int ret;
 
-	segbuf = NILFS_FIRST_SEGBUF(&sci->sc_segbufs);
-	ret = nilfs_sufile_get_segment_usage(sufile, segbuf->sb_segnum,
-					     &raw_su, &bh_su);
-	WARN_ON(ret); /* always succeed because bh_su is dirty */
-	raw_su->su_nblocks = cpu_to_le32(segbuf->sb_pseg_start -
-					 segbuf->sb_fseg_start);
-	nilfs_sufile_put_segment_usage(sufile, segbuf->sb_segnum, bh_su);
+	segbuf = NILFS_FIRST_SEGBUF(logs);
+	ret = nilfs_sufile_set_segment_usage(sufile, segbuf->sb_segnum,
+					     segbuf->sb_pseg_start -
+					     segbuf->sb_fseg_start, 0);
+	WARN_ON(ret); /* always succeed because the segusage is dirty */
 
-	list_for_each_entry_continue(segbuf, &sci->sc_segbufs, sb_list) {
-		ret = nilfs_sufile_get_segment_usage(sufile, segbuf->sb_segnum,
-						     &raw_su, &bh_su);
+	list_for_each_entry_continue(segbuf, logs, sb_list) {
+		ret = nilfs_sufile_set_segment_usage(sufile, segbuf->sb_segnum,
+						     0, 0);
 		WARN_ON(ret); /* always succeed */
-		raw_su->su_nblocks = 0;
-		nilfs_sufile_put_segment_usage(sufile, segbuf->sb_segnum,
-					       bh_su);
 	}
 }
 
@@ -1520,17 +1470,15 @@
 					    struct nilfs_segment_buffer *last,
 					    struct inode *sufile)
 {
-	struct nilfs_segment_buffer *segbuf = last, *n;
+	struct nilfs_segment_buffer *segbuf = last;
 	int ret;
 
-	list_for_each_entry_safe_continue(segbuf, n, &sci->sc_segbufs,
-					  sb_list) {
-		list_del_init(&segbuf->sb_list);
+	list_for_each_entry_continue(segbuf, &sci->sc_segbufs, sb_list) {
 		sci->sc_segbuf_nblocks -= segbuf->sb_rest_blocks;
 		ret = nilfs_sufile_free(sufile, segbuf->sb_nextnum);
 		WARN_ON(ret);
-		nilfs_segbuf_free(segbuf);
 	}
+	nilfs_truncate_logs(&sci->sc_segbufs, last);
 }
 
 
@@ -1569,7 +1517,7 @@
 							NULL);
 			WARN_ON(err); /* do not happen */
 		}
-		nilfs_segctor_clear_segment_buffers(sci);
+		nilfs_clear_logs(&sci->sc_segbufs);
 
 		err = nilfs_segctor_extend_segments(sci, nilfs, nadd);
 		if (unlikely(err))
@@ -1814,26 +1762,18 @@
 }
 
 static int nilfs_segctor_write(struct nilfs_sc_info *sci,
-			       struct backing_dev_info *bdi)
+			       struct the_nilfs *nilfs)
 {
 	struct nilfs_segment_buffer *segbuf;
-	struct nilfs_write_info wi;
-	int err, res;
-
-	wi.sb = sci->sc_super;
-	wi.bh_sr = sci->sc_super_root;
-	wi.bdi = bdi;
+	int ret = 0;
 
 	list_for_each_entry(segbuf, &sci->sc_segbufs, sb_list) {
-		nilfs_segbuf_prepare_write(segbuf, &wi);
-		err = nilfs_segbuf_write(segbuf, &wi);
-
-		res = nilfs_segbuf_wait(segbuf, &wi);
-		err = err ? : res;
-		if (err)
-			return err;
+		ret = nilfs_segbuf_write(segbuf, nilfs);
+		if (ret)
+			break;
 	}
-	return 0;
+	list_splice_tail_init(&sci->sc_segbufs, &sci->sc_write_logs);
+	return ret;
 }
 
 static void __nilfs_end_page_io(struct page *page, int err)
@@ -1911,15 +1851,17 @@
 	}
 }
 
-static void nilfs_segctor_abort_write(struct nilfs_sc_info *sci,
-				      struct page *failed_page, int err)
+static void nilfs_abort_logs(struct list_head *logs, struct page *failed_page,
+			     struct buffer_head *bh_sr, int err)
 {
 	struct nilfs_segment_buffer *segbuf;
 	struct page *bd_page = NULL, *fs_page = NULL;
+	struct buffer_head *bh;
 
-	list_for_each_entry(segbuf, &sci->sc_segbufs, sb_list) {
-		struct buffer_head *bh;
+	if (list_empty(logs))
+		return;
 
+	list_for_each_entry(segbuf, logs, sb_list) {
 		list_for_each_entry(bh, &segbuf->sb_segsum_buffers,
 				    b_assoc_buffers) {
 			if (bh->b_page != bd_page) {
@@ -1931,7 +1873,7 @@
 
 		list_for_each_entry(bh, &segbuf->sb_payload_buffers,
 				    b_assoc_buffers) {
-			if (bh == sci->sc_super_root) {
+			if (bh == bh_sr) {
 				if (bh->b_page != bd_page) {
 					end_page_writeback(bd_page);
 					bd_page = bh->b_page;
@@ -1941,7 +1883,7 @@
 			if (bh->b_page != fs_page) {
 				nilfs_end_page_io(fs_page, err);
 				if (fs_page && fs_page == failed_page)
-					goto done;
+					return;
 				fs_page = bh->b_page;
 			}
 		}
@@ -1950,8 +1892,34 @@
 		end_page_writeback(bd_page);
 
 	nilfs_end_page_io(fs_page, err);
- done:
+}
+
+static void nilfs_segctor_abort_construction(struct nilfs_sc_info *sci,
+					     struct the_nilfs *nilfs, int err)
+{
+	LIST_HEAD(logs);
+	int ret;
+
+	list_splice_tail_init(&sci->sc_write_logs, &logs);
+	ret = nilfs_wait_on_logs(&logs);
+	if (ret)
+		nilfs_abort_logs(&logs, NULL, sci->sc_super_root, ret);
+
+	list_splice_tail_init(&sci->sc_segbufs, &logs);
+	nilfs_cancel_segusage(&logs, nilfs->ns_sufile);
+	nilfs_free_incomplete_logs(&logs, nilfs);
 	nilfs_clear_copied_buffers(&sci->sc_copied_buffers, err);
+
+	if (sci->sc_stage.flags & NILFS_CF_SUFREED) {
+		ret = nilfs_sufile_cancel_freev(nilfs->ns_sufile,
+						sci->sc_freesegs,
+						sci->sc_nfreesegs,
+						NULL);
+		WARN_ON(ret); /* do not happen */
+	}
+
+	nilfs_destroy_logs(&logs);
+	sci->sc_super_root = NULL;
 }
 
 static void nilfs_set_next_segment(struct the_nilfs *nilfs,
@@ -1973,7 +1941,7 @@
 	struct the_nilfs *nilfs = sbi->s_nilfs;
 	int update_sr = (sci->sc_super_root != NULL);
 
-	list_for_each_entry(segbuf, &sci->sc_segbufs, sb_list) {
+	list_for_each_entry(segbuf, &sci->sc_write_logs, sb_list) {
 		struct buffer_head *bh;
 
 		list_for_each_entry(bh, &segbuf->sb_segsum_buffers,
@@ -2046,7 +2014,7 @@
 
 	sci->sc_nblk_inc += sci->sc_nblk_this_inc;
 
-	segbuf = NILFS_LAST_SEGBUF(&sci->sc_segbufs);
+	segbuf = NILFS_LAST_SEGBUF(&sci->sc_write_logs);
 	nilfs_set_next_segment(nilfs, segbuf);
 
 	if (update_sr) {
@@ -2057,10 +2025,23 @@
 		clear_bit(NILFS_SC_HAVE_DELTA, &sci->sc_flags);
 		clear_bit(NILFS_SC_DIRTY, &sci->sc_flags);
 		set_bit(NILFS_SC_SUPER_ROOT, &sci->sc_flags);
+		nilfs_segctor_clear_metadata_dirty(sci);
 	} else
 		clear_bit(NILFS_SC_SUPER_ROOT, &sci->sc_flags);
 }
 
+static int nilfs_segctor_wait(struct nilfs_sc_info *sci)
+{
+	int ret;
+
+	ret = nilfs_wait_on_logs(&sci->sc_write_logs);
+	if (!ret) {
+		nilfs_segctor_complete_write(sci);
+		nilfs_destroy_logs(&sci->sc_write_logs);
+	}
+	return ret;
+}
+
 static int nilfs_segctor_check_in_files(struct nilfs_sc_info *sci,
 					struct nilfs_sb_info *sbi)
 {
@@ -2173,7 +2154,7 @@
 		/* Avoid empty segment */
 		if (sci->sc_stage.scnt == NILFS_ST_DONE &&
 		    NILFS_SEG_EMPTY(&sci->sc_curseg->sb_sum)) {
-			nilfs_segctor_end_construction(sci, nilfs, 1);
+			nilfs_segctor_abort_construction(sci, nilfs, 1);
 			goto out;
 		}
 
@@ -2187,7 +2168,7 @@
 		if (has_sr) {
 			err = nilfs_segctor_fill_in_checkpoint(sci);
 			if (unlikely(err))
-				goto failed_to_make_up;
+				goto failed_to_write;
 
 			nilfs_segctor_fill_in_super_root(sci, nilfs);
 		}
@@ -2195,42 +2176,46 @@
 
 		/* Write partial segments */
 		err = nilfs_segctor_prepare_write(sci, &failed_page);
-		if (unlikely(err))
+		if (err) {
+			nilfs_abort_logs(&sci->sc_segbufs, failed_page,
+					 sci->sc_super_root, err);
 			goto failed_to_write;
-
+		}
 		nilfs_segctor_fill_in_checksums(sci, nilfs->ns_crc_seed);
 
-		err = nilfs_segctor_write(sci, nilfs->ns_bdi);
+		err = nilfs_segctor_write(sci, nilfs);
 		if (unlikely(err))
 			goto failed_to_write;
 
-		nilfs_segctor_complete_write(sci);
-
-		/* Commit segments */
-		if (has_sr)
-			nilfs_segctor_clear_metadata_dirty(sci);
-
-		nilfs_segctor_end_construction(sci, nilfs, 0);
-
+		if (sci->sc_stage.scnt == NILFS_ST_DONE ||
+		    nilfs->ns_blocksize_bits != PAGE_CACHE_SHIFT) {
+			/*
+			 * At this point, we avoid double buffering
+			 * for blocksize < pagesize because page dirty
+			 * flag is turned off during write and dirty
+			 * buffers are not properly collected for
+			 * pages crossing over segments.
+			 */
+			err = nilfs_segctor_wait(sci);
+			if (err)
+				goto failed_to_write;
+		}
 	} while (sci->sc_stage.scnt != NILFS_ST_DONE);
 
+	sci->sc_super_root = NULL;
+
  out:
-	nilfs_segctor_destroy_segment_buffers(sci);
 	nilfs_segctor_check_out_files(sci, sbi);
 	return err;
 
  failed_to_write:
-	nilfs_segctor_abort_write(sci, failed_page, err);
-	nilfs_segctor_cancel_segusage(sci, nilfs->ns_sufile);
-
- failed_to_make_up:
 	if (sci->sc_stage.flags & NILFS_CF_IFILE_STARTED)
 		nilfs_redirty_inodes(&sci->sc_dirty_files);
 
  failed:
 	if (nilfs_doing_gc())
 		nilfs_redirty_inodes(&sci->sc_gc_inodes);
-	nilfs_segctor_end_construction(sci, nilfs, err);
+	nilfs_segctor_abort_construction(sci, nilfs, err);
 	goto out;
 }
 
@@ -2559,7 +2544,7 @@
 
 	sci->sc_freesegs = kbufs[4];
 	sci->sc_nfreesegs = argv[4].v_nmembs;
-	list_splice_init(&nilfs->ns_gc_inodes, sci->sc_gc_inodes.prev);
+	list_splice_tail_init(&nilfs->ns_gc_inodes, &sci->sc_gc_inodes);
 
 	for (;;) {
 		nilfs_segctor_accept(sci, &req);
@@ -2788,6 +2773,7 @@
 	spin_lock_init(&sci->sc_state_lock);
 	INIT_LIST_HEAD(&sci->sc_dirty_files);
 	INIT_LIST_HEAD(&sci->sc_segbufs);
+	INIT_LIST_HEAD(&sci->sc_write_logs);
 	INIT_LIST_HEAD(&sci->sc_gc_inodes);
 	INIT_LIST_HEAD(&sci->sc_copied_buffers);
 
@@ -2855,6 +2841,7 @@
 	}
 
 	WARN_ON(!list_empty(&sci->sc_segbufs));
+	WARN_ON(!list_empty(&sci->sc_write_logs));
 
 	down_write(&sbi->s_nilfs->ns_segctor_sem);
 
diff --git a/fs/nilfs2/segment.h b/fs/nilfs2/segment.h
index 0d2a475..3d3ab2f 100644
--- a/fs/nilfs2/segment.h
+++ b/fs/nilfs2/segment.h
@@ -97,6 +97,7 @@
  * @sc_dsync_start: start byte offset of data pages
  * @sc_dsync_end: end byte offset of data pages (inclusive)
  * @sc_segbufs: List of segment buffers
+ * @sc_write_logs: List of segment buffers to hold logs under writing
  * @sc_segbuf_nblocks: Number of available blocks in segment buffers.
  * @sc_curseg: Current segment buffer
  * @sc_super_root: Pointer to the super root buffer
@@ -143,6 +144,7 @@
 
 	/* Segment buffers */
 	struct list_head	sc_segbufs;
+	struct list_head	sc_write_logs;
 	unsigned long		sc_segbuf_nblocks;
 	struct nilfs_segment_buffer *sc_curseg;
 	struct buffer_head     *sc_super_root;
diff --git a/fs/nilfs2/sufile.c b/fs/nilfs2/sufile.c
index 37994d4..b6c36d0 100644
--- a/fs/nilfs2/sufile.c
+++ b/fs/nilfs2/sufile.c
@@ -31,6 +31,16 @@
 #include "sufile.h"
 
 
+struct nilfs_sufile_info {
+	struct nilfs_mdt_info mi;
+	unsigned long ncleansegs;
+};
+
+static inline struct nilfs_sufile_info *NILFS_SUI(struct inode *sufile)
+{
+	return (struct nilfs_sufile_info *)NILFS_MDT(sufile);
+}
+
 static inline unsigned long
 nilfs_sufile_segment_usages_per_block(const struct inode *sufile)
 {
@@ -62,14 +72,6 @@
 		     max - curr + 1);
 }
 
-static inline struct nilfs_sufile_header *
-nilfs_sufile_block_get_header(const struct inode *sufile,
-			      struct buffer_head *bh,
-			      void *kaddr)
-{
-	return kaddr + bh_offset(bh);
-}
-
 static struct nilfs_segment_usage *
 nilfs_sufile_block_get_segment_usage(const struct inode *sufile, __u64 segnum,
 				     struct buffer_head *bh, void *kaddr)
@@ -110,6 +112,15 @@
 }
 
 /**
+ * nilfs_sufile_get_ncleansegs - return the number of clean segments
+ * @sufile: inode of segment usage file
+ */
+unsigned long nilfs_sufile_get_ncleansegs(struct inode *sufile)
+{
+	return NILFS_SUI(sufile)->ncleansegs;
+}
+
+/**
  * nilfs_sufile_updatev - modify multiple segment usages at a time
  * @sufile: inode of segment usage file
  * @segnumv: array of segment numbers
@@ -270,7 +281,7 @@
 	if (ret < 0)
 		goto out_sem;
 	kaddr = kmap_atomic(header_bh->b_page, KM_USER0);
-	header = nilfs_sufile_block_get_header(sufile, header_bh, kaddr);
+	header = kaddr + bh_offset(header_bh);
 	ncleansegs = le64_to_cpu(header->sh_ncleansegs);
 	last_alloc = le64_to_cpu(header->sh_last_alloc);
 	kunmap_atomic(kaddr, KM_USER0);
@@ -302,13 +313,13 @@
 			kunmap_atomic(kaddr, KM_USER0);
 
 			kaddr = kmap_atomic(header_bh->b_page, KM_USER0);
-			header = nilfs_sufile_block_get_header(
-				sufile, header_bh, kaddr);
+			header = kaddr + bh_offset(header_bh);
 			le64_add_cpu(&header->sh_ncleansegs, -1);
 			le64_add_cpu(&header->sh_ndirtysegs, 1);
 			header->sh_last_alloc = cpu_to_le64(segnum);
 			kunmap_atomic(kaddr, KM_USER0);
 
+			NILFS_SUI(sufile)->ncleansegs--;
 			nilfs_mdt_mark_buffer_dirty(header_bh);
 			nilfs_mdt_mark_buffer_dirty(su_bh);
 			nilfs_mdt_mark_dirty(sufile);
@@ -351,6 +362,8 @@
 	kunmap_atomic(kaddr, KM_USER0);
 
 	nilfs_sufile_mod_counter(header_bh, -1, 1);
+	NILFS_SUI(sufile)->ncleansegs--;
+
 	nilfs_mdt_mark_buffer_dirty(su_bh);
 	nilfs_mdt_mark_dirty(sufile);
 }
@@ -380,6 +393,8 @@
 	kunmap_atomic(kaddr, KM_USER0);
 
 	nilfs_sufile_mod_counter(header_bh, clean ? (u64)-1 : 0, dirty ? 0 : 1);
+	NILFS_SUI(sufile)->ncleansegs -= clean;
+
 	nilfs_mdt_mark_buffer_dirty(su_bh);
 	nilfs_mdt_mark_dirty(sufile);
 }
@@ -409,58 +424,61 @@
 	nilfs_mdt_mark_buffer_dirty(su_bh);
 
 	nilfs_sufile_mod_counter(header_bh, 1, sudirty ? (u64)-1 : 0);
+	NILFS_SUI(sufile)->ncleansegs++;
+
 	nilfs_mdt_mark_dirty(sufile);
 }
 
 /**
- * nilfs_sufile_get_segment_usage - get a segment usage
+ * nilfs_sufile_mark_dirty - mark the buffer having a segment usage dirty
  * @sufile: inode of segment usage file
  * @segnum: segment number
- * @sup: pointer to segment usage
- * @bhp: pointer to buffer head
- *
- * Description: nilfs_sufile_get_segment_usage() acquires the segment usage
- * specified by @segnum.
- *
- * Return Value: On success, 0 is returned, and the segment usage and the
- * buffer head of the buffer on which the segment usage is located are stored
- * in the place pointed by @sup and @bhp, respectively. On error, one of the
- * following negative error codes is returned.
- *
- * %-EIO - I/O error.
- *
- * %-ENOMEM - Insufficient amount of memory available.
- *
- * %-EINVAL - Invalid segment usage number.
  */
-int nilfs_sufile_get_segment_usage(struct inode *sufile, __u64 segnum,
-				   struct nilfs_segment_usage **sup,
-				   struct buffer_head **bhp)
+int nilfs_sufile_mark_dirty(struct inode *sufile, __u64 segnum)
+{
+	struct buffer_head *bh;
+	int ret;
+
+	ret = nilfs_sufile_get_segment_usage_block(sufile, segnum, 0, &bh);
+	if (!ret) {
+		nilfs_mdt_mark_buffer_dirty(bh);
+		nilfs_mdt_mark_dirty(sufile);
+		brelse(bh);
+	}
+	return ret;
+}
+
+/**
+ * nilfs_sufile_set_segment_usage - set usage of a segment
+ * @sufile: inode of segment usage file
+ * @segnum: segment number
+ * @nblocks: number of live blocks in the segment
+ * @modtime: modification time (option)
+ */
+int nilfs_sufile_set_segment_usage(struct inode *sufile, __u64 segnum,
+				   unsigned long nblocks, time_t modtime)
 {
 	struct buffer_head *bh;
 	struct nilfs_segment_usage *su;
 	void *kaddr;
 	int ret;
 
-	/* segnum is 0 origin */
-	if (segnum >= nilfs_sufile_get_nsegments(sufile))
-		return -EINVAL;
 	down_write(&NILFS_MDT(sufile)->mi_sem);
-	ret = nilfs_sufile_get_segment_usage_block(sufile, segnum, 1, &bh);
+	ret = nilfs_sufile_get_segment_usage_block(sufile, segnum, 0, &bh);
 	if (ret < 0)
 		goto out_sem;
-	kaddr = kmap(bh->b_page);
-	su = nilfs_sufile_block_get_segment_usage(sufile, segnum, bh, kaddr);
-	if (nilfs_segment_usage_error(su)) {
-		kunmap(bh->b_page);
-		brelse(bh);
-		ret = -EINVAL;
-		goto out_sem;
-	}
 
-	if (sup != NULL)
-		*sup = su;
-	*bhp = bh;
+	kaddr = kmap_atomic(bh->b_page, KM_USER0);
+	su = nilfs_sufile_block_get_segment_usage(sufile, segnum, bh, kaddr);
+	WARN_ON(nilfs_segment_usage_error(su));
+	if (modtime)
+		su->su_lastmod = cpu_to_le64(modtime);
+	su->su_nblocks = cpu_to_le32(nblocks);
+	kunmap_atomic(kaddr, KM_USER0);
+
+	nilfs_mdt_mark_buffer_dirty(bh);
+	nilfs_mdt_mark_dirty(sufile);
+	brelse(bh);
 
  out_sem:
 	up_write(&NILFS_MDT(sufile)->mi_sem);
@@ -468,23 +486,6 @@
 }
 
 /**
- * nilfs_sufile_put_segment_usage - put a segment usage
- * @sufile: inode of segment usage file
- * @segnum: segment number
- * @bh: buffer head
- *
- * Description: nilfs_sufile_put_segment_usage() releases the segment usage
- * specified by @segnum. @bh must be the buffer head which have been returned
- * by a previous call to nilfs_sufile_get_segment_usage() with @segnum.
- */
-void nilfs_sufile_put_segment_usage(struct inode *sufile, __u64 segnum,
-				    struct buffer_head *bh)
-{
-	kunmap(bh->b_page);
-	brelse(bh);
-}
-
-/**
  * nilfs_sufile_get_stat - get segment usage statistics
  * @sufile: inode of segment usage file
  * @stat: pointer to a structure of segment usage statistics
@@ -515,7 +516,7 @@
 		goto out_sem;
 
 	kaddr = kmap_atomic(header_bh->b_page, KM_USER0);
-	header = nilfs_sufile_block_get_header(sufile, header_bh, kaddr);
+	header = kaddr + bh_offset(header_bh);
 	sustat->ss_nsegs = nilfs_sufile_get_nsegments(sufile);
 	sustat->ss_ncleansegs = le64_to_cpu(header->sh_ncleansegs);
 	sustat->ss_ndirtysegs = le64_to_cpu(header->sh_ndirtysegs);
@@ -532,33 +533,6 @@
 	return ret;
 }
 
-/**
- * nilfs_sufile_get_ncleansegs - get the number of clean segments
- * @sufile: inode of segment usage file
- * @nsegsp: pointer to the number of clean segments
- *
- * Description: nilfs_sufile_get_ncleansegs() acquires the number of clean
- * segments.
- *
- * Return Value: On success, 0 is returned and the number of clean segments is
- * stored in the place pointed by @nsegsp. On error, one of the following
- * negative error codes is returned.
- *
- * %-EIO - I/O error.
- *
- * %-ENOMEM - Insufficient amount of memory available.
- */
-int nilfs_sufile_get_ncleansegs(struct inode *sufile, unsigned long *nsegsp)
-{
-	struct nilfs_sustat sustat;
-	int ret;
-
-	ret = nilfs_sufile_get_stat(sufile, &sustat);
-	if (ret == 0)
-		*nsegsp = sustat.ss_ncleansegs;
-	return ret;
-}
-
 void nilfs_sufile_do_set_error(struct inode *sufile, __u64 segnum,
 			       struct buffer_head *header_bh,
 			       struct buffer_head *su_bh)
@@ -577,8 +551,10 @@
 	nilfs_segment_usage_set_error(su);
 	kunmap_atomic(kaddr, KM_USER0);
 
-	if (suclean)
+	if (suclean) {
 		nilfs_sufile_mod_counter(header_bh, -1, 0);
+		NILFS_SUI(sufile)->ncleansegs--;
+	}
 	nilfs_mdt_mark_buffer_dirty(su_bh);
 	nilfs_mdt_mark_dirty(sufile);
 }
@@ -657,3 +633,48 @@
 	up_read(&NILFS_MDT(sufile)->mi_sem);
 	return ret;
 }
+
+/**
+ * nilfs_sufile_read - read sufile inode
+ * @sufile: sufile inode
+ * @raw_inode: on-disk sufile inode
+ */
+int nilfs_sufile_read(struct inode *sufile, struct nilfs_inode *raw_inode)
+{
+	struct nilfs_sufile_info *sui = NILFS_SUI(sufile);
+	struct buffer_head *header_bh;
+	struct nilfs_sufile_header *header;
+	void *kaddr;
+	int ret;
+
+	ret = nilfs_read_inode_common(sufile, raw_inode);
+	if (ret < 0)
+		return ret;
+
+	ret = nilfs_sufile_get_header_block(sufile, &header_bh);
+	if (!ret) {
+		kaddr = kmap_atomic(header_bh->b_page, KM_USER0);
+		header = kaddr + bh_offset(header_bh);
+		sui->ncleansegs = le64_to_cpu(header->sh_ncleansegs);
+		kunmap_atomic(kaddr, KM_USER0);
+		brelse(header_bh);
+	}
+	return ret;
+}
+
+/**
+ * nilfs_sufile_new - create sufile
+ * @nilfs: nilfs object
+ * @susize: size of a segment usage entry
+ */
+struct inode *nilfs_sufile_new(struct the_nilfs *nilfs, size_t susize)
+{
+	struct inode *sufile;
+
+	sufile = nilfs_mdt_new(nilfs, NULL, NILFS_SUFILE_INO,
+			       sizeof(struct nilfs_sufile_info));
+	if (sufile)
+		nilfs_mdt_set_entry_size(sufile, susize,
+					 sizeof(struct nilfs_sufile_header));
+	return sufile;
+}
diff --git a/fs/nilfs2/sufile.h b/fs/nilfs2/sufile.h
index 0e99e5c..15163b8 100644
--- a/fs/nilfs2/sufile.h
+++ b/fs/nilfs2/sufile.h
@@ -34,14 +34,13 @@
 	return NILFS_MDT(sufile)->mi_nilfs->ns_nsegments;
 }
 
+unsigned long nilfs_sufile_get_ncleansegs(struct inode *sufile);
+
 int nilfs_sufile_alloc(struct inode *, __u64 *);
-int nilfs_sufile_get_segment_usage(struct inode *, __u64,
-				   struct nilfs_segment_usage **,
-				   struct buffer_head **);
-void nilfs_sufile_put_segment_usage(struct inode *, __u64,
-				    struct buffer_head *);
+int nilfs_sufile_mark_dirty(struct inode *sufile, __u64 segnum);
+int nilfs_sufile_set_segment_usage(struct inode *sufile, __u64 segnum,
+				   unsigned long nblocks, time_t modtime);
 int nilfs_sufile_get_stat(struct inode *, struct nilfs_sustat *);
-int nilfs_sufile_get_ncleansegs(struct inode *, unsigned long *);
 ssize_t nilfs_sufile_get_suinfo(struct inode *, __u64, void *, unsigned,
 				size_t);
 
@@ -62,6 +61,9 @@
 void nilfs_sufile_do_set_error(struct inode *, __u64, struct buffer_head *,
 			       struct buffer_head *);
 
+int nilfs_sufile_read(struct inode *sufile, struct nilfs_inode *raw_inode);
+struct inode *nilfs_sufile_new(struct the_nilfs *nilfs, size_t susize);
+
 /**
  * nilfs_sufile_scrap - make a segment garbage
  * @sufile: inode of segment usage file
diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c
index 644e667..5403b3e 100644
--- a/fs/nilfs2/super.c
+++ b/fs/nilfs2/super.c
@@ -363,14 +363,10 @@
 	list_add(&sbi->s_list, &nilfs->ns_supers);
 	up_write(&nilfs->ns_super_sem);
 
-	sbi->s_ifile = nilfs_mdt_new(nilfs, sbi->s_super, NILFS_IFILE_INO);
+	sbi->s_ifile = nilfs_ifile_new(sbi, nilfs->ns_inode_size);
 	if (!sbi->s_ifile)
 		return -ENOMEM;
 
-	err = nilfs_palloc_init_blockgroup(sbi->s_ifile, nilfs->ns_inode_size);
-	if (unlikely(err))
-		goto failed;
-
 	down_read(&nilfs->ns_segctor_sem);
 	err = nilfs_cpfile_get_checkpoint(nilfs->ns_cpfile, cno, 0, &raw_cp,
 					  &bh_cp);
@@ -411,7 +407,6 @@
 {
 	struct the_nilfs *nilfs = sbi->s_nilfs;
 
-	nilfs_mdt_clear(sbi->s_ifile);
 	nilfs_mdt_destroy(sbi->s_ifile);
 	sbi->s_ifile = NULL;
 	down_write(&nilfs->ns_super_sem);
@@ -419,22 +414,6 @@
 	up_write(&nilfs->ns_super_sem);
 }
 
-static int nilfs_mark_recovery_complete(struct nilfs_sb_info *sbi)
-{
-	struct the_nilfs *nilfs = sbi->s_nilfs;
-	int err = 0;
-
-	down_write(&nilfs->ns_sem);
-	if (!(nilfs->ns_mount_state & NILFS_VALID_FS)) {
-		nilfs->ns_mount_state |= NILFS_VALID_FS;
-		err = nilfs_commit_super(sbi, 1);
-		if (likely(!err))
-			printk(KERN_INFO "NILFS: recovery complete.\n");
-	}
-	up_write(&nilfs->ns_sem);
-	return err;
-}
-
 static int nilfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 {
 	struct super_block *sb = dentry->d_sb;
@@ -490,7 +469,7 @@
 	struct nilfs_sb_info *sbi = NILFS_SB(sb);
 
 	if (!nilfs_test_opt(sbi, BARRIER))
-		seq_printf(seq, ",barrier=off");
+		seq_printf(seq, ",nobarrier");
 	if (nilfs_test_opt(sbi, SNAPSHOT))
 		seq_printf(seq, ",cp=%llu",
 			   (unsigned long long int)sbi->s_snapshot_cno);
@@ -500,6 +479,8 @@
 		seq_printf(seq, ",errors=panic");
 	if (nilfs_test_opt(sbi, STRICT_ORDER))
 		seq_printf(seq, ",order=strict");
+	if (nilfs_test_opt(sbi, NORECOVERY))
+		seq_printf(seq, ",norecovery");
 
 	return 0;
 }
@@ -568,7 +549,7 @@
 
 enum {
 	Opt_err_cont, Opt_err_panic, Opt_err_ro,
-	Opt_barrier, Opt_snapshot, Opt_order,
+	Opt_nobarrier, Opt_snapshot, Opt_order, Opt_norecovery,
 	Opt_err,
 };
 
@@ -576,25 +557,13 @@
 	{Opt_err_cont, "errors=continue"},
 	{Opt_err_panic, "errors=panic"},
 	{Opt_err_ro, "errors=remount-ro"},
-	{Opt_barrier, "barrier=%s"},
+	{Opt_nobarrier, "nobarrier"},
 	{Opt_snapshot, "cp=%u"},
 	{Opt_order, "order=%s"},
+	{Opt_norecovery, "norecovery"},
 	{Opt_err, NULL}
 };
 
-static int match_bool(substring_t *s, int *result)
-{
-	int len = s->to - s->from;
-
-	if (strncmp(s->from, "on", len) == 0)
-		*result = 1;
-	else if (strncmp(s->from, "off", len) == 0)
-		*result = 0;
-	else
-		return 1;
-	return 0;
-}
-
 static int parse_options(char *options, struct super_block *sb)
 {
 	struct nilfs_sb_info *sbi = NILFS_SB(sb);
@@ -612,13 +581,8 @@
 
 		token = match_token(p, tokens, args);
 		switch (token) {
-		case Opt_barrier:
-			if (match_bool(&args[0], &option))
-				return 0;
-			if (option)
-				nilfs_set_opt(sbi, BARRIER);
-			else
-				nilfs_clear_opt(sbi, BARRIER);
+		case Opt_nobarrier:
+			nilfs_clear_opt(sbi, BARRIER);
 			break;
 		case Opt_order:
 			if (strcmp(args[0].from, "relaxed") == 0)
@@ -647,6 +611,9 @@
 			sbi->s_snapshot_cno = option;
 			nilfs_set_opt(sbi, SNAPSHOT);
 			break;
+		case Opt_norecovery:
+			nilfs_set_opt(sbi, NORECOVERY);
+			break;
 		default:
 			printk(KERN_ERR
 			       "NILFS: Unrecognized mount option \"%s\"\n", p);
@@ -672,9 +639,7 @@
 	int mnt_count = le16_to_cpu(sbp->s_mnt_count);
 
 	/* nilfs->sem must be locked by the caller. */
-	if (!(nilfs->ns_mount_state & NILFS_VALID_FS)) {
-		printk(KERN_WARNING "NILFS warning: mounting unchecked fs\n");
-	} else if (nilfs->ns_mount_state & NILFS_ERROR_FS) {
+	if (nilfs->ns_mount_state & NILFS_ERROR_FS) {
 		printk(KERN_WARNING
 		       "NILFS warning: mounting fs with errors\n");
 #if 0
@@ -782,11 +747,10 @@
 	sb->s_root = NULL;
 	sb->s_time_gran = 1;
 
-	if (!nilfs_loaded(nilfs)) {
-		err = load_nilfs(nilfs, sbi);
-		if (err)
-			goto failed_sbi;
-	}
+	err = load_nilfs(nilfs, sbi);
+	if (err)
+		goto failed_sbi;
+
 	cno = nilfs_last_cno(nilfs);
 
 	if (sb->s_flags & MS_RDONLY) {
@@ -854,12 +818,6 @@
 		up_write(&nilfs->ns_sem);
 	}
 
-	err = nilfs_mark_recovery_complete(sbi);
-	if (unlikely(err)) {
-		printk(KERN_ERR "NILFS: recovery failed.\n");
-		goto failed_root;
-	}
-
 	down_write(&nilfs->ns_super_sem);
 	if (!nilfs_test_opt(sbi, SNAPSHOT))
 		nilfs->ns_current = sbi;
@@ -867,10 +825,6 @@
 
 	return 0;
 
- failed_root:
-	dput(sb->s_root);
-	sb->s_root = NULL;
-
  failed_segctor:
 	nilfs_detach_segment_constructor(sbi);
 
@@ -915,6 +869,14 @@
 		goto restore_opts;
 	}
 
+	if (!nilfs_valid_fs(nilfs)) {
+		printk(KERN_WARNING "NILFS (device %s): couldn't "
+		       "remount because the filesystem is in an "
+		       "incomplete recovery state.\n", sb->s_id);
+		err = -EINVAL;
+		goto restore_opts;
+	}
+
 	if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
 		goto out;
 	if (*flags & MS_RDONLY) {
diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c
index ad391a8..6241e17 100644
--- a/fs/nilfs2/the_nilfs.c
+++ b/fs/nilfs2/the_nilfs.c
@@ -146,13 +146,9 @@
 
 	might_sleep();
 	if (nilfs_loaded(nilfs)) {
-		nilfs_mdt_clear(nilfs->ns_sufile);
 		nilfs_mdt_destroy(nilfs->ns_sufile);
-		nilfs_mdt_clear(nilfs->ns_cpfile);
 		nilfs_mdt_destroy(nilfs->ns_cpfile);
-		nilfs_mdt_clear(nilfs->ns_dat);
 		nilfs_mdt_destroy(nilfs->ns_dat);
-		/* XXX: how and when to clear nilfs->ns_gc_dat? */
 		nilfs_mdt_destroy(nilfs->ns_gc_dat);
 	}
 	if (nilfs_init(nilfs)) {
@@ -166,7 +162,6 @@
 static int nilfs_load_super_root(struct the_nilfs *nilfs,
 				 struct nilfs_sb_info *sbi, sector_t sr_block)
 {
-	static struct lock_class_key dat_lock_key;
 	struct buffer_head *bh_sr;
 	struct nilfs_super_root *raw_sr;
 	struct nilfs_super_block **sbp = nilfs->ns_sbp;
@@ -187,51 +182,36 @@
 	inode_size = nilfs->ns_inode_size;
 
 	err = -ENOMEM;
-	nilfs->ns_dat = nilfs_mdt_new(nilfs, NULL, NILFS_DAT_INO);
+	nilfs->ns_dat = nilfs_dat_new(nilfs, dat_entry_size);
 	if (unlikely(!nilfs->ns_dat))
 		goto failed;
 
-	nilfs->ns_gc_dat = nilfs_mdt_new(nilfs, NULL, NILFS_DAT_INO);
+	nilfs->ns_gc_dat = nilfs_dat_new(nilfs, dat_entry_size);
 	if (unlikely(!nilfs->ns_gc_dat))
 		goto failed_dat;
 
-	nilfs->ns_cpfile = nilfs_mdt_new(nilfs, NULL, NILFS_CPFILE_INO);
+	nilfs->ns_cpfile = nilfs_cpfile_new(nilfs, checkpoint_size);
 	if (unlikely(!nilfs->ns_cpfile))
 		goto failed_gc_dat;
 
-	nilfs->ns_sufile = nilfs_mdt_new(nilfs, NULL, NILFS_SUFILE_INO);
+	nilfs->ns_sufile = nilfs_sufile_new(nilfs, segment_usage_size);
 	if (unlikely(!nilfs->ns_sufile))
 		goto failed_cpfile;
 
-	err = nilfs_palloc_init_blockgroup(nilfs->ns_dat, dat_entry_size);
-	if (unlikely(err))
-		goto failed_sufile;
-
-	err = nilfs_palloc_init_blockgroup(nilfs->ns_gc_dat, dat_entry_size);
-	if (unlikely(err))
-		goto failed_sufile;
-
-	lockdep_set_class(&NILFS_MDT(nilfs->ns_dat)->mi_sem, &dat_lock_key);
-	lockdep_set_class(&NILFS_MDT(nilfs->ns_gc_dat)->mi_sem, &dat_lock_key);
-
 	nilfs_mdt_set_shadow(nilfs->ns_dat, nilfs->ns_gc_dat);
-	nilfs_mdt_set_entry_size(nilfs->ns_cpfile, checkpoint_size,
-				 sizeof(struct nilfs_cpfile_header));
-	nilfs_mdt_set_entry_size(nilfs->ns_sufile, segment_usage_size,
-				 sizeof(struct nilfs_sufile_header));
 
-	err = nilfs_mdt_read_inode_direct(
-		nilfs->ns_dat, bh_sr, NILFS_SR_DAT_OFFSET(inode_size));
+	err = nilfs_dat_read(nilfs->ns_dat, (void *)bh_sr->b_data +
+			     NILFS_SR_DAT_OFFSET(inode_size));
 	if (unlikely(err))
 		goto failed_sufile;
 
-	err = nilfs_mdt_read_inode_direct(
-		nilfs->ns_cpfile, bh_sr, NILFS_SR_CPFILE_OFFSET(inode_size));
+	err = nilfs_cpfile_read(nilfs->ns_cpfile, (void *)bh_sr->b_data +
+				NILFS_SR_CPFILE_OFFSET(inode_size));
 	if (unlikely(err))
 		goto failed_sufile;
 
-	err = nilfs_mdt_read_inode_direct(
-		nilfs->ns_sufile, bh_sr, NILFS_SR_SUFILE_OFFSET(inode_size));
+	err = nilfs_sufile_read(nilfs->ns_sufile, (void *)bh_sr->b_data +
+				NILFS_SR_SUFILE_OFFSET(inode_size));
 	if (unlikely(err))
 		goto failed_sufile;
 
@@ -281,29 +261,30 @@
 	struct nilfs_recovery_info ri;
 	unsigned int s_flags = sbi->s_super->s_flags;
 	int really_read_only = bdev_read_only(nilfs->ns_bdev);
-	unsigned valid_fs;
-	int err = 0;
+	int valid_fs = nilfs_valid_fs(nilfs);
+	int err;
+
+	if (nilfs_loaded(nilfs)) {
+		if (valid_fs ||
+		    ((s_flags & MS_RDONLY) && nilfs_test_opt(sbi, NORECOVERY)))
+			return 0;
+		printk(KERN_ERR "NILFS: the filesystem is in an incomplete "
+		       "recovery state.\n");
+		return -EINVAL;
+	}
+
+	if (!valid_fs) {
+		printk(KERN_WARNING "NILFS warning: mounting unchecked fs\n");
+		if (s_flags & MS_RDONLY) {
+			printk(KERN_INFO "NILFS: INFO: recovery "
+			       "required for readonly filesystem.\n");
+			printk(KERN_INFO "NILFS: write access will "
+			       "be enabled during recovery.\n");
+		}
+	}
 
 	nilfs_init_recovery_info(&ri);
 
-	down_write(&nilfs->ns_sem);
-	valid_fs = (nilfs->ns_mount_state & NILFS_VALID_FS);
-	up_write(&nilfs->ns_sem);
-
-	if (!valid_fs && (s_flags & MS_RDONLY)) {
-		printk(KERN_INFO "NILFS: INFO: recovery "
-		       "required for readonly filesystem.\n");
-		if (really_read_only) {
-			printk(KERN_ERR "NILFS: write access "
-			       "unavailable, cannot proceed.\n");
-			err = -EROFS;
-			goto failed;
-		}
-		printk(KERN_INFO "NILFS: write access will "
-		       "be enabled during recovery.\n");
-		sbi->s_super->s_flags &= ~MS_RDONLY;
-	}
-
 	err = nilfs_search_super_root(nilfs, sbi, &ri);
 	if (unlikely(err)) {
 		printk(KERN_ERR "NILFS: error searching super root.\n");
@@ -316,19 +297,56 @@
 		goto failed;
 	}
 
-	if (!valid_fs) {
-		err = nilfs_recover_logical_segments(nilfs, sbi, &ri);
-		if (unlikely(err)) {
-			nilfs_mdt_destroy(nilfs->ns_cpfile);
-			nilfs_mdt_destroy(nilfs->ns_sufile);
-			nilfs_mdt_destroy(nilfs->ns_dat);
-			goto failed;
+	if (valid_fs)
+		goto skip_recovery;
+
+	if (s_flags & MS_RDONLY) {
+		if (nilfs_test_opt(sbi, NORECOVERY)) {
+			printk(KERN_INFO "NILFS: norecovery option specified. "
+			       "skipping roll-forward recovery\n");
+			goto skip_recovery;
 		}
-		if (ri.ri_need_recovery == NILFS_RECOVERY_SR_UPDATED)
-			sbi->s_super->s_dirt = 1;
+		if (really_read_only) {
+			printk(KERN_ERR "NILFS: write access "
+			       "unavailable, cannot proceed.\n");
+			err = -EROFS;
+			goto failed_unload;
+		}
+		sbi->s_super->s_flags &= ~MS_RDONLY;
+	} else if (nilfs_test_opt(sbi, NORECOVERY)) {
+		printk(KERN_ERR "NILFS: recovery cancelled because norecovery "
+		       "option was specified for a read/write mount\n");
+		err = -EINVAL;
+		goto failed_unload;
 	}
 
+	err = nilfs_recover_logical_segments(nilfs, sbi, &ri);
+	if (err)
+		goto failed_unload;
+
+	down_write(&nilfs->ns_sem);
+	nilfs->ns_mount_state |= NILFS_VALID_FS;
+	nilfs->ns_sbp[0]->s_state = cpu_to_le16(nilfs->ns_mount_state);
+	err = nilfs_commit_super(sbi, 1);
+	up_write(&nilfs->ns_sem);
+
+	if (err) {
+		printk(KERN_ERR "NILFS: failed to update super block. "
+		       "recovery unfinished.\n");
+		goto failed_unload;
+	}
+	printk(KERN_INFO "NILFS: recovery complete.\n");
+
+ skip_recovery:
 	set_nilfs_loaded(nilfs);
+	nilfs_clear_recovery_info(&ri);
+	sbi->s_super->s_flags = s_flags;
+	return 0;
+
+ failed_unload:
+	nilfs_mdt_destroy(nilfs->ns_cpfile);
+	nilfs_mdt_destroy(nilfs->ns_sufile);
+	nilfs_mdt_destroy(nilfs->ns_dat);
 
  failed:
 	nilfs_clear_recovery_info(&ri);
@@ -632,30 +650,23 @@
 {
 	struct inode *dat = nilfs_dat_inode(nilfs);
 	unsigned long ncleansegs;
-	int err;
 
 	down_read(&NILFS_MDT(dat)->mi_sem);	/* XXX */
-	err = nilfs_sufile_get_ncleansegs(nilfs->ns_sufile, &ncleansegs);
+	ncleansegs = nilfs_sufile_get_ncleansegs(nilfs->ns_sufile);
 	up_read(&NILFS_MDT(dat)->mi_sem);	/* XXX */
-	if (likely(!err))
-		*nblocks = (sector_t)ncleansegs * nilfs->ns_blocks_per_segment;
-	return err;
+	*nblocks = (sector_t)ncleansegs * nilfs->ns_blocks_per_segment;
+	return 0;
 }
 
 int nilfs_near_disk_full(struct the_nilfs *nilfs)
 {
-	struct inode *sufile = nilfs->ns_sufile;
 	unsigned long ncleansegs, nincsegs;
-	int ret;
 
-	ret = nilfs_sufile_get_ncleansegs(sufile, &ncleansegs);
-	if (likely(!ret)) {
-		nincsegs = atomic_read(&nilfs->ns_ndirtyblks) /
-			nilfs->ns_blocks_per_segment + 1;
-		if (ncleansegs <= nilfs->ns_nrsvsegs + nincsegs)
-			ret++;
-	}
-	return ret;
+	ncleansegs = nilfs_sufile_get_ncleansegs(nilfs->ns_sufile);
+	nincsegs = atomic_read(&nilfs->ns_ndirtyblks) /
+		nilfs->ns_blocks_per_segment + 1;
+
+	return ncleansegs <= nilfs->ns_nrsvsegs + nincsegs;
 }
 
 /**
diff --git a/fs/nilfs2/the_nilfs.h b/fs/nilfs2/the_nilfs.h
index 20abd55..589786e 100644
--- a/fs/nilfs2/the_nilfs.h
+++ b/fs/nilfs2/the_nilfs.h
@@ -258,6 +258,16 @@
 		kfree(sbi);
 }
 
+static inline int nilfs_valid_fs(struct the_nilfs *nilfs)
+{
+	unsigned valid_fs;
+
+	down_read(&nilfs->ns_sem);
+	valid_fs = (nilfs->ns_mount_state & NILFS_VALID_FS);
+	up_read(&nilfs->ns_sem);
+	return valid_fs;
+}
+
 static inline void
 nilfs_get_segment_range(struct the_nilfs *nilfs, __u64 segnum,
 			sector_t *seg_start, sector_t *seg_end)
diff --git a/include/acpi/acpi_hest.h b/include/acpi/acpi_hest.h
new file mode 100644
index 0000000..63194d0
--- /dev/null
+++ b/include/acpi/acpi_hest.h
@@ -0,0 +1,12 @@
+#ifndef __ACPI_HEST_H
+#define __ACPI_HEST_H
+
+#include <linux/pci.h>
+
+#ifdef CONFIG_ACPI
+extern int acpi_hest_firmware_first_pci(struct pci_dev *pci);
+#else
+static inline int acpi_hest_firmware_first_pci(struct pci_dev *pci) { return 0; }
+#endif
+
+#endif
diff --git a/include/drm/Kbuild b/include/drm/Kbuild
index b940fdf..cfa6af4 100644
--- a/include/drm/Kbuild
+++ b/include/drm/Kbuild
@@ -8,3 +8,4 @@
 unifdef-y += sis_drm.h
 unifdef-y += savage_drm.h
 unifdef-y += via_drm.h
+unifdef-y += nouveau_drm.h
diff --git a/include/drm/i2c/ch7006.h b/include/drm/i2c/ch7006.h
new file mode 100644
index 0000000..8390b43
--- /dev/null
+++ b/include/drm/i2c/ch7006.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2009 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef __DRM_I2C_CH7006_H__
+#define __DRM_I2C_CH7006_H__
+
+/**
+ * struct ch7006_encoder_params
+ *
+ * Describes how the ch7006 is wired up with the GPU. It should be
+ * used as the @params parameter of its @set_config method.
+ *
+ * See "http://www.chrontel.com/pdf/7006.pdf" for their precise
+ * meaning.
+ */
+struct ch7006_encoder_params {
+	enum {
+		CH7006_FORMAT_RGB16 = 0,
+		CH7006_FORMAT_YCrCb24m16,
+		CH7006_FORMAT_RGB24m16,
+		CH7006_FORMAT_RGB15,
+		CH7006_FORMAT_RGB24m12C,
+		CH7006_FORMAT_RGB24m12I,
+		CH7006_FORMAT_RGB24m8,
+		CH7006_FORMAT_RGB16m8,
+		CH7006_FORMAT_RGB15m8,
+		CH7006_FORMAT_YCrCb24m8,
+	} input_format;
+
+	enum {
+		CH7006_CLOCK_SLAVE = 0,
+		CH7006_CLOCK_MASTER,
+	} clock_mode;
+
+	enum {
+		CH7006_CLOCK_EDGE_NEG = 0,
+		CH7006_CLOCK_EDGE_POS,
+	} clock_edge;
+
+	int xcm, pcm;
+
+	enum {
+		CH7006_SYNC_SLAVE = 0,
+		CH7006_SYNC_MASTER,
+	} sync_direction;
+
+	enum {
+		CH7006_SYNC_SEPARATED = 0,
+		CH7006_SYNC_EMBEDDED,
+	} sync_encoding;
+
+	enum {
+		CH7006_POUT_1_8V = 0,
+		CH7006_POUT_3_3V,
+	} pout_level;
+
+	enum {
+		CH7006_ACTIVE_HSYNC = 0,
+		CH7006_ACTIVE_DSTART,
+	} active_detect;
+};
+
+#endif
diff --git a/include/drm/nouveau_drm.h b/include/drm/nouveau_drm.h
new file mode 100644
index 0000000..1e67c44
--- /dev/null
+++ b/include/drm/nouveau_drm.h
@@ -0,0 +1,220 @@
+/*
+ * Copyright 2005 Stephane Marchesin.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __NOUVEAU_DRM_H__
+#define __NOUVEAU_DRM_H__
+
+#define NOUVEAU_DRM_HEADER_PATCHLEVEL 15
+
+struct drm_nouveau_channel_alloc {
+	uint32_t     fb_ctxdma_handle;
+	uint32_t     tt_ctxdma_handle;
+
+	int          channel;
+
+	/* Notifier memory */
+	uint32_t     notifier_handle;
+
+	/* DRM-enforced subchannel assignments */
+	struct {
+		uint32_t handle;
+		uint32_t grclass;
+	} subchan[8];
+	uint32_t nr_subchan;
+};
+
+struct drm_nouveau_channel_free {
+	int channel;
+};
+
+struct drm_nouveau_grobj_alloc {
+	int      channel;
+	uint32_t handle;
+	int      class;
+};
+
+struct drm_nouveau_notifierobj_alloc {
+	uint32_t channel;
+	uint32_t handle;
+	uint32_t size;
+	uint32_t offset;
+};
+
+struct drm_nouveau_gpuobj_free {
+	int      channel;
+	uint32_t handle;
+};
+
+/* FIXME : maybe unify {GET,SET}PARAMs */
+#define NOUVEAU_GETPARAM_PCI_VENDOR      3
+#define NOUVEAU_GETPARAM_PCI_DEVICE      4
+#define NOUVEAU_GETPARAM_BUS_TYPE        5
+#define NOUVEAU_GETPARAM_FB_PHYSICAL     6
+#define NOUVEAU_GETPARAM_AGP_PHYSICAL    7
+#define NOUVEAU_GETPARAM_FB_SIZE         8
+#define NOUVEAU_GETPARAM_AGP_SIZE        9
+#define NOUVEAU_GETPARAM_PCI_PHYSICAL    10
+#define NOUVEAU_GETPARAM_CHIPSET_ID      11
+#define NOUVEAU_GETPARAM_VM_VRAM_BASE    12
+struct drm_nouveau_getparam {
+	uint64_t param;
+	uint64_t value;
+};
+
+struct drm_nouveau_setparam {
+	uint64_t param;
+	uint64_t value;
+};
+
+#define NOUVEAU_GEM_DOMAIN_CPU       (1 << 0)
+#define NOUVEAU_GEM_DOMAIN_VRAM      (1 << 1)
+#define NOUVEAU_GEM_DOMAIN_GART      (1 << 2)
+#define NOUVEAU_GEM_DOMAIN_MAPPABLE  (1 << 3)
+
+struct drm_nouveau_gem_info {
+	uint32_t handle;
+	uint32_t domain;
+	uint64_t size;
+	uint64_t offset;
+	uint64_t map_handle;
+	uint32_t tile_mode;
+	uint32_t tile_flags;
+};
+
+struct drm_nouveau_gem_new {
+	struct drm_nouveau_gem_info info;
+	uint32_t channel_hint;
+	uint32_t align;
+};
+
+struct drm_nouveau_gem_pushbuf_bo {
+	uint64_t user_priv;
+	uint32_t handle;
+	uint32_t read_domains;
+	uint32_t write_domains;
+	uint32_t valid_domains;
+	uint32_t presumed_ok;
+	uint32_t presumed_domain;
+	uint64_t presumed_offset;
+};
+
+#define NOUVEAU_GEM_RELOC_LOW  (1 << 0)
+#define NOUVEAU_GEM_RELOC_HIGH (1 << 1)
+#define NOUVEAU_GEM_RELOC_OR   (1 << 2)
+struct drm_nouveau_gem_pushbuf_reloc {
+	uint32_t bo_index;
+	uint32_t reloc_index;
+	uint32_t flags;
+	uint32_t data;
+	uint32_t vor;
+	uint32_t tor;
+};
+
+#define NOUVEAU_GEM_MAX_BUFFERS 1024
+#define NOUVEAU_GEM_MAX_RELOCS 1024
+
+struct drm_nouveau_gem_pushbuf {
+	uint32_t channel;
+	uint32_t nr_dwords;
+	uint32_t nr_buffers;
+	uint32_t nr_relocs;
+	uint64_t dwords;
+	uint64_t buffers;
+	uint64_t relocs;
+};
+
+struct drm_nouveau_gem_pushbuf_call {
+	uint32_t channel;
+	uint32_t handle;
+	uint32_t offset;
+	uint32_t nr_buffers;
+	uint32_t nr_relocs;
+	uint32_t nr_dwords;
+	uint64_t buffers;
+	uint64_t relocs;
+	uint32_t suffix0;
+	uint32_t suffix1;
+	/* below only accessed for CALL2 */
+	uint64_t vram_available;
+	uint64_t gart_available;
+};
+
+struct drm_nouveau_gem_pin {
+	uint32_t handle;
+	uint32_t domain;
+	uint64_t offset;
+};
+
+struct drm_nouveau_gem_unpin {
+	uint32_t handle;
+};
+
+#define NOUVEAU_GEM_CPU_PREP_NOWAIT                                  0x00000001
+#define NOUVEAU_GEM_CPU_PREP_NOBLOCK                                 0x00000002
+#define NOUVEAU_GEM_CPU_PREP_WRITE                                   0x00000004
+struct drm_nouveau_gem_cpu_prep {
+	uint32_t handle;
+	uint32_t flags;
+};
+
+struct drm_nouveau_gem_cpu_fini {
+	uint32_t handle;
+};
+
+struct drm_nouveau_gem_tile {
+	uint32_t handle;
+	uint32_t offset;
+	uint32_t size;
+	uint32_t tile_mode;
+	uint32_t tile_flags;
+};
+
+enum nouveau_bus_type {
+	NV_AGP     = 0,
+	NV_PCI     = 1,
+	NV_PCIE    = 2,
+};
+
+struct drm_nouveau_sarea {
+};
+
+#define DRM_NOUVEAU_CARD_INIT          0x00
+#define DRM_NOUVEAU_GETPARAM           0x01
+#define DRM_NOUVEAU_SETPARAM           0x02
+#define DRM_NOUVEAU_CHANNEL_ALLOC      0x03
+#define DRM_NOUVEAU_CHANNEL_FREE       0x04
+#define DRM_NOUVEAU_GROBJ_ALLOC        0x05
+#define DRM_NOUVEAU_NOTIFIEROBJ_ALLOC  0x06
+#define DRM_NOUVEAU_GPUOBJ_FREE        0x07
+#define DRM_NOUVEAU_GEM_NEW            0x40
+#define DRM_NOUVEAU_GEM_PUSHBUF        0x41
+#define DRM_NOUVEAU_GEM_PUSHBUF_CALL   0x42
+#define DRM_NOUVEAU_GEM_PIN            0x43 /* !KMS only */
+#define DRM_NOUVEAU_GEM_UNPIN          0x44 /* !KMS only */
+#define DRM_NOUVEAU_GEM_CPU_PREP       0x45
+#define DRM_NOUVEAU_GEM_CPU_FINI       0x46
+#define DRM_NOUVEAU_GEM_INFO           0x47
+#define DRM_NOUVEAU_GEM_PUSHBUF_CALL2  0x48
+
+#endif /* __NOUVEAU_DRM_H__ */
diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h
index 4fd4985..81eb9f4 100644
--- a/include/drm/ttm/ttm_bo_api.h
+++ b/include/drm/ttm/ttm_bo_api.h
@@ -308,7 +308,7 @@
 extern int ttm_bo_wait(struct ttm_buffer_object *bo, bool lazy,
 		       bool interruptible, bool no_wait);
 /**
- * ttm_buffer_object_validate
+ * ttm_bo_validate
  *
  * @bo: The buffer object.
  * @placement: Proposed placement for the buffer object.
@@ -323,9 +323,9 @@
  * -EBUSY if no_wait is true and buffer busy.
  * -ERESTARTSYS if interrupted by a signal.
  */
-extern int ttm_buffer_object_validate(struct ttm_buffer_object *bo,
-					struct ttm_placement *placement,
-					bool interruptible, bool no_wait);
+extern int ttm_bo_validate(struct ttm_buffer_object *bo,
+				struct ttm_placement *placement,
+				bool interruptible, bool no_wait);
 
 /**
  * ttm_bo_unref
@@ -362,7 +362,7 @@
 extern void ttm_bo_synccpu_write_release(struct ttm_buffer_object *bo);
 
 /**
- * ttm_buffer_object_init
+ * ttm_bo_init
  *
  * @bdev: Pointer to a ttm_bo_device struct.
  * @bo: Pointer to a ttm_buffer_object to be initialized.
@@ -393,17 +393,17 @@
  * -ERESTARTSYS: Interrupted by signal while sleeping waiting for resources.
  */
 
-extern int ttm_buffer_object_init(struct ttm_bo_device *bdev,
-				  struct ttm_buffer_object *bo,
-				  unsigned long size,
-				  enum ttm_bo_type type,
-				  uint32_t flags,
-				  uint32_t page_alignment,
-				  unsigned long buffer_start,
-				  bool interrubtible,
-				  struct file *persistant_swap_storage,
-				  size_t acc_size,
-				  void (*destroy) (struct ttm_buffer_object *));
+extern int ttm_bo_init(struct ttm_bo_device *bdev,
+			struct ttm_buffer_object *bo,
+			unsigned long size,
+			enum ttm_bo_type type,
+			struct ttm_placement *placement,
+			uint32_t page_alignment,
+			unsigned long buffer_start,
+			bool interrubtible,
+			struct file *persistant_swap_storage,
+			size_t acc_size,
+			void (*destroy) (struct ttm_buffer_object *));
 /**
  * ttm_bo_synccpu_object_init
  *
@@ -424,40 +424,37 @@
  * GEM user interface.
  * @p_bo: On successful completion *p_bo points to the created object.
  *
- * This function allocates a ttm_buffer_object, and then calls
- * ttm_buffer_object_init on that object.
- * The destroy function is set to kfree().
+ * This function allocates a ttm_buffer_object, and then calls ttm_bo_init
+ * on that object. The destroy function is set to kfree().
  * Returns
  * -ENOMEM: Out of memory.
  * -EINVAL: Invalid placement flags.
  * -ERESTARTSYS: Interrupted by signal while waiting for resources.
  */
 
-extern int ttm_buffer_object_create(struct ttm_bo_device *bdev,
-				    unsigned long size,
-				    enum ttm_bo_type type,
-				    uint32_t flags,
-				    uint32_t page_alignment,
-				    unsigned long buffer_start,
-				    bool interruptible,
-				    struct file *persistant_swap_storage,
-				    struct ttm_buffer_object **p_bo);
+extern int ttm_bo_create(struct ttm_bo_device *bdev,
+				unsigned long size,
+				enum ttm_bo_type type,
+				struct ttm_placement *placement,
+				uint32_t page_alignment,
+				unsigned long buffer_start,
+				bool interruptible,
+				struct file *persistant_swap_storage,
+				struct ttm_buffer_object **p_bo);
 
 /**
  * ttm_bo_check_placement
  *
- * @bo: the buffer object.
- * @set_flags: placement flags to set.
- * @clr_flags: placement flags to clear.
+ * @bo:		the buffer object.
+ * @placement:	placements
  *
  * Performs minimal validity checking on an intended change of
  * placement flags.
  * Returns
  * -EINVAL: Intended change is invalid or not allowed.
  */
-
 extern int ttm_bo_check_placement(struct ttm_buffer_object *bo,
-				  uint32_t set_flags, uint32_t clr_flags);
+					struct ttm_placement *placement);
 
 /**
  * ttm_bo_init_mm
diff --git a/include/linux/nilfs2_fs.h b/include/linux/nilfs2_fs.h
index ce52040..3fe02cf 100644
--- a/include/linux/nilfs2_fs.h
+++ b/include/linux/nilfs2_fs.h
@@ -151,6 +151,8 @@
 #define NILFS_MOUNT_BARRIER		0x1000  /* Use block barriers */
 #define NILFS_MOUNT_STRICT_ORDER	0x2000  /* Apply strict in-order
 						   semantics also for data */
+#define NILFS_MOUNT_NORECOVERY		0x4000  /* Disable write access during
+						   mount-time recovery */
 
 
 /**
@@ -403,6 +405,28 @@
 #define NILFS_SS_GC     0x0010  /* segment written for cleaner operation */
 
 /**
+ * struct nilfs_btree_node - B-tree node
+ * @bn_flags: flags
+ * @bn_level: level
+ * @bn_nchildren: number of children
+ * @bn_pad: padding
+ */
+struct nilfs_btree_node {
+	__u8 bn_flags;
+	__u8 bn_level;
+	__le16 bn_nchildren;
+	__le32 bn_pad;
+};
+
+/* flags */
+#define NILFS_BTREE_NODE_ROOT   0x01
+
+/* level */
+#define NILFS_BTREE_LEVEL_DATA          0
+#define NILFS_BTREE_LEVEL_NODE_MIN      (NILFS_BTREE_LEVEL_DATA + 1)
+#define NILFS_BTREE_LEVEL_MAX           14
+
+/**
  * struct nilfs_palloc_group_desc - block group descriptor
  * @pg_nfrees: number of free entries in block group
  */
diff --git a/include/linux/pci.h b/include/linux/pci.h
index f5c7cd3..04771b9 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -218,6 +218,7 @@
 	unsigned int	class;		/* 3 bytes: (base,sub,prog-if) */
 	u8		revision;	/* PCI revision, low byte of class word */
 	u8		hdr_type;	/* PCI header type (`multi' flag masked out) */
+	u8		pcie_cap;	/* PCI-E capability offset */
 	u8		pcie_type;	/* PCI-E device/port type */
 	u8		rom_base_reg;	/* which config register controls the ROM */
 	u8		pin;  		/* which interrupt pin this device uses */
@@ -280,6 +281,7 @@
 	unsigned int	is_virtfn:1;
 	unsigned int	reset_fn:1;
 	unsigned int    is_hotplug_bridge:1;
+	unsigned int    aer_firmware_first:1;
 	pci_dev_flags_t dev_flags;
 	atomic_t	enable_cnt;	/* pci_enable_device has been called */
 
@@ -635,7 +637,13 @@
 				unsigned int ss_vendor, unsigned int ss_device,
 				struct pci_dev *from);
 struct pci_dev *pci_get_slot(struct pci_bus *bus, unsigned int devfn);
-struct pci_dev *pci_get_bus_and_slot(unsigned int bus, unsigned int devfn);
+struct pci_dev *pci_get_domain_bus_and_slot(int domain, unsigned int bus,
+					    unsigned int devfn);
+static inline struct pci_dev *pci_get_bus_and_slot(unsigned int bus,
+						   unsigned int devfn)
+{
+	return pci_get_domain_bus_and_slot(0, bus, devfn);
+}
 struct pci_dev *pci_get_class(unsigned int class, struct pci_dev *from);
 int pci_dev_present(const struct pci_device_id *ids);
 
@@ -701,6 +709,7 @@
 void pci_set_master(struct pci_dev *dev);
 void pci_clear_master(struct pci_dev *dev);
 int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state);
+int pci_set_cacheline_size(struct pci_dev *dev);
 #define HAVE_PCI_SET_MWI
 int __must_check pci_set_mwi(struct pci_dev *dev);
 int pci_try_set_mwi(struct pci_dev *dev);
@@ -1246,6 +1255,8 @@
 
 extern unsigned long pci_cardbus_io_size;
 extern unsigned long pci_cardbus_mem_size;
+extern u8 pci_dfl_cache_line_size;
+extern u8 pci_cache_line_size;
 
 extern unsigned long pci_hotplug_io_size;
 extern unsigned long pci_hotplug_mem_size;
@@ -1290,5 +1301,34 @@
 extern void pci_hp_remove_module_link(struct pci_slot *pci_slot);
 #endif
 
+/**
+ * pci_pcie_cap - get the saved PCIe capability offset
+ * @dev: PCI device
+ *
+ * PCIe capability offset is calculated at PCI device initialization
+ * time and saved in the data structure. This function returns saved
+ * PCIe capability offset. Using this instead of pci_find_capability()
+ * reduces unnecessary search in the PCI configuration space. If you
+ * need to calculate PCIe capability offset from raw device for some
+ * reasons, please use pci_find_capability() instead.
+ */
+static inline int pci_pcie_cap(struct pci_dev *dev)
+{
+	return dev->pcie_cap;
+}
+
+/**
+ * pci_is_pcie - check if the PCI device is PCI Express capable
+ * @dev: PCI device
+ *
+ * Retrun true if the PCI device is PCI Express capable, false otherwise.
+ */
+static inline bool pci_is_pcie(struct pci_dev *dev)
+{
+	return !!pci_pcie_cap(dev);
+}
+
+void pci_request_acs(void);
+
 #endif /* __KERNEL__ */
 #endif /* LINUX_PCI_H */
diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h
index dd0bed4..9f2ad0a 100644
--- a/include/linux/pci_regs.h
+++ b/include/linux/pci_regs.h
@@ -365,6 +365,11 @@
 #define  PCI_X_STATUS_266MHZ	0x40000000	/* 266 MHz capable */
 #define  PCI_X_STATUS_533MHZ	0x80000000	/* 533 MHz capable */
 
+/* PCI Bridge Subsystem ID registers */
+
+#define PCI_SSVID_VENDOR_ID     4	/* PCI-Bridge subsystem vendor id register */
+#define PCI_SSVID_DEVICE_ID     6	/* PCI-Bridge subsystem device id register */
+
 /* PCI Express capability registers */
 
 #define PCI_EXP_FLAGS		2	/* Capabilities register */
@@ -502,6 +507,7 @@
 #define PCI_EXT_CAP_ID_VC	2
 #define PCI_EXT_CAP_ID_DSN	3
 #define PCI_EXT_CAP_ID_PWR	4
+#define PCI_EXT_CAP_ID_ACS	13
 #define PCI_EXT_CAP_ID_ARI	14
 #define PCI_EXT_CAP_ID_ATS	15
 #define PCI_EXT_CAP_ID_SRIOV	16
@@ -662,4 +668,16 @@
 #define  PCI_SRIOV_VFM_MO	0x2	/* Active.MigrateOut */
 #define  PCI_SRIOV_VFM_AV	0x3	/* Active.Available */
 
+/* Access Control Service */
+#define PCI_ACS_CAP		0x04	/* ACS Capability Register */
+#define  PCI_ACS_SV		0x01	/* Source Validation */
+#define  PCI_ACS_TB		0x02	/* Translation Blocking */
+#define  PCI_ACS_RR		0x04	/* P2P Request Redirect */
+#define  PCI_ACS_CR		0x08	/* P2P Completion Redirect */
+#define  PCI_ACS_UF		0x10	/* Upstream Forwarding */
+#define  PCI_ACS_EC		0x20	/* P2P Egress Control */
+#define  PCI_ACS_DT		0x40	/* Direct Translated P2P */
+#define PCI_ACS_CTRL		0x06	/* ACS Control Register */
+#define PCI_ACS_EGRESS_CTL_V	0x08	/* ACS Egress Control Vector */
+
 #endif /* LINUX_PCI_REGS_H */
diff --git a/include/linux/pcieport_if.h b/include/linux/pcieport_if.h
index b4c7954..6775532 100644
--- a/include/linux/pcieport_if.h
+++ b/include/linux/pcieport_if.h
@@ -10,10 +10,7 @@
 #define _PCIEPORT_IF_H_
 
 /* Port Type */
-#define PCIE_RC_PORT			4	/* Root port of RC */
-#define PCIE_SW_UPSTREAM_PORT		5	/* Upstream port of Switch */
-#define PCIE_SW_DOWNSTREAM_PORT		6	/* Downstream port of Switch */
-#define PCIE_ANY_PORT			7
+#define PCIE_ANY_PORT			(~0)
 
 /* Service Type */
 #define PCIE_PORT_SERVICE_PME_SHIFT	0	/* Power Management Event */
@@ -25,17 +22,6 @@
 #define PCIE_PORT_SERVICE_VC_SHIFT	3	/* Virtual Channel */
 #define PCIE_PORT_SERVICE_VC		(1 << PCIE_PORT_SERVICE_VC_SHIFT)
 
-/* Root/Upstream/Downstream Port's Interrupt Mode */
-#define PCIE_PORT_NO_IRQ		(-1)
-#define PCIE_PORT_INTx_MODE		0
-#define PCIE_PORT_MSI_MODE		1
-#define PCIE_PORT_MSIX_MODE		2
-
-struct pcie_port_data {
-	int port_type;		/* Type of the port */
-	int port_irq_mode;	/* [0:INTx | 1:MSI | 2:MSI-X] */
-};
-
 struct pcie_device {
 	int 		irq;	    /* Service IRQ/MSI/MSI-X Vector */
 	struct pci_dev *port;	    /* Root/Upstream/Downstream Port */
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index bc70c58..939a615 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -834,4 +834,8 @@
 asmlinkage long sys_perf_event_open(
 		struct perf_event_attr __user *attr_uptr,
 		pid_t pid, int cpu, int group_fd, unsigned long flags);
+
+asmlinkage long sys_mmap_pgoff(unsigned long addr, unsigned long len,
+			unsigned long prot, unsigned long flags,
+			unsigned long fd, unsigned long pgoff);
 #endif
diff --git a/include/xen/xen.h b/include/xen/xen.h
new file mode 100644
index 0000000..a164024
--- /dev/null
+++ b/include/xen/xen.h
@@ -0,0 +1,32 @@
+#ifndef _XEN_XEN_H
+#define _XEN_XEN_H
+
+enum xen_domain_type {
+	XEN_NATIVE,		/* running on bare hardware    */
+	XEN_PV_DOMAIN,		/* running in a PV domain      */
+	XEN_HVM_DOMAIN,		/* running in a Xen hvm domain */
+};
+
+#ifdef CONFIG_XEN
+extern enum xen_domain_type xen_domain_type;
+#else
+#define xen_domain_type		XEN_NATIVE
+#endif
+
+#define xen_domain()		(xen_domain_type != XEN_NATIVE)
+#define xen_pv_domain()		(xen_domain() &&			\
+				 xen_domain_type == XEN_PV_DOMAIN)
+#define xen_hvm_domain()	(xen_domain() &&			\
+				 xen_domain_type == XEN_HVM_DOMAIN)
+
+#ifdef CONFIG_XEN_DOM0
+#include <xen/interface/xen.h>
+#include <asm/xen/hypervisor.h>
+
+#define xen_initial_domain()	(xen_pv_domain() && \
+				 xen_start_info->flags & SIF_INITDOMAIN)
+#else  /* !CONFIG_XEN_DOM0 */
+#define xen_initial_domain()	(0)
+#endif	/* CONFIG_XEN_DOM0 */
+
+#endif	/* _XEN_XEN_H */
diff --git a/ipc/shm.c b/ipc/shm.c
index 464694e..11bec62 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -290,28 +290,28 @@
 	unsigned long flags)
 {
 	struct shm_file_data *sfd = shm_file_data(file);
-	return get_unmapped_area(sfd->file, addr, len, pgoff, flags);
-}
-
-int is_file_shm_hugepages(struct file *file)
-{
-	int ret = 0;
-
-	if (file->f_op == &shm_file_operations) {
-		struct shm_file_data *sfd;
-		sfd = shm_file_data(file);
-		ret = is_file_hugepages(sfd->file);
-	}
-	return ret;
+	return sfd->file->f_op->get_unmapped_area(sfd->file, addr, len,
+						pgoff, flags);
 }
 
 static const struct file_operations shm_file_operations = {
 	.mmap		= shm_mmap,
 	.fsync		= shm_fsync,
 	.release	= shm_release,
+};
+
+static const struct file_operations shm_file_operations_huge = {
+	.mmap		= shm_mmap,
+	.fsync		= shm_fsync,
+	.release	= shm_release,
 	.get_unmapped_area	= shm_get_unmapped_area,
 };
 
+int is_file_shm_hugepages(struct file *file)
+{
+	return file->f_op == &shm_file_operations_huge;
+}
+
 static const struct vm_operations_struct shm_vm_ops = {
 	.open	= shm_open,	/* callback for a new vm-area open */
 	.close	= shm_close,	/* callback for when the vm-area is released */
@@ -889,7 +889,10 @@
 	if (!sfd)
 		goto out_put_dentry;
 
-	file = alloc_file(path.mnt, path.dentry, f_mode, &shm_file_operations);
+	file = alloc_file(path.mnt, path.dentry, f_mode,
+			is_file_hugepages(shp->shm_file) ?
+				&shm_file_operations_huge :
+				&shm_file_operations);
 	if (!file)
 		goto out_free;
 	ima_counts_get(file);
diff --git a/kernel/resource.c b/kernel/resource.c
index fb11a58..dc15686 100644
--- a/kernel/resource.c
+++ b/kernel/resource.c
@@ -308,35 +308,37 @@
 			 void *alignf_data)
 {
 	struct resource *this = root->child;
+	resource_size_t start, end;
 
-	new->start = root->start;
+	start = root->start;
 	/*
 	 * Skip past an allocated resource that starts at 0, since the assignment
 	 * of this->start - 1 to new->end below would cause an underflow.
 	 */
 	if (this && this->start == 0) {
-		new->start = this->end + 1;
+		start = this->end + 1;
 		this = this->sibling;
 	}
 	for(;;) {
 		if (this)
-			new->end = this->start - 1;
+			end = this->start - 1;
 		else
-			new->end = root->end;
-		if (new->start < min)
-			new->start = min;
-		if (new->end > max)
-			new->end = max;
-		new->start = ALIGN(new->start, align);
+			end = root->end;
+		if (start < min)
+			start = min;
+		if (end > max)
+			end = max;
+		start = ALIGN(start, align);
 		if (alignf)
 			alignf(alignf_data, new, size, align);
-		if (new->start < new->end && new->end - new->start >= size - 1) {
-			new->end = new->start + size - 1;
+		if (start < end && end - start >= size - 1) {
+			new->start = start;
+			new->end = start + size - 1;
 			return 0;
 		}
 		if (!this)
 			break;
-		new->start = this->end + 1;
+		start = this->end + 1;
 		this = this->sibling;
 	}
 	return -EBUSY;
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 33bed5e..6438cd5 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -595,37 +595,89 @@
 }
 
 static char *resource_string(char *buf, char *end, struct resource *res,
-				struct printf_spec spec)
+				struct printf_spec spec, const char *fmt)
 {
 #ifndef IO_RSRC_PRINTK_SIZE
-#define IO_RSRC_PRINTK_SIZE	4
+#define IO_RSRC_PRINTK_SIZE	6
 #endif
 
 #ifndef MEM_RSRC_PRINTK_SIZE
-#define MEM_RSRC_PRINTK_SIZE	8
+#define MEM_RSRC_PRINTK_SIZE	10
 #endif
-	struct printf_spec num_spec = {
+	struct printf_spec hex_spec = {
 		.base = 16,
 		.precision = -1,
 		.flags = SPECIAL | SMALL | ZEROPAD,
 	};
-	/* room for the actual numbers, the two "0x", -, [, ] and the final zero */
-	char sym[4*sizeof(resource_size_t) + 8];
-	char *p = sym, *pend = sym + sizeof(sym);
-	int size = -1;
+	struct printf_spec dec_spec = {
+		.base = 10,
+		.precision = -1,
+		.flags = 0,
+	};
+	struct printf_spec str_spec = {
+		.field_width = -1,
+		.precision = 10,
+		.flags = LEFT,
+	};
+	struct printf_spec flag_spec = {
+		.base = 16,
+		.precision = -1,
+		.flags = SPECIAL | SMALL,
+	};
 
-	if (res->flags & IORESOURCE_IO)
+	/* 32-bit res (sizeof==4): 10 chars in dec, 10 in hex ("0x" + 8)
+	 * 64-bit res (sizeof==8): 20 chars in dec, 18 in hex ("0x" + 16) */
+#define RSRC_BUF_SIZE		((2 * sizeof(resource_size_t)) + 4)
+#define FLAG_BUF_SIZE		(2 * sizeof(res->flags))
+#define DECODED_BUF_SIZE	sizeof("[mem - 64bit pref disabled]")
+#define RAW_BUF_SIZE		sizeof("[mem - flags 0x]")
+	char sym[max(2*RSRC_BUF_SIZE + DECODED_BUF_SIZE,
+		     2*RSRC_BUF_SIZE + FLAG_BUF_SIZE + RAW_BUF_SIZE)];
+
+	char *p = sym, *pend = sym + sizeof(sym);
+	int size = -1, addr = 0;
+	int decode = (fmt[0] == 'R') ? 1 : 0;
+
+	if (res->flags & IORESOURCE_IO) {
 		size = IO_RSRC_PRINTK_SIZE;
-	else if (res->flags & IORESOURCE_MEM)
+		addr = 1;
+	} else if (res->flags & IORESOURCE_MEM) {
 		size = MEM_RSRC_PRINTK_SIZE;
+		addr = 1;
+	}
 
 	*p++ = '[';
-	num_spec.field_width = size;
-	p = number(p, pend, res->start, num_spec);
-	*p++ = '-';
-	p = number(p, pend, res->end, num_spec);
+	if (res->flags & IORESOURCE_IO)
+		p = string(p, pend, "io  ", str_spec);
+	else if (res->flags & IORESOURCE_MEM)
+		p = string(p, pend, "mem ", str_spec);
+	else if (res->flags & IORESOURCE_IRQ)
+		p = string(p, pend, "irq ", str_spec);
+	else if (res->flags & IORESOURCE_DMA)
+		p = string(p, pend, "dma ", str_spec);
+	else {
+		p = string(p, pend, "??? ", str_spec);
+		decode = 0;
+	}
+	hex_spec.field_width = size;
+	p = number(p, pend, res->start, addr ? hex_spec : dec_spec);
+	if (res->start != res->end) {
+		*p++ = '-';
+		p = number(p, pend, res->end, addr ? hex_spec : dec_spec);
+	}
+	if (decode) {
+		if (res->flags & IORESOURCE_MEM_64)
+			p = string(p, pend, " 64bit", str_spec);
+		if (res->flags & IORESOURCE_PREFETCH)
+			p = string(p, pend, " pref", str_spec);
+		if (res->flags & IORESOURCE_DISABLED)
+			p = string(p, pend, " disabled", str_spec);
+	} else {
+		p = string(p, pend, " flags ", str_spec);
+		p = number(p, pend, res->flags, flag_spec);
+	}
 	*p++ = ']';
-	*p = 0;
+	*p = '\0';
 
 	return string(buf, end, sym, spec);
 }
@@ -801,8 +853,8 @@
  * - 'f' For simple symbolic function names without offset
  * - 'S' For symbolic direct pointers with offset
  * - 's' For symbolic direct pointers without offset
- * - 'R' For a struct resource pointer, it prints the range of
- *       addresses (not the name nor the flags)
+ * - 'R' For decoded struct resource, e.g., [mem 0x0-0x1f 64bit pref]
+ * - 'r' For raw struct resource, e.g., [mem 0x0-0x1f flags 0x201]
  * - 'M' For a 6-byte MAC address, it prints the address in the
  *       usual colon-separated hex notation
  * - 'm' For a 6-byte MAC address, it prints the hex address without colons
@@ -833,7 +885,8 @@
 	case 'S':
 		return symbol_string(buf, end, ptr, spec, *fmt);
 	case 'R':
-		return resource_string(buf, end, ptr, spec);
+	case 'r':
+		return resource_string(buf, end, ptr, spec, fmt);
 	case 'M':			/* Colon separated: 00:01:02:03:04:05 */
 	case 'm':			/* Contiguous: 000102030405 */
 		return mac_address_string(buf, end, ptr, spec, fmt);
diff --git a/mm/mmap.c b/mm/mmap.c
index 292ddc3..ed70a68 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -931,13 +931,9 @@
 	if (!(flags & MAP_FIXED))
 		addr = round_hint_to_min(addr);
 
-	error = arch_mmap_check(addr, len, flags);
-	if (error)
-		return error;
-
 	/* Careful about overflows.. */
 	len = PAGE_ALIGN(len);
-	if (!len || len > TASK_SIZE)
+	if (!len)
 		return -ENOMEM;
 
 	/* offset overflow? */
@@ -948,24 +944,6 @@
 	if (mm->map_count > sysctl_max_map_count)
 		return -ENOMEM;
 
-	if (flags & MAP_HUGETLB) {
-		struct user_struct *user = NULL;
-		if (file)
-			return -EINVAL;
-
-		/*
-		 * VM_NORESERVE is used because the reservations will be
-		 * taken when vm_ops->mmap() is called
-		 * A dummy user value is used because we are not locking
-		 * memory so no accounting is necessary
-		 */
-		len = ALIGN(len, huge_page_size(&default_hstate));
-		file = hugetlb_file_setup(HUGETLB_ANON_FILE, len, VM_NORESERVE,
-						&user, HUGETLB_ANONHUGE_INODE);
-		if (IS_ERR(file))
-			return PTR_ERR(file);
-	}
-
 	/* Obtain the address to map to. we verify (or select) it and ensure
 	 * that it represents a valid section of the address space.
 	 */
@@ -1455,6 +1433,14 @@
 	unsigned long (*get_area)(struct file *, unsigned long,
 				  unsigned long, unsigned long, unsigned long);
 
+	unsigned long error = arch_mmap_check(addr, len, flags);
+	if (error)
+		return error;
+
+	/* Careful about overflows.. */
+	if (len > TASK_SIZE)
+		return -ENOMEM;
+
 	get_area = current->mm->get_unmapped_area;
 	if (file && file->f_op && file->f_op->get_unmapped_area)
 		get_area = file->f_op->get_unmapped_area;
@@ -1999,20 +1985,14 @@
 	if (!len)
 		return addr;
 
-	if ((addr + len) > TASK_SIZE || (addr + len) < addr)
-		return -EINVAL;
-
-	if (is_hugepage_only_range(mm, addr, len))
-		return -EINVAL;
-
 	error = security_file_mmap(NULL, 0, 0, 0, addr, 1);
 	if (error)
 		return error;
 
 	flags = VM_DATA_DEFAULT_FLAGS | VM_ACCOUNT | mm->def_flags;
 
-	error = arch_mmap_check(addr, len, flags);
-	if (error)
+	error = get_unmapped_area(NULL, addr, len, 0, MAP_FIXED);
+	if (error & ~PAGE_MASK)
 		return error;
 
 	/*
diff --git a/mm/mremap.c b/mm/mremap.c
index 97bff25..8451908 100644
--- a/mm/mremap.c
+++ b/mm/mremap.c
@@ -261,6 +261,137 @@
 	return new_addr;
 }
 
+static struct vm_area_struct *vma_to_resize(unsigned long addr,
+	unsigned long old_len, unsigned long new_len, unsigned long *p)
+{
+	struct mm_struct *mm = current->mm;
+	struct vm_area_struct *vma = find_vma(mm, addr);
+
+	if (!vma || vma->vm_start > addr)
+		goto Efault;
+
+	if (is_vm_hugetlb_page(vma))
+		goto Einval;
+
+	/* We can't remap across vm area boundaries */
+	if (old_len > vma->vm_end - addr)
+		goto Efault;
+
+	if (vma->vm_flags & (VM_DONTEXPAND | VM_PFNMAP)) {
+		if (new_len > old_len)
+			goto Efault;
+	}
+
+	if (vma->vm_flags & VM_LOCKED) {
+		unsigned long locked, lock_limit;
+		locked = mm->locked_vm << PAGE_SHIFT;
+		lock_limit = current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur;
+		locked += new_len - old_len;
+		if (locked > lock_limit && !capable(CAP_IPC_LOCK))
+			goto Eagain;
+	}
+
+	if (!may_expand_vm(mm, (new_len - old_len) >> PAGE_SHIFT))
+		goto Enomem;
+
+	if (vma->vm_flags & VM_ACCOUNT) {
+		unsigned long charged = (new_len - old_len) >> PAGE_SHIFT;
+		if (security_vm_enough_memory(charged))
+			goto Efault;
+		*p = charged;
+	}
+
+	return vma;
+
+Efault:	/* very odd choice for most of the cases, but... */
+	return ERR_PTR(-EFAULT);
+Einval:
+	return ERR_PTR(-EINVAL);
+Enomem:
+	return ERR_PTR(-ENOMEM);
+Eagain:
+	return ERR_PTR(-EAGAIN);
+}
+
+static unsigned long mremap_to(unsigned long addr,
+	unsigned long old_len, unsigned long new_addr,
+	unsigned long new_len)
+{
+	struct mm_struct *mm = current->mm;
+	struct vm_area_struct *vma;
+	unsigned long ret = -EINVAL;
+	unsigned long charged = 0;
+	unsigned long map_flags;
+
+	if (new_addr & ~PAGE_MASK)
+		goto out;
+
+	if (new_len > TASK_SIZE || new_addr > TASK_SIZE - new_len)
+		goto out;
+
+	/* Check if the location we're moving into overlaps the
+	 * old location at all, and fail if it does.
+	 */
+	if ((new_addr <= addr) && (new_addr+new_len) > addr)
+		goto out;
+
+	if ((addr <= new_addr) && (addr+old_len) > new_addr)
+		goto out;
+
+	ret = security_file_mmap(NULL, 0, 0, 0, new_addr, 1);
+	if (ret)
+		goto out;
+
+	ret = do_munmap(mm, new_addr, new_len);
+	if (ret)
+		goto out;
+
+	if (old_len >= new_len) {
+		ret = do_munmap(mm, addr+new_len, old_len - new_len);
+		if (ret && old_len != new_len)
+			goto out;
+		old_len = new_len;
+	}
+
+	vma = vma_to_resize(addr, old_len, new_len, &charged);
+	if (IS_ERR(vma)) {
+		ret = PTR_ERR(vma);
+		goto out;
+	}
+
+	map_flags = MAP_FIXED;
+	if (vma->vm_flags & VM_MAYSHARE)
+		map_flags |= MAP_SHARED;
+
+	ret = get_unmapped_area(vma->vm_file, new_addr, new_len, vma->vm_pgoff +
+				((addr - vma->vm_start) >> PAGE_SHIFT),
+				map_flags);
+	if (ret & ~PAGE_MASK)
+		goto out1;
+
+	ret = move_vma(vma, addr, old_len, new_len, new_addr);
+	if (!(ret & ~PAGE_MASK))
+		goto out;
+out1:
+	vm_unacct_memory(charged);
+
+out:
+	return ret;
+}
+
+static int vma_expandable(struct vm_area_struct *vma, unsigned long delta)
+{
+	unsigned long end = vma->vm_end + delta;
+	if (end < vma->vm_end) /* overflow */
+		return 0;
+	if (vma->vm_next && vma->vm_next->vm_start < end) /* intersection */
+		return 0;
+	if (get_unmapped_area(NULL, vma->vm_start, end - vma->vm_start,
+			      0, MAP_FIXED) & ~PAGE_MASK)
+		return 0;
+	return 1;
+}
+
 /*
  * Expand (or shrink) an existing mapping, potentially moving it at the
  * same time (controlled by the MREMAP_MAYMOVE flag and available VM space)
@@ -294,32 +425,10 @@
 	if (!new_len)
 		goto out;
 
-	/* new_addr is only valid if MREMAP_FIXED is specified */
 	if (flags & MREMAP_FIXED) {
-		if (new_addr & ~PAGE_MASK)
-			goto out;
-		if (!(flags & MREMAP_MAYMOVE))
-			goto out;
-
-		if (new_len > TASK_SIZE || new_addr > TASK_SIZE - new_len)
-			goto out;
-
-		/* Check if the location we're moving into overlaps the
-		 * old location at all, and fail if it does.
-		 */
-		if ((new_addr <= addr) && (new_addr+new_len) > addr)
-			goto out;
-
-		if ((addr <= new_addr) && (addr+old_len) > new_addr)
-			goto out;
-
-		ret = security_file_mmap(NULL, 0, 0, 0, new_addr, 1);
-		if (ret)
-			goto out;
-
-		ret = do_munmap(mm, new_addr, new_len);
-		if (ret)
-			goto out;
+		if (flags & MREMAP_MAYMOVE)
+			ret = mremap_to(addr, old_len, new_addr, new_len);
+		goto out;
 	}
 
 	/*
@@ -332,60 +441,23 @@
 		if (ret && old_len != new_len)
 			goto out;
 		ret = addr;
-		if (!(flags & MREMAP_FIXED) || (new_addr == addr))
-			goto out;
-		old_len = new_len;
+		goto out;
 	}
 
 	/*
-	 * Ok, we need to grow..  or relocate.
+	 * Ok, we need to grow..
 	 */
-	ret = -EFAULT;
-	vma = find_vma(mm, addr);
-	if (!vma || vma->vm_start > addr)
+	vma = vma_to_resize(addr, old_len, new_len, &charged);
+	if (IS_ERR(vma)) {
+		ret = PTR_ERR(vma);
 		goto out;
-	if (is_vm_hugetlb_page(vma)) {
-		ret = -EINVAL;
-		goto out;
-	}
-	/* We can't remap across vm area boundaries */
-	if (old_len > vma->vm_end - addr)
-		goto out;
-	if (vma->vm_flags & (VM_DONTEXPAND | VM_PFNMAP)) {
-		if (new_len > old_len)
-			goto out;
-	}
-	if (vma->vm_flags & VM_LOCKED) {
-		unsigned long locked, lock_limit;
-		locked = mm->locked_vm << PAGE_SHIFT;
-		lock_limit = current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur;
-		locked += new_len - old_len;
-		ret = -EAGAIN;
-		if (locked > lock_limit && !capable(CAP_IPC_LOCK))
-			goto out;
-	}
-	if (!may_expand_vm(mm, (new_len - old_len) >> PAGE_SHIFT)) {
-		ret = -ENOMEM;
-		goto out;
-	}
-
-	if (vma->vm_flags & VM_ACCOUNT) {
-		charged = (new_len - old_len) >> PAGE_SHIFT;
-		if (security_vm_enough_memory(charged))
-			goto out_nc;
 	}
 
 	/* old_len exactly to the end of the area..
-	 * And we're not relocating the area.
 	 */
-	if (old_len == vma->vm_end - addr &&
-	    !((flags & MREMAP_FIXED) && (addr != new_addr)) &&
-	    (old_len != new_len || !(flags & MREMAP_MAYMOVE))) {
-		unsigned long max_addr = TASK_SIZE;
-		if (vma->vm_next)
-			max_addr = vma->vm_next->vm_start;
+	if (old_len == vma->vm_end - addr) {
 		/* can we just expand the current mapping? */
-		if (max_addr - addr >= new_len) {
+		if (vma_expandable(vma, new_len - old_len)) {
 			int pages = (new_len - old_len) >> PAGE_SHIFT;
 
 			vma_adjust(vma, vma->vm_start,
@@ -409,28 +481,27 @@
 	 */
 	ret = -ENOMEM;
 	if (flags & MREMAP_MAYMOVE) {
-		if (!(flags & MREMAP_FIXED)) {
-			unsigned long map_flags = 0;
-			if (vma->vm_flags & VM_MAYSHARE)
-				map_flags |= MAP_SHARED;
+		unsigned long map_flags = 0;
+		if (vma->vm_flags & VM_MAYSHARE)
+			map_flags |= MAP_SHARED;
 
-			new_addr = get_unmapped_area(vma->vm_file, 0, new_len,
-						vma->vm_pgoff, map_flags);
-			if (new_addr & ~PAGE_MASK) {
-				ret = new_addr;
-				goto out;
-			}
-
-			ret = security_file_mmap(NULL, 0, 0, 0, new_addr, 1);
-			if (ret)
-				goto out;
+		new_addr = get_unmapped_area(vma->vm_file, 0, new_len,
+					vma->vm_pgoff +
+					((addr - vma->vm_start) >> PAGE_SHIFT),
+					map_flags);
+		if (new_addr & ~PAGE_MASK) {
+			ret = new_addr;
+			goto out;
 		}
+
+		ret = security_file_mmap(NULL, 0, 0, 0, new_addr, 1);
+		if (ret)
+			goto out;
 		ret = move_vma(vma, addr, old_len, new_len, new_addr);
 	}
 out:
 	if (ret & ~PAGE_MASK)
 		vm_unacct_memory(charged);
-out_nc:
 	return ret;
 }
 
diff --git a/mm/util.c b/mm/util.c
index 7c35ad9..b377ce4 100644
--- a/mm/util.c
+++ b/mm/util.c
@@ -4,6 +4,10 @@
 #include <linux/module.h>
 #include <linux/err.h>
 #include <linux/sched.h>
+#include <linux/hugetlb.h>
+#include <linux/syscalls.h>
+#include <linux/mman.h>
+#include <linux/file.h>
 #include <asm/uaccess.h>
 
 #define CREATE_TRACE_POINTS
@@ -268,6 +272,46 @@
 }
 EXPORT_SYMBOL_GPL(get_user_pages_fast);
 
+SYSCALL_DEFINE6(mmap_pgoff, unsigned long, addr, unsigned long, len,
+		unsigned long, prot, unsigned long, flags,
+		unsigned long, fd, unsigned long, pgoff)
+{
+	struct file * file = NULL;
+	unsigned long retval = -EBADF;
+
+	if (!(flags & MAP_ANONYMOUS)) {
+		if (unlikely(flags & MAP_HUGETLB))
+			return -EINVAL;
+		file = fget(fd);
+		if (!file)
+			goto out;
+	} else if (flags & MAP_HUGETLB) {
+		struct user_struct *user = NULL;
+		/*
+		 * VM_NORESERVE is used because the reservations will be
+		 * taken when vm_ops->mmap() is called
+		 * A dummy user value is used because we are not locking
+		 * memory so no accounting is necessary
+		 */
+		len = ALIGN(len, huge_page_size(&default_hstate));
+		file = hugetlb_file_setup(HUGETLB_ANON_FILE, len, VM_NORESERVE,
+						&user, HUGETLB_ANONHUGE_INODE);
+		if (IS_ERR(file))
+			return PTR_ERR(file);
+	}
+
+	flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
+
+	down_write(&current->mm->mmap_sem);
+	retval = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
+	up_write(&current->mm->mmap_sem);
+
+	if (file)
+		fput(file);
+out:
+	return retval;
+}
+
 /* Tracepoints definitions. */
 EXPORT_TRACEPOINT_SYMBOL(kmalloc);
 EXPORT_TRACEPOINT_SYMBOL(kmem_cache_alloc);