diff --git a/.gitignore b/.gitignore index c2fd41347..03bacc779 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ -linux-*.tar.bz2 -patch-*.bz2 clog +*.bz2 *.rpm +*.orig kernel-2.6.*/ +kernel-3.*/ diff --git a/0001-EHCI-don-t-rescan-interrupt-QHs-needlessly.patch b/0001-EHCI-don-t-rescan-interrupt-QHs-needlessly.patch new file mode 100644 index 000000000..499c31a02 --- /dev/null +++ b/0001-EHCI-don-t-rescan-interrupt-QHs-needlessly.patch @@ -0,0 +1,95 @@ +From 1729d20333739e6cc23023e405fe8668f08117a2 Mon Sep 17 00:00:00 2001 +From: Alan Stern +Date: Tue, 17 May 2011 10:40:51 -0400 +Subject: [PATCH 1/2] EHCI: don't rescan interrupt QHs needlessly + +This patch (as1466) speeds up processing of ehci-hcd's periodic list. +The existing code will pointlessly rescan an interrupt endpoint queue +each time it encounters the queue's QH in the periodic list, which can +happen quite a few times if the endpoint's period is low. On some +embedded systems, this useless overhead can waste so much time that +the driver falls hopelessly behind and loses events. + +The patch introduces a "periodic_stamp" variable, which gets +incremented each time scan_periodic() runs and each time the scan +advances to a new frame. If the corresponding stamp in an interrupt +QH is equal to the current periodic_stamp, we assume the QH has +already been scanned and skip over it. Otherwise we scan the QH as +usual, and if none of its URBs have completed then we store the +current periodic_stamp in the QH's stamp, preventing it from being +scanned again. + +Signed-off-by: Alan Stern +Signed-off-by: Greg Kroah-Hartman +--- + drivers/usb/host/ehci-q.c | 1 + + drivers/usb/host/ehci-sched.c | 14 ++++++++++---- + drivers/usb/host/ehci.h | 1 + + 3 files changed, 12 insertions(+), 4 deletions(-) + +diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c +index ed8db6a..0079610 100644 +--- a/drivers/usb/host/ehci-q.c ++++ b/drivers/usb/host/ehci-q.c +@@ -826,6 +826,7 @@ qh_make ( + is_input, 0, + hb_mult(maxp) * max_packet(maxp))); + qh->start = NO_FRAME; ++ qh->stamp = ehci->periodic_stamp; + + if (urb->dev->speed == USB_SPEED_HIGH) { + qh->c_usecs = 0; +diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c +index a530856..9e54e85 100644 +--- a/drivers/usb/host/ehci-sched.c ++++ b/drivers/usb/host/ehci-sched.c +@@ -2351,6 +2351,7 @@ scan_periodic (struct ehci_hcd *ehci) + } + clock %= mod; + clock_frame = clock >> 3; ++ ++ehci->periodic_stamp; + + for (;;) { + union ehci_shadow q, *q_p; +@@ -2379,10 +2380,14 @@ restart: + temp.qh = qh_get (q.qh); + type = Q_NEXT_TYPE(ehci, q.qh->hw->hw_next); + q = q.qh->qh_next; +- modified = qh_completions (ehci, temp.qh); +- if (unlikely(list_empty(&temp.qh->qtd_list) || +- temp.qh->needs_rescan)) +- intr_deschedule (ehci, temp.qh); ++ if (temp.qh->stamp != ehci->periodic_stamp) { ++ modified = qh_completions(ehci, temp.qh); ++ if (!modified) ++ temp.qh->stamp = ehci->periodic_stamp; ++ if (unlikely(list_empty(&temp.qh->qtd_list) || ++ temp.qh->needs_rescan)) ++ intr_deschedule(ehci, temp.qh); ++ } + qh_put (temp.qh); + break; + case Q_TYPE_FSTN: +@@ -2515,6 +2520,7 @@ restart: + if (ehci->clock_frame != clock_frame) { + free_cached_lists(ehci); + ehci->clock_frame = clock_frame; ++ ++ehci->periodic_stamp; + } + } else { + now_uframe++; +diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h +index c702e4b..c2c2f9d 100644 +--- a/drivers/usb/host/ehci.h ++++ b/drivers/usb/host/ehci.h +@@ -117,6 +117,7 @@ struct ehci_hcd { /* one per controller */ + struct timer_list watchdog; + unsigned long actions; + unsigned stamp; ++ unsigned periodic_stamp; + unsigned random_frame; + unsigned long next_statechange; + ktime_t last_periodic_enable; +-- +1.7.6 + diff --git a/0002-USB-EHCI-go-back-to-using-the-system-clock-for-QH-un.patch b/0002-USB-EHCI-go-back-to-using-the-system-clock-for-QH-un.patch new file mode 100644 index 000000000..404ece19d --- /dev/null +++ b/0002-USB-EHCI-go-back-to-using-the-system-clock-for-QH-un.patch @@ -0,0 +1,217 @@ +From e8b80056d07bc849777d032bc699a59cc28f9ddb Mon Sep 17 00:00:00 2001 +From: Alan Stern +Date: Tue, 5 Jul 2011 12:34:05 -0400 +Subject: [PATCH 2/2] USB: EHCI: go back to using the system clock for QH + unlinks + +This patch (as1477) fixes a problem affecting a few types of EHCI +controller. Contrary to what one might expect, these controllers +automatically stop their internal frame counter when no ports are +enabled. Since ehci-hcd currently relies on the frame counter for +determining when it should unlink QHs from the async schedule, those +controllers run into trouble: The frame counter stops and the QHs +never get unlinked. + +Some systems have also experienced other problems traced back to +commit b963801164618e25fbdc0cd452ce49c3628b46c8 (USB: ehci-hcd unlink +speedups), which made the original switch from using the system clock +to using the frame counter. It never became clear what the reason was +for these problems, but evidently it is related to use of the frame +counter. + +To fix all these problems, this patch more or less reverts that commit +and goes back to using the system clock. But this can't be done +cleanly because other changes have since been made to the scan_async() +subroutine. One of these changes involved the tricky logic that tries +to avoid rescanning QHs that have already been seen when the scanning +loop is restarted, which happens whenever an URB is given back. +Switching back to clock-based unlinks would make this logic even more +complicated. + +Therefore the new code doesn't rescan the entire async list whenever a +giveback occurs. Instead it rescans only the current QH and continues +on from there. This requires the use of a separate pointer to keep +track of the next QH to scan, since the current QH may be unlinked +while the scanning is in progress. That new pointer must be global, +so that it can be adjusted forward whenever the _next_ QH gets +unlinked. (uhci-hcd uses this same trick.) + +Simplification of the scanning loop removes a level of indentation, +which accounts for the size of the patch. The amount of code changed +is relatively small, and it isn't exactly a reversion of the +b963801164 commit. + +This fixes Bugzilla #32432. + +Signed-off-by: Alan Stern +CC: +Tested-by: Matej Kenda +Signed-off-by: Greg Kroah-Hartman +--- + drivers/usb/host/ehci-hcd.c | 8 ++--- + drivers/usb/host/ehci-q.c | 82 +++++++++++++++++++++---------------------- + drivers/usb/host/ehci.h | 3 +- + 3 files changed, 45 insertions(+), 48 deletions(-) + +diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c +index 761bbb3..8e5b995 100644 +--- a/drivers/usb/host/ehci-hcd.c ++++ b/drivers/usb/host/ehci-hcd.c +@@ -83,7 +83,8 @@ static const char hcd_name [] = "ehci_hcd"; + #define EHCI_IAA_MSECS 10 /* arbitrary */ + #define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */ + #define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */ +-#define EHCI_SHRINK_FRAMES 5 /* async qh unlink delay */ ++#define EHCI_SHRINK_JIFFIES (DIV_ROUND_UP(HZ, 200) + 1) ++ /* 200-ms async qh unlink delay */ + + /* Initial IRQ latency: faster than hw default */ + static int log2_irq_thresh = 0; // 0 to 6 +@@ -138,10 +139,7 @@ timer_action(struct ehci_hcd *ehci, enum ehci_timer_action action) + break; + /* case TIMER_ASYNC_SHRINK: */ + default: +- /* add a jiffie since we synch against the +- * 8 KHz uframe counter. +- */ +- t = DIV_ROUND_UP(EHCI_SHRINK_FRAMES * HZ, 1000) + 1; ++ t = EHCI_SHRINK_JIFFIES; + break; + } + mod_timer(&ehci->watchdog, t + jiffies); +diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c +index 0079610..a664203 100644 +--- a/drivers/usb/host/ehci-q.c ++++ b/drivers/usb/host/ehci-q.c +@@ -1226,6 +1226,8 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh) + + prev->hw->hw_next = qh->hw->hw_next; + prev->qh_next = qh->qh_next; ++ if (ehci->qh_scan_next == qh) ++ ehci->qh_scan_next = qh->qh_next.qh; + wmb (); + + /* If the controller isn't running, we don't have to wait for it */ +@@ -1251,53 +1253,49 @@ static void scan_async (struct ehci_hcd *ehci) + struct ehci_qh *qh; + enum ehci_timer_action action = TIMER_IO_WATCHDOG; + +- ehci->stamp = ehci_readl(ehci, &ehci->regs->frame_index); + timer_action_done (ehci, TIMER_ASYNC_SHRINK); +-rescan: + stopped = !HC_IS_RUNNING(ehci_to_hcd(ehci)->state); +- qh = ehci->async->qh_next.qh; +- if (likely (qh != NULL)) { +- do { +- /* clean any finished work for this qh */ +- if (!list_empty(&qh->qtd_list) && (stopped || +- qh->stamp != ehci->stamp)) { +- int temp; +- +- /* unlinks could happen here; completion +- * reporting drops the lock. rescan using +- * the latest schedule, but don't rescan +- * qhs we already finished (no looping) +- * unless the controller is stopped. +- */ +- qh = qh_get (qh); +- qh->stamp = ehci->stamp; +- temp = qh_completions (ehci, qh); +- if (qh->needs_rescan) +- unlink_async(ehci, qh); +- qh_put (qh); +- if (temp != 0) { +- goto rescan; +- } +- } + +- /* unlink idle entries, reducing DMA usage as well +- * as HCD schedule-scanning costs. delay for any qh +- * we just scanned, there's a not-unusual case that it +- * doesn't stay idle for long. +- * (plus, avoids some kind of re-activation race.) ++ ehci->qh_scan_next = ehci->async->qh_next.qh; ++ while (ehci->qh_scan_next) { ++ qh = ehci->qh_scan_next; ++ ehci->qh_scan_next = qh->qh_next.qh; ++ rescan: ++ /* clean any finished work for this qh */ ++ if (!list_empty(&qh->qtd_list)) { ++ int temp; ++ ++ /* ++ * Unlinks could happen here; completion reporting ++ * drops the lock. That's why ehci->qh_scan_next ++ * always holds the next qh to scan; if the next qh ++ * gets unlinked then ehci->qh_scan_next is adjusted ++ * in start_unlink_async(). + */ +- if (list_empty(&qh->qtd_list) +- && qh->qh_state == QH_STATE_LINKED) { +- if (!ehci->reclaim && (stopped || +- ((ehci->stamp - qh->stamp) & 0x1fff) +- >= EHCI_SHRINK_FRAMES * 8)) +- start_unlink_async(ehci, qh); +- else +- action = TIMER_ASYNC_SHRINK; +- } ++ qh = qh_get(qh); ++ temp = qh_completions(ehci, qh); ++ if (qh->needs_rescan) ++ unlink_async(ehci, qh); ++ qh->unlink_time = jiffies + EHCI_SHRINK_JIFFIES; ++ qh_put(qh); ++ if (temp != 0) ++ goto rescan; ++ } + +- qh = qh->qh_next.qh; +- } while (qh); ++ /* unlink idle entries, reducing DMA usage as well ++ * as HCD schedule-scanning costs. delay for any qh ++ * we just scanned, there's a not-unusual case that it ++ * doesn't stay idle for long. ++ * (plus, avoids some kind of re-activation race.) ++ */ ++ if (list_empty(&qh->qtd_list) ++ && qh->qh_state == QH_STATE_LINKED) { ++ if (!ehci->reclaim && (stopped || ++ time_after_eq(jiffies, qh->unlink_time))) ++ start_unlink_async(ehci, qh); ++ else ++ action = TIMER_ASYNC_SHRINK; ++ } + } + if (action == TIMER_ASYNC_SHRINK) + timer_action (ehci, TIMER_ASYNC_SHRINK); +diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h +index c2c2f9d..23d9a5e 100644 +--- a/drivers/usb/host/ehci.h ++++ b/drivers/usb/host/ehci.h +@@ -74,6 +74,7 @@ struct ehci_hcd { /* one per controller */ + /* async schedule support */ + struct ehci_qh *async; + struct ehci_qh *reclaim; ++ struct ehci_qh *qh_scan_next; + unsigned scanning : 1; + + /* periodic schedule support */ +@@ -116,7 +117,6 @@ struct ehci_hcd { /* one per controller */ + struct timer_list iaa_watchdog; + struct timer_list watchdog; + unsigned long actions; +- unsigned stamp; + unsigned periodic_stamp; + unsigned random_frame; + unsigned long next_statechange; +@@ -337,6 +337,7 @@ struct ehci_qh { + struct ehci_qh *reclaim; /* next to reclaim */ + + struct ehci_hcd *ehci; ++ unsigned long unlink_time; + + /* + * Do NOT use atomic operations for QH refcounting. On some CPUs +-- +1.7.6 + diff --git a/Ecryptfs-Add-mount-option-to-check-uid-of-device-bei.patch b/Ecryptfs-Add-mount-option-to-check-uid-of-device-bei.patch new file mode 100644 index 000000000..1d67ff8b3 --- /dev/null +++ b/Ecryptfs-Add-mount-option-to-check-uid-of-device-bei.patch @@ -0,0 +1,130 @@ +From ed62f63919f93dcb040109959ee19983eda8ea84 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Thu, 15 Sep 2011 12:48:12 -0400 +Subject: [PATCH] Ecryptfs: Add mount option to check uid of device being + mounted = expect uid + +Close a TOCTOU race for mounts done via ecryptfs-mount-private. The mount +source (device) can be raced when the ownership test is done in userspace. +Provide Ecryptfs a means to force the uid check at mount time. + +Signed-off-by: John Johansen +Cc: +Signed-off-by: Tyler Hicks + +Backported to 2.6.35.14 by Josh Boyer +--- + fs/ecryptfs/main.c | 27 ++++++++++++++++++++++----- + 1 files changed, 22 insertions(+), 5 deletions(-) + +diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c +index cbd4e18..c249d14 100644 +--- a/fs/ecryptfs/main.c ++++ b/fs/ecryptfs/main.c +@@ -208,7 +208,7 @@ enum { ecryptfs_opt_sig, ecryptfs_opt_ecryptfs_sig, + ecryptfs_opt_passthrough, ecryptfs_opt_xattr_metadata, + ecryptfs_opt_encrypted_view, ecryptfs_opt_fnek_sig, + ecryptfs_opt_fn_cipher, ecryptfs_opt_fn_cipher_key_bytes, +- ecryptfs_opt_unlink_sigs, ecryptfs_opt_err }; ++ ecryptfs_opt_unlink_sigs, ecryptfs_opt_check_dev_ruid, ecryptfs_opt_err }; + + static const match_table_t tokens = { + {ecryptfs_opt_sig, "sig=%s"}, +@@ -223,6 +223,7 @@ static const match_table_t tokens = { + {ecryptfs_opt_fn_cipher, "ecryptfs_fn_cipher=%s"}, + {ecryptfs_opt_fn_cipher_key_bytes, "ecryptfs_fn_key_bytes=%u"}, + {ecryptfs_opt_unlink_sigs, "ecryptfs_unlink_sigs"}, ++ {ecryptfs_opt_check_dev_ruid, "ecryptfs_check_dev_ruid"}, + {ecryptfs_opt_err, NULL} + }; + +@@ -266,6 +267,7 @@ static void ecryptfs_init_mount_crypt_stat( + * ecryptfs_parse_options + * @sb: The ecryptfs super block + * @options: The options pased to the kernel ++ * @check_ruid: set to 1 if device uid should be checked against the ruid + * + * Parse mount options: + * debug=N - ecryptfs_verbosity level for debug output +@@ -281,7 +283,7 @@ static void ecryptfs_init_mount_crypt_stat( + * + * Returns zero on success; non-zero on error + */ +-static int ecryptfs_parse_options(struct ecryptfs_sb_info *sbi, char *options) ++static int ecryptfs_parse_options(struct ecryptfs_sb_info *sbi, char *options, uid_t *check_ruid) + { + char *p; + int rc = 0; +@@ -306,6 +308,8 @@ static int ecryptfs_parse_options(struct ecryptfs_sb_info *sbi, char *options) + char *cipher_key_bytes_src; + char *fn_cipher_key_bytes_src; + ++ *check_ruid = 0; ++ + if (!options) { + rc = -EINVAL; + goto out; +@@ -406,6 +410,9 @@ static int ecryptfs_parse_options(struct ecryptfs_sb_info *sbi, char *options) + case ecryptfs_opt_unlink_sigs: + mount_crypt_stat->flags |= ECRYPTFS_UNLINK_SIGS; + break; ++ case ecryptfs_opt_check_dev_ruid: ++ *check_ruid = 1; ++ break; + case ecryptfs_opt_err: + default: + printk(KERN_WARNING +@@ -494,7 +501,7 @@ static struct file_system_type ecryptfs_fs_type; + * ecryptfs_interpose to create our initial inode and super block + * struct. + */ +-static int ecryptfs_read_super(struct super_block *sb, const char *dev_name) ++static int ecryptfs_read_super(struct super_block *sb, const char *dev_name, uid_t check_ruid) + { + struct path path; + int rc; +@@ -511,6 +518,15 @@ static int ecryptfs_read_super(struct super_block *sb, const char *dev_name) + "known incompatibilities\n"); + goto out_free; + } ++ ++ if (check_ruid && path.dentry->d_inode->i_uid != current_uid()) { ++ rc = -EPERM; ++ printk(KERN_ERR "Mount of device (uid: %d) not owned by " ++ "requested user (uid: %d)\n", ++ path.dentry->d_inode->i_uid, current_uid()); ++ goto out_free; ++ } ++ + ecryptfs_set_superblock_lower(sb, path.dentry->d_sb); + sb->s_maxbytes = path.dentry->d_sb->s_maxbytes; + sb->s_blocksize = path.dentry->d_sb->s_blocksize; +@@ -549,6 +565,7 @@ static int ecryptfs_get_sb(struct file_system_type *fs_type, int flags, + struct ecryptfs_dentry_info *root_info; + const char *err = "Getting sb failed"; + int rc; ++ uid_t check_ruid; + + sbi = kmem_cache_zalloc(ecryptfs_sb_info_cache, GFP_KERNEL); + if (!sbi) { +@@ -556,7 +573,7 @@ static int ecryptfs_get_sb(struct file_system_type *fs_type, int flags, + goto out; + } + +- rc = ecryptfs_parse_options(sbi, raw_data); ++ rc = ecryptfs_parse_options(sbi, raw_data, &check_ruid); + if (rc) { + err = "Error parsing options"; + goto out; +@@ -601,7 +618,7 @@ static int ecryptfs_get_sb(struct file_system_type *fs_type, int flags, + /* ->kill_sb() will take care of root_info */ + ecryptfs_set_dentry_private(s->s_root, root_info); + s->s_flags |= MS_ACTIVE; +- rc = ecryptfs_read_super(s, dev_name); ++ rc = ecryptfs_read_super(s, dev_name, check_ruid); + if (rc) { + deactivate_locked_super(s); + err = "Reading sb failed"; +-- +1.7.6 + diff --git a/KEYS-Fix-a-NULL-pointer-deref-in-the-user-defined-ke.patch b/KEYS-Fix-a-NULL-pointer-deref-in-the-user-defined-ke.patch new file mode 100644 index 000000000..46755df78 --- /dev/null +++ b/KEYS-Fix-a-NULL-pointer-deref-in-the-user-defined-ke.patch @@ -0,0 +1,71 @@ +From f8789858be5c1b13543040b74d978ea448461155 Mon Sep 17 00:00:00 2001 +From: David Howells +Date: Tue, 15 Nov 2011 22:09:45 +0000 +Subject: [PATCH] KEYS: Fix a NULL pointer deref in the user-defined key type + +commit 9f35a33b8d06263a165efe3541d9aa0cdbd70b3b upstream. + +Fix a NULL pointer deref in the user-defined key type whereby updating a +negative key into a fully instantiated key will cause an oops to occur +when the code attempts to free the non-existent old payload. + +This results in an oops that looks something like the following: + + BUG: unable to handle kernel NULL pointer dereference at 0000000000000008 + IP: [] __call_rcu+0x11/0x13e + PGD 3391d067 PUD 3894a067 PMD 0 + Oops: 0002 [#1] SMP + CPU 1 + Pid: 4354, comm: keyctl Not tainted 3.1.0-fsdevel+ #1140 /DG965RY + RIP: 0010:[] [] __call_rcu+0x11/0x13e + RSP: 0018:ffff88003d591df8 EFLAGS: 00010246 + RAX: 0000000000000000 RBX: 0000000000000000 RCX: 000000000000006e + RDX: ffffffff8161d0c0 RSI: 0000000000000000 RDI: 0000000000000000 + RBP: ffff88003d591e18 R08: 0000000000000000 R09: ffffffff8152fa6c + R10: 0000000000000000 R11: 0000000000000300 R12: ffff88003b8f9538 + R13: ffffffff8161d0c0 R14: ffff88003b8f9d50 R15: ffff88003c69f908 + FS: 00007f97eb18c720(0000) GS:ffff88003bd00000(0000) knlGS:0000000000000000 + CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 + CR2: 0000000000000008 CR3: 000000003d47a000 CR4: 00000000000006e0 + DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 + DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 + Process keyctl (pid: 4354, threadinfo ffff88003d590000, task ffff88003c78a040) + Stack: + ffff88003e0ffde0 ffff88003b8f9538 0000000000000001 ffff88003b8f9d50 + ffff88003d591e28 ffffffff810860f0 ffff88003d591e68 ffffffff8117bfea + ffff88003d591e68 ffffffff00000000 ffff88003e0ffde1 ffff88003e0ffde0 + Call Trace: + [] call_rcu_sched+0x10/0x12 + [] user_update+0x8d/0xa2 + [] key_create_or_update+0x236/0x270 + [] sys_add_key+0x123/0x17e + [] system_call_fastpath+0x16/0x1b + +Signed-off-by: David Howells +Acked-by: Jeff Layton +Acked-by: Neil Horman +Acked-by: Steve Dickson +Acked-by: James Morris +Signed-off-by: Linus Torvalds +Signed-off-by: Greg Kroah-Hartman +--- + security/keys/user_defined.c | 3 ++- + 1 files changed, 2 insertions(+), 1 deletions(-) + +diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c +index e9aa079..d6781b9 100644 +--- a/security/keys/user_defined.c ++++ b/security/keys/user_defined.c +@@ -119,7 +119,8 @@ int user_update(struct key *key, const void *data, size_t datalen) + key->expiry = 0; + } + +- call_rcu(&zap->rcu, user_update_rcu_disposal); ++ if (zap) ++ call_rcu(&zap->rcu, user_update_rcu_disposal); + + error: + return ret; +-- +1.7.7.1 + diff --git a/Makefile b/Makefile index ea1296b48..e8d6a55e8 100644 --- a/Makefile +++ b/Makefile @@ -88,6 +88,7 @@ debug: @perl -pi -e 's/# CONFIG_KDB_KEYBOARD is not set/CONFIG_KDB_KEYBOARD=y/' config-nodebug @perl -pi -e 's/# CONFIG_CPU_NOTIFIER_ERROR_INJECT is not set/CONFIG_CPU_NOTIFIER_ERROR_INJECT=m/' config-nodebug @perl -pi -e 's/# CONFIG_DEBUG_PER_CPU_MAPS is not set/CONFIG_DEBUG_PER_CPU_MAPS=y/' config-nodebug + #@perl -pi -e 's/# CONFIG_PCI_DEFAULT_USE_CRS is not set/CONFIG_PCI_DEFAULT_USE_CRS=y/' config-nodebug @# just in case we're going from extremedebug -> debug @perl -pi -e 's/CONFIG_DEBUG_PAGEALLOC=y/# CONFIG_DEBUG_PAGEALLOC is not set/' config-nodebug @@ -97,6 +98,10 @@ debug: @perl -pi -e 's/^%define debugbuildsenabled 1/%define debugbuildsenabled 0/' kernel.spec @perl -pi -e 's/^%define rawhide_skip_docs 0/%define rawhide_skip_docs 1/' kernel.spec +nodebuginfo: + @perl -pi -e 's/^%define with_debuginfo %\{\?_without_debuginfo: 0\} %\{\?\!_without_debuginfo: 1\}/%define with_debuginfo %\{\?_without_debuginfo: 0\} %\{\?\!_without_debuginfo: 0\}/' kernel.spec +nodebug: release + @perl -pi -e 's/^%define debugbuildsenabled 1/%define debugbuildsenabled 0/' kernel.spec release: @perl -pi -e 's/CONFIG_SLUB_DEBUG_ON=y/# CONFIG_SLUB_DEBUG_ON is not set/' config-nodebug @perl -pi -e 's/CONFIG_LOCK_STAT=y/# CONFIG_LOCK_STAT is not set/' config-nodebug @@ -156,6 +161,7 @@ release: #@perl -pi -e 's/CONFIG_KGDB_KDB=y/# CONFIG_KGDB_KDB is not set/' config-nodebug #@perl -pi -e 's/CONFIG_KDB_KEYBOARD=y/# CONFIG_KDB_KEYBOARD is not set/' config-nodebug @perl -pi -e 's/CONFIG_DEBUG_PER_CPU_MAPS=y/# CONFIG_DEBUG_PER_CPU_MAPS is not set/' config-nodebug + #@perl -pi -e 's/CONFIG_PCI_DEFAULT_USE_CRS=y/# CONFIG_PCI_DEFAULT_USE_CRS is not set/' config-nodebug @perl -pi -e 's/CONFIG_DEBUG_PAGEALLOC=y/# CONFIG_DEBUG_PAGEALLOC is not set/' config-debug @perl -pi -e 's/CONFIG_DEBUG_PAGEALLOC=y/# CONFIG_DEBUG_PAGEALLOC is not set/' config-nodebug diff --git a/TPM-Call-tpm_transmit-with-correct-size.patch b/TPM-Call-tpm_transmit-with-correct-size.patch new file mode 100644 index 000000000..64f776f13 --- /dev/null +++ b/TPM-Call-tpm_transmit-with-correct-size.patch @@ -0,0 +1,43 @@ +From 6b07d30aca7e52f2881b8c8c20c8a2cd28e8b3d3 Mon Sep 17 00:00:00 2001 +From: Peter Huewe +Date: Thu, 15 Sep 2011 14:37:43 -0300 +Subject: [PATCH] TPM: Call tpm_transmit with correct size + +This patch changes the call of tpm_transmit by supplying the size of the +userspace buffer instead of TPM_BUFSIZE. + +This got assigned CVE-2011-1161. + +[The first hunk didn't make sense given one could expect + way less data than TPM_BUFSIZE, so added tpm_transmit boundary + check over bufsiz instead + The last parameter of tpm_transmit() reflects the amount + of data expected from the device, and not the buffer size + being supplied to it. It isn't ideal to parse it directly, + so we just set it to the maximum the input buffer can handle + and let the userspace API to do such job.] + +Signed-off-by: Rajiv Andrade +Cc: Stable Kernel +Signed-off-by: James Morris +--- + drivers/char/tpm/tpm.c | 3 +++ + 1 files changed, 3 insertions(+), 0 deletions(-) + +diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c +index caf8012..1fe9793 100644 +--- a/drivers/char/tpm/tpm.c ++++ b/drivers/char/tpm/tpm.c +@@ -383,6 +383,9 @@ static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, + u32 count, ordinal; + unsigned long stop; + ++ if (bufsiz > TPM_BUFSIZE) ++ bufsiz = TPM_BUFSIZE; ++ + count = be32_to_cpu(*((__be32 *) (buf + 2))); + ordinal = be32_to_cpu(*((__be32 *) (buf + 6))); + if (count == 0) +-- +1.7.6 + diff --git a/TPM-Zero-buffer-after-copying-to-userspace.patch b/TPM-Zero-buffer-after-copying-to-userspace.patch new file mode 100644 index 000000000..1b3d9ac48 --- /dev/null +++ b/TPM-Zero-buffer-after-copying-to-userspace.patch @@ -0,0 +1,45 @@ +From 3321c07ae5068568cd61ac9f4ba749006a7185c9 Mon Sep 17 00:00:00 2001 +From: Peter Huewe +Date: Thu, 15 Sep 2011 14:47:42 -0300 +Subject: [PATCH] TPM: Zero buffer after copying to userspace + +Since the buffer might contain security related data it might be a good idea to +zero the buffer after we have copied it to userspace. + +This got assigned CVE-2011-1162. + +Signed-off-by: Rajiv Andrade +Cc: Stable Kernel +Signed-off-by: James Morris +--- + drivers/char/tpm/tpm.c | 6 +++++- + 1 files changed, 5 insertions(+), 1 deletions(-) + +diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c +index 1fe9793..9ca5c02 100644 +--- a/drivers/char/tpm/tpm.c ++++ b/drivers/char/tpm/tpm.c +@@ -1105,6 +1105,7 @@ ssize_t tpm_read(struct file *file, char __user *buf, + { + struct tpm_chip *chip = file->private_data; + ssize_t ret_size; ++ int rc; + + del_singleshot_timer_sync(&chip->user_read_timer); + flush_work_sync(&chip->work); +@@ -1115,8 +1116,11 @@ ssize_t tpm_read(struct file *file, char __user *buf, + ret_size = size; + + mutex_lock(&chip->buffer_mutex); +- if (copy_to_user(buf, chip->data_buffer, ret_size)) ++ rc = copy_to_user(buf, chip->data_buffer, ret_size); ++ memset(chip->data_buffer, 0, ret_size); ++ if (rc) + ret_size = -EFAULT; ++ + mutex_unlock(&chip->buffer_mutex); + } + +-- +1.7.6 + diff --git a/acer-wmi-modalias.patch b/acer-wmi-modalias.patch new file mode 100644 index 000000000..ac2f452d0 --- /dev/null +++ b/acer-wmi-modalias.patch @@ -0,0 +1,29 @@ +commit 08a0799d5736f1494ef35d386570d177447acbfb +Author: Lee, Chun-Yi +Date: Wed Apr 6 17:40:06 2011 +0800 + + acer-wmi: Fix capitalisation of GUID in module alias + + wmi:6AF4F258-B401-42Fd-BE91-3D4AC2D7C0D3 needs to be + wmi:6AF4F258-B401-42FD-BE91-3D4AC2D7C0D3 in module alias for acer-wmi is + automatically loaded. + + Cc: Pali Rohár + Cc: Carlos Corbacho + Cc: Matthew Garrett + Signed-off-by: Lee, Chun-Yi + Signed-off-by: Matthew Garrett + +diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c +index 5ea6c34..ac4e7f8 100644 +--- a/drivers/platform/x86/acer-wmi.c ++++ b/drivers/platform/x86/acer-wmi.c +@@ -95,7 +95,7 @@ struct acer_quirks { + #define WMID_GUID2 "95764E09-FB56-4e83-B31A-37761F60994A" + + MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB"); +-MODULE_ALIAS("wmi:6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3"); ++MODULE_ALIAS("wmi:6AF4F258-B401-42FD-BE91-3D4AC2D7C0D3"); + + /* + * Interface capability flags diff --git a/acpi-sony-nonvs-blacklist.patch b/acpi-sony-nonvs-blacklist.patch new file mode 100644 index 000000000..db500e8bf --- /dev/null +++ b/acpi-sony-nonvs-blacklist.patch @@ -0,0 +1,38 @@ +diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c +index 3ed80b2..17fc718 100644 +--- a/drivers/acpi/sleep.c ++++ b/drivers/acpi/sleep.c +@@ -390,6 +390,14 @@ static struct dmi_system_id __initdata acpisleep_dmi_table[] = { + }, + { + .callback = init_nvs_nosave, ++ .ident = "Sony Vaio VGN-FW21E", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FW21E"), ++ }, ++ }, ++ { ++ .callback = init_nvs_nosave, + .ident = "Sony Vaio VGN-SR11M", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), +diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c +index 0e46fae..6d9a3ab 100644 +--- a/drivers/acpi/sleep.c ++++ b/drivers/acpi/sleep.c +@@ -398,6 +398,14 @@ static struct dmi_system_id __initdata acpisleep_dmi_table[] = { + }, + { + .callback = init_nvs_nosave, ++ .ident = "Sony Vaio VPCEB17FX", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "VPCEB17FX"), ++ }, ++ }, ++ { ++ .callback = init_nvs_nosave, + .ident = "Sony Vaio VGN-SR11M", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), diff --git a/acpi-update-battery-information-on-notification-0x81.patch b/acpi-update-battery-information-on-notification-0x81.patch new file mode 100644 index 000000000..e9c027291 --- /dev/null +++ b/acpi-update-battery-information-on-notification-0x81.patch @@ -0,0 +1,68 @@ +--- linux-2.6.35.x86_64/drivers/acpi/battery.c.orig 2010-12-23 10:42:09.291854595 -0500 ++++ linux-2.6.35.x86_64/drivers/acpi/battery.c 2010-12-23 10:44:30.996045838 -0500 +@@ -596,9 +596,10 @@ + } + } + +-static int acpi_battery_update(struct acpi_battery *battery) ++static int acpi_battery_update(struct acpi_battery *battery, bool get_info) + { + int result, old_present = acpi_battery_present(battery); ++ int old_power_unit = battery->power_unit; + result = acpi_battery_get_status(battery); + if (result) + return result; +@@ -621,6 +622,16 @@ + if (!battery->bat.dev) + sysfs_add_battery(battery); + #endif ++ if (get_info) { ++ acpi_battery_get_info(battery); ++#ifdef CONFIG_ACPI_SYSFS_POWER ++ if (old_power_unit != battery->power_unit) { ++ /* The battery has changed its reporting units */ ++ sysfs_remove_battery(battery); ++ sysfs_add_battery(battery); ++ } ++#endif ++ } + result = acpi_battery_get_state(battery); + acpi_battery_quirks2(battery); + return result; +@@ -798,7 +809,7 @@ + static int acpi_battery_read(int fid, struct seq_file *seq) + { + struct acpi_battery *battery = seq->private; +- int result = acpi_battery_update(battery); ++ int result = acpi_battery_update(battery, false); + return acpi_print_funcs[fid](seq, result); + } + +@@ -913,7 +924,8 @@ + #ifdef CONFIG_ACPI_SYSFS_POWER + old = battery->bat.dev; + #endif +- acpi_battery_update(battery); ++ acpi_battery_update(battery, (event == ACPI_BATTERY_NOTIFY_INFO ? true ++ : false)); + acpi_bus_generate_proc_event(device, event, + acpi_battery_present(battery)); + acpi_bus_generate_netlink_event(device->pnp.device_class, +@@ -944,7 +956,7 @@ + if (ACPI_SUCCESS(acpi_get_handle(battery->device->handle, + "_BIX", &handle))) + set_bit(ACPI_BATTERY_XINFO_PRESENT, &battery->flags); +- acpi_battery_update(battery); ++ acpi_battery_update(battery, false); + #ifdef CONFIG_ACPI_PROCFS_POWER + result = acpi_battery_add_fs(device); + #endif +@@ -987,7 +999,7 @@ + return -EINVAL; + battery = acpi_driver_data(device); + battery->update_time = 0; +- acpi_battery_update(battery); ++ acpi_battery_update(battery, true); + return 0; + } + diff --git a/add-appleir-usb-driver.patch b/add-appleir-usb-driver.patch new file mode 100644 index 000000000..999ee2e69 --- /dev/null +++ b/add-appleir-usb-driver.patch @@ -0,0 +1,702 @@ +From 46fadae732d825141f45bf2fbd6381451da26ad7 Mon Sep 17 00:00:00 2001 +From: Bastien Nocera +Date: Fri, 10 Sep 2010 16:40:46 +0100 +Subject: [PATCH] Input: add appleir USB driver + +This driver was originally written by James McKenzie, updated by +Greg Kroah-Hartman, further updated by myself, with suspend support +added. + +More recent versions of the IR receiver are also supported through +a patch by Alex Karpenko. The patch also adds support for the 2nd +and 5th generation of the controller, and the menu key on newer +brushed metal remotes. + +Tested on a MacbookAir1,1 + +Signed-off-by: Bastien Nocera +--- + Documentation/input/appleir.txt | 46 ++++ + drivers/hid/hid-apple.c | 4 - + drivers/hid/hid-core.c | 7 +- + drivers/hid/hid-ids.h | 5 +- + drivers/input/misc/Kconfig | 13 + + drivers/input/misc/Makefile | 1 + + drivers/input/misc/appleir.c | 519 +++++++++++++++++++++++++++++++++++++++ + 7 files changed, 588 insertions(+), 7 deletions(-) + create mode 100644 Documentation/input/appleir.txt + create mode 100644 drivers/input/misc/appleir.c + +diff --git a/Documentation/input/appleir.txt b/Documentation/input/appleir.txt +new file mode 100644 +index 0000000..db637fb +--- /dev/null ++++ b/Documentation/input/appleir.txt +@@ -0,0 +1,46 @@ ++Apple IR receiver Driver (appleir) ++---------------------------------- ++ Copyright (C) 2009 Bastien Nocera ++ ++The appleir driver is a kernel input driver to handle Apple's IR ++receivers (and associated remotes) in the kernel. ++ ++The driver is an input driver which only handles "official" remotes ++as built and sold by Apple. ++ ++Authors ++------- ++ ++James McKenzie (original driver) ++Alex Karpenko (05ac:8242 support) ++Greg Kroah-Hartman (cleanups and original submission) ++Bastien Nocera (further cleanups, brushed metal "enter" ++button support and suspend support) ++ ++Supported hardware ++------------------ ++ ++- All Apple laptops and desktops from 2005 onwards, except: ++ - the unibody Macbook (2009) ++ - Mac Pro (all versions) ++- Apple TV (all revisions prior to September 2010) ++ ++The remote will only support the 6 (old white) or 7 (brushed metal) buttons ++of the remotes as sold by Apple. See the next section if you want to use ++other remotes or want to use lirc with the device instead of the kernel driver. ++ ++Using lirc (native) instead of the kernel driver ++------------------------------------------------ ++ ++First, you will need to disable the kernel driver for the receiver. ++ ++This can be achieved by passing quirks to the usbhid driver. ++The quirk line would be: ++usbhid.quirks=0x05ac:0x8242:0x40000010 ++ ++With 0x05ac being the vendor ID (Apple, you shouldn't need to change this) ++With 0x8242 being the product ID (check the output of lsusb for your hardware) ++And 0x10 being "HID_QUIRK_HIDDEV_FORCE" and 0x40000000 being "HID_QUIRK_NO_IGNORE" ++ ++This should force the creation of a hiddev device for the receiver, and ++make it usable under lirc. +diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c +index bba05d0..0059d5a 100644 +--- a/drivers/hid/hid-apple.c ++++ b/drivers/hid/hid-apple.c +@@ -361,10 +361,6 @@ static void apple_remove(struct hid_device *hdev) + } + + static const struct hid_device_id apple_devices[] = { +- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL), +- .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT }, +- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4), +- .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE), + .driver_data = APPLE_MIGHTYMOUSE | APPLE_INVERT_HWHEEL }, + +diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c +index 866e54e..1d5e284 100644 +--- a/drivers/hid/hid-core.c ++++ b/drivers/hid/hid-core.c +@@ -1239,8 +1239,6 @@ static const struct hid_device_id hid_blacklist[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_3M, USB_DEVICE_ID_3M2256) }, + { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) }, + { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) }, +- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) }, +- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI) }, +@@ -1571,6 +1569,11 @@ static const struct hid_device_id hid_ignore_list[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24) }, + { HID_USB_DEVICE(USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL2) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL3) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) }, ++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL5) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_T91MT)}, + { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_LCM)}, + { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_LCM2)}, +diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h +index 31601ee..9afe3bc 100644 +--- a/drivers/hid/hid-ids.h ++++ b/drivers/hid/hid-ids.h +@@ -98,8 +98,11 @@ + #define USB_DEVICE_ID_APPLE_ALU_WIRELESS_2009_JIS 0x023b + #define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a + #define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b +-#define USB_DEVICE_ID_APPLE_ATV_IRCONTROL 0x8241 ++#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240 ++#define USB_DEVICE_ID_APPLE_IRCONTROL2 0x1440 ++#define USB_DEVICE_ID_APPLE_IRCONTROL3 0x8241 + #define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242 ++#define USB_DEVICE_ID_APPLE_IRCONTROL5 0x8243 + + #define USB_VENDOR_ID_ASUS 0x0486 + #define USB_DEVICE_ID_ASUS_T91MT 0x0185 +diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig +index c44b9ea..76a12b7 100644 +--- a/drivers/input/misc/Kconfig ++++ b/drivers/input/misc/Kconfig +@@ -199,6 +199,19 @@ config INPUT_KEYSPAN_REMOTE + To compile this driver as a module, choose M here: the module will + be called keyspan_remote. + ++config INPUT_APPLEIR ++ tristate "Apple infrared receiver (built in)" ++ depends on USB_ARCH_HAS_HCD ++ select USB ++ help ++ Say Y here if you want to use a Apple infrared remote control. All ++ the Apple computers from 2005 onwards include such a port, except ++ the unibody Macbook (2009), and Mac Pros. This receiver is also ++ used in the Apple TV set-top box prior to the 2010 model. ++ ++ To compile this driver as a module, choose M here: the module will ++ be called appleir. ++ + config INPUT_POWERMATE + tristate "Griffin PowerMate and Contour Jog support" + depends on USB_ARCH_HAS_HCD +diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile +index 71fe57d..62a5c60 100644 +--- a/drivers/input/misc/Makefile ++++ b/drivers/input/misc/Makefile +@@ -9,6 +9,7 @@ obj-$(CONFIG_INPUT_AD714X) += ad714x.o + obj-$(CONFIG_INPUT_AD714X_I2C) += ad714x-i2c.o + obj-$(CONFIG_INPUT_AD714X_SPI) += ad714x-spi.o + obj-$(CONFIG_INPUT_APANEL) += apanel.o ++obj-$(CONFIG_INPUT_APPLEIR) += appleir.o + obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o + obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o + obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o +diff --git a/drivers/input/misc/appleir.c b/drivers/input/misc/appleir.c +new file mode 100644 +index 0000000..3817a3c +--- /dev/null ++++ b/drivers/input/misc/appleir.c +@@ -0,0 +1,519 @@ ++/* ++ * appleir: USB driver for the apple ir device ++ * ++ * Original driver written by James McKenzie ++ * Ported to recent 2.6 kernel versions by Greg Kroah-Hartman ++ * ++ * Copyright (C) 2006 James McKenzie ++ * Copyright (C) 2008 Greg Kroah-Hartman ++ * Copyright (C) 2008 Novell Inc. ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the Free ++ * Software Foundation, version 2. ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DRIVER_VERSION "v1.2" ++#define DRIVER_AUTHOR "James McKenzie" ++#define DRIVER_DESC "Apple infrared receiver driver" ++#define DRIVER_LICENSE "GPL" ++ ++MODULE_AUTHOR(DRIVER_AUTHOR); ++MODULE_DESCRIPTION(DRIVER_DESC); ++MODULE_LICENSE(DRIVER_LICENSE); ++ ++#define USB_VENDOR_ID_APPLE 0x05ac ++#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240 ++#define USB_DEVICE_ID_APPLE_IRCONTROL2 0x1440 ++#define USB_DEVICE_ID_APPLE_IRCONTROL3 0x8241 ++#define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242 ++#define USB_DEVICE_ID_APPLE_IRCONTROL5 0x8243 ++ ++#define URB_SIZE 32 ++ ++#define MAX_KEYS 9 ++#define MAX_KEYS_MASK (MAX_KEYS - 1) ++ ++#define dbginfo(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0) ++ ++static int debug; ++module_param(debug, int, 0644); ++MODULE_PARM_DESC(debug, "Enable extra debug messages and information"); ++ ++/* I have two devices both of which report the following */ ++/* 25 87 ee 83 0a + */ ++/* 25 87 ee 83 0c - */ ++/* 25 87 ee 83 09 << */ ++/* 25 87 ee 83 06 >> */ ++/* 25 87 ee 83 05 >" */ ++/* 25 87 ee 83 03 menu */ ++/* 26 00 00 00 00 for key repeat*/ ++ ++/* Thomas Glanzmann reports the following responses */ ++/* 25 87 ee ca 0b + */ ++/* 25 87 ee ca 0d - */ ++/* 25 87 ee ca 08 << */ ++/* 25 87 ee ca 07 >> */ ++/* 25 87 ee ca 04 >" */ ++/* 25 87 ee ca 02 menu */ ++/* 26 00 00 00 00 for key repeat*/ ++/* He also observes the following event sometimes */ ++/* sent after a key is release, which I interpret */ ++/* as a flat battery message */ ++/* 25 87 e0 ca 06 flat battery */ ++ ++/* Alexandre Karpenko reports the following responses for Device ID 0x8242 */ ++/* 25 87 ee 47 0b + */ ++/* 25 87 ee 47 0d - */ ++/* 25 87 ee 47 08 << */ ++/* 25 87 ee 47 07 >> */ ++/* 25 87 ee 47 04 >" */ ++/* 25 87 ee 47 02 menu */ ++/* 26 87 ee 47 ** for key repeat (** is the code of the key being held) */ ++ ++/* Bastien Nocera's "new" remote */ ++/* 25 87 ee 91 5f followed by ++ * 25 87 ee 91 05 gives you >" ++ * ++ * 25 87 ee 91 5c followed by ++ * 25 87 ee 91 05 gives you the middle button */ ++ ++static const unsigned short appleir_key_table[] = { ++ KEY_RESERVED, ++ KEY_MENU, ++ KEY_PLAYPAUSE, ++ KEY_FORWARD, ++ KEY_BACK, ++ KEY_VOLUMEUP, ++ KEY_VOLUMEDOWN, ++ KEY_ENTER, ++ KEY_RESERVED, ++}; ++ ++struct appleir { ++ struct input_dev *input_dev; ++ unsigned short keymap[ARRAY_SIZE(appleir_key_table)]; ++ u8 *data; ++ dma_addr_t dma_buf; ++ struct usb_device *usbdev; ++ unsigned int flags; ++ struct urb *urb; ++ struct timer_list key_up_timer; ++ int current_key; ++ int prev_key_idx; ++ char phys[32]; ++}; ++ ++static DEFINE_MUTEX(appleir_mutex); ++ ++enum { ++ APPLEIR_OPENED = 0x1, ++ APPLEIR_SUSPENDED = 0x2, ++}; ++ ++static struct usb_device_id appleir_ids[] = { ++ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) }, ++ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL2) }, ++ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL3) }, ++ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) }, ++ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL5) }, ++ {} ++}; ++MODULE_DEVICE_TABLE(usb, appleir_ids); ++ ++static void dump_packet(struct appleir *appleir, char *msg, u8 *data, int len) ++{ ++ int i; ++ ++ printk(KERN_ERR "appleir: %s (%d bytes)", msg, len); ++ ++ for (i = 0; i < len; ++i) ++ printk(" %02x", data[i]); ++ printk(" (should be command %d)\n", (data[4] >> 1) & MAX_KEYS_MASK); ++} ++ ++static int get_key(int data) ++{ ++ switch (data) { ++ case 0x02: ++ case 0x03: ++ /* menu */ ++ return 1; ++ case 0x04: ++ case 0x05: ++ /* >" */ ++ return 2; ++ case 0x06: ++ case 0x07: ++ /* >> */ ++ return 3; ++ case 0x08: ++ case 0x09: ++ /* << */ ++ return 4; ++ case 0x0a: ++ case 0x0b: ++ /* + */ ++ return 5; ++ case 0x0c: ++ case 0x0d: ++ /* - */ ++ return 6; ++ case 0x5c: ++ /* Middle button, on newer remotes, ++ * part of a 2 packet-command */ ++ return -7; ++ default: ++ return -1; ++ } ++} ++ ++static void key_up(struct appleir *appleir, int key) ++{ ++ dbginfo(&appleir->input_dev->dev, "key %d up\n", key); ++ input_report_key(appleir->input_dev, key, 0); ++ input_sync(appleir->input_dev); ++} ++ ++static void key_down(struct appleir *appleir, int key) ++{ ++ dbginfo(&appleir->input_dev->dev, "key %d down\n", key); ++ input_report_key(appleir->input_dev, key, 1); ++ input_sync(appleir->input_dev); ++} ++ ++static void battery_flat(struct appleir *appleir) ++{ ++ dev_err(&appleir->input_dev->dev, "possible flat battery?\n"); ++} ++ ++static void key_up_tick(unsigned long data) ++{ ++ struct appleir *appleir = (struct appleir *)data; ++ ++ if (appleir->current_key) { ++ key_up(appleir, appleir->current_key); ++ appleir->current_key = 0; ++ } ++} ++ ++static void new_data(struct appleir *appleir, u8 *data, int len) ++{ ++ static const u8 keydown[] = { 0x25, 0x87, 0xee }; ++ static const u8 keyrepeat[] = { 0x26, }; ++ static const u8 flatbattery[] = { 0x25, 0x87, 0xe0 }; ++ ++ if (debug) ++ dump_packet(appleir, "received", data, len); ++ ++ if (len != 5) ++ return; ++ ++ if (!memcmp(data, keydown, sizeof(keydown))) { ++ int index; ++ ++ /* If we already have a key down, take it up before marking ++ this one down */ ++ if (appleir->current_key) ++ key_up(appleir, appleir->current_key); ++ ++ /* Handle dual packet commands */ ++ if (appleir->prev_key_idx > 0) ++ index = appleir->prev_key_idx; ++ else ++ index = get_key(data[4]); ++ ++ if (index > 0) { ++ appleir->current_key = appleir->keymap[index]; ++ ++ key_down(appleir, appleir->current_key); ++ /* Remote doesn't do key up, either pull them up, in the test ++ above, or here set a timer which pulls them up after 1/8 s */ ++ mod_timer(&appleir->key_up_timer, jiffies + HZ / 8); ++ appleir->prev_key_idx = 0; ++ return; ++ } else if (index == -7) { ++ /* Remember key for next packet */ ++ appleir->prev_key_idx = 0 - index; ++ return; ++ } ++ } ++ ++ appleir->prev_key_idx = 0; ++ ++ if (!memcmp(data, keyrepeat, sizeof(keyrepeat))) { ++ key_down(appleir, appleir->current_key); ++ /* Remote doesn't do key up, either pull them up, in the test ++ above, or here set a timer which pulls them up after 1/8 s */ ++ mod_timer(&appleir->key_up_timer, jiffies + HZ / 8); ++ return; ++ } ++ ++ if (!memcmp(data, flatbattery, sizeof(flatbattery))) { ++ battery_flat(appleir); ++ /* Fall through */ ++ } ++ ++ dump_packet(appleir, "unknown packet", data, len); ++} ++ ++static void appleir_urb(struct urb *urb) ++{ ++ struct appleir *appleir = urb->context; ++ int status = urb->status; ++ int retval; ++ ++ switch (status) { ++ case 0: ++ new_data(appleir, urb->transfer_buffer, urb->actual_length); ++ break; ++ case -ECONNRESET: ++ case -ENOENT: ++ case -ESHUTDOWN: ++ /* This urb is terminated, clean up */ ++ dbginfo(&appleir->input_dev->dev, "%s - urb shutting down with status: %d", __func__, ++ urb->status); ++ return; ++ default: ++ dbginfo(&appleir->input_dev->dev, "%s - nonzero urb status received: %d", __func__, ++ urb->status); ++ } ++ ++ retval = usb_submit_urb(urb, GFP_ATOMIC); ++ if (retval) ++ err("%s - usb_submit_urb failed with result %d", __func__, ++ retval); ++} ++ ++static int appleir_open(struct input_dev *dev) ++{ ++ struct appleir *appleir = input_get_drvdata(dev); ++ struct usb_interface *intf = usb_ifnum_to_if(appleir->usbdev, 0); ++ int r; ++ ++ r = usb_autopm_get_interface(intf); ++ if (r) { ++ dev_err(&intf->dev, ++ "%s(): usb_autopm_get_interface() = %d\n", __func__, r); ++ return r; ++ } ++ ++ mutex_lock(&appleir_mutex); ++ ++ if (usb_submit_urb(appleir->urb, GFP_ATOMIC)) { ++ r = -EIO; ++ goto fail; ++ } ++ ++ appleir->flags |= APPLEIR_OPENED; ++ ++ mutex_unlock(&appleir_mutex); ++ ++ usb_autopm_put_interface(intf); ++ ++ return 0; ++fail: ++ mutex_unlock(&appleir_mutex); ++ usb_autopm_put_interface(intf); ++ return r; ++} ++ ++static void appleir_close(struct input_dev *dev) ++{ ++ struct appleir *appleir = input_get_drvdata(dev); ++ ++ mutex_lock(&appleir_mutex); ++ ++ if (!(appleir->flags & APPLEIR_SUSPENDED)) { ++ usb_kill_urb(appleir->urb); ++ del_timer_sync(&appleir->key_up_timer); ++ } ++ ++ appleir->flags &= ~APPLEIR_OPENED; ++ ++ mutex_unlock(&appleir_mutex); ++} ++ ++static int appleir_probe(struct usb_interface *intf, ++ const struct usb_device_id *id) ++{ ++ struct usb_device *dev = interface_to_usbdev(intf); ++ struct usb_endpoint_descriptor *endpoint; ++ struct appleir *appleir = NULL; ++ struct input_dev *input_dev; ++ int retval = -ENOMEM; ++ int i; ++ ++ appleir = kzalloc(sizeof(struct appleir), GFP_KERNEL); ++ if (!appleir) ++ goto allocfail; ++ ++ appleir->data = usb_alloc_coherent(dev, URB_SIZE, GFP_KERNEL, ++ &appleir->dma_buf); ++ if (!appleir->data) ++ goto usbfail; ++ ++ appleir->urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (!appleir->urb) ++ goto urbfail; ++ ++ appleir->usbdev = dev; ++ ++ input_dev = input_allocate_device(); ++ if (!input_dev) ++ goto inputfail; ++ ++ appleir->input_dev = input_dev; ++ ++ usb_make_path(dev, appleir->phys, sizeof(appleir->phys)); ++ strlcpy(appleir->phys, "/input0", sizeof(appleir->phys)); ++ ++ input_dev->name = "Apple Infrared Remote Controller"; ++ input_dev->phys = appleir->phys; ++ usb_to_input_id(dev, &input_dev->id); ++ input_dev->dev.parent = &intf->dev; ++ input_dev->keycode = appleir->keymap; ++ input_dev->keycodesize = sizeof(unsigned short); ++ input_dev->keycodemax = ARRAY_SIZE(appleir->keymap); ++ ++ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); ++ ++ memcpy(appleir->keymap, appleir_key_table, sizeof(appleir->keymap)); ++ for (i = 0; i < ARRAY_SIZE(appleir_key_table); i++) ++ set_bit(appleir->keymap[i], input_dev->keybit); ++ clear_bit(KEY_RESERVED, input_dev->keybit); ++ ++ input_set_drvdata(input_dev, appleir); ++ input_dev->open = appleir_open; ++ input_dev->close = appleir_close; ++ ++ endpoint = &intf->cur_altsetting->endpoint[0].desc; ++ ++ usb_fill_int_urb(appleir->urb, dev, ++ usb_rcvintpipe(dev, endpoint->bEndpointAddress), ++ appleir->data, 8, ++ appleir_urb, appleir, endpoint->bInterval); ++ ++ appleir->urb->transfer_dma = appleir->dma_buf; ++ appleir->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; ++ ++ setup_timer(&appleir->key_up_timer, ++ key_up_tick, (unsigned long) appleir); ++ ++ retval = input_register_device(appleir->input_dev); ++ if (retval) ++ goto inputfail; ++ ++ usb_set_intfdata(intf, appleir); ++ ++ return 0; ++ ++inputfail: ++ input_free_device(appleir->input_dev); ++ ++urbfail: ++ usb_free_urb(appleir->urb); ++ ++usbfail: ++ usb_free_coherent(dev, URB_SIZE, appleir->data, ++ appleir->dma_buf); ++ ++allocfail: ++ kfree(appleir); ++ ++ return retval; ++} ++ ++static void appleir_disconnect(struct usb_interface *intf) ++{ ++ struct appleir *appleir = usb_get_intfdata(intf); ++ ++ usb_set_intfdata(intf, NULL); ++ input_unregister_device(appleir->input_dev); ++ usb_free_urb(appleir->urb); ++ usb_free_coherent(interface_to_usbdev(intf), URB_SIZE, ++ appleir->data, appleir->dma_buf); ++ kfree(appleir); ++} ++ ++static int appleir_suspend(struct usb_interface *interface, ++ pm_message_t message) ++{ ++ struct appleir *appleir = usb_get_intfdata(interface); ++ ++ mutex_lock(&appleir_mutex); ++ if (appleir->flags & APPLEIR_OPENED) ++ usb_kill_urb(appleir->urb); ++ ++ appleir->flags |= APPLEIR_SUSPENDED; ++ ++ mutex_unlock(&appleir_mutex); ++ ++ return 0; ++} ++ ++static int appleir_resume(struct usb_interface *interface) ++{ ++ struct appleir *appleir; ++ int r = 0; ++ ++ appleir = usb_get_intfdata(interface); ++ ++ mutex_lock(&appleir_mutex); ++ if (appleir->flags & APPLEIR_OPENED) { ++ struct usb_endpoint_descriptor *endpoint; ++ ++ endpoint = &interface->cur_altsetting->endpoint[0].desc; ++ usb_fill_int_urb(appleir->urb, appleir->usbdev, ++ usb_rcvintpipe(appleir->usbdev, endpoint->bEndpointAddress), ++ appleir->data, 8, ++ appleir_urb, appleir, endpoint->bInterval); ++ appleir->urb->transfer_dma = appleir->dma_buf; ++ appleir->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; ++ ++ /* And reset the USB device */ ++ if (usb_submit_urb(appleir->urb, GFP_ATOMIC)) ++ r = -EIO; ++ } ++ ++ appleir->flags &= ~APPLEIR_SUSPENDED; ++ ++ mutex_unlock(&appleir_mutex); ++ ++ return r; ++} ++ ++static struct usb_driver appleir_driver = { ++ .name = "appleir", ++ .probe = appleir_probe, ++ .disconnect = appleir_disconnect, ++ .suspend = appleir_suspend, ++ .resume = appleir_resume, ++ .reset_resume = appleir_resume, ++ .id_table = appleir_ids, ++}; ++ ++static int __init appleir_init(void) ++{ ++ return usb_register(&appleir_driver); ++} ++ ++static void __exit appleir_exit(void) ++{ ++ usb_deregister(&appleir_driver); ++} ++ ++module_init(appleir_init); ++module_exit(appleir_exit); +-- +1.7.2.2 + diff --git a/af_netlink-add-needed-scm_destroy-after-scm_send.patch b/af_netlink-add-needed-scm_destroy-after-scm_send.patch new file mode 100644 index 000000000..df8c69ca0 --- /dev/null +++ b/af_netlink-add-needed-scm_destroy-after-scm_send.patch @@ -0,0 +1,57 @@ +From: Eric W. Biederman +Date: Sun, 13 Jun 2010 03:31:06 +0000 (+0000) +Subject: af_netlink: Add needed scm_destroy after scm_send. +X-Git-Tag: v2.6.36-rc1~571^2~552 +X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=b47030c71dfd6c8cd5cb6e551b6f7f7cfc96f6a6 + +af_netlink: Add needed scm_destroy after scm_send. + +scm_send occasionally allocates state in the scm_cookie, so I have +modified netlink_sendmsg to guarantee that when scm_send succeeds +scm_destory will be called to free that state. + +Signed-off-by: Eric W. Biederman +Reviewed-by: Daniel Lezcano +Acked-by: Pavel Emelyanov +Signed-off-by: David S. Miller +--- + +diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c +index a2eb965..7aeaa83 100644 +--- a/net/netlink/af_netlink.c ++++ b/net/netlink/af_netlink.c +@@ -1323,19 +1323,23 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock, + if (msg->msg_flags&MSG_OOB) + return -EOPNOTSUPP; + +- if (NULL == siocb->scm) ++ if (NULL == siocb->scm) { + siocb->scm = &scm; ++ memset(&scm, 0, sizeof(scm)); ++ } + err = scm_send(sock, msg, siocb->scm); + if (err < 0) + return err; + + if (msg->msg_namelen) { ++ err = -EINVAL; + if (addr->nl_family != AF_NETLINK) +- return -EINVAL; ++ goto out; + dst_pid = addr->nl_pid; + dst_group = ffs(addr->nl_groups); ++ err = -EPERM; + if (dst_group && !netlink_capable(sock, NL_NONROOT_SEND)) +- return -EPERM; ++ goto out; + } else { + dst_pid = nlk->dst_pid; + dst_group = nlk->dst_group; +@@ -1387,6 +1391,7 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock, + err = netlink_unicast(sk, skb, dst_pid, msg->msg_flags&MSG_DONTWAIT); + + out: ++ scm_destroy(siocb->scm); + return err; + } + diff --git a/asix-add-USB-ID-for-Logitec-LAN-GTJ-U2A.patch b/asix-add-USB-ID-for-Logitec-LAN-GTJ-U2A.patch new file mode 100644 index 000000000..c0d7754a0 --- /dev/null +++ b/asix-add-USB-ID-for-Logitec-LAN-GTJ-U2A.patch @@ -0,0 +1,34 @@ +From f4680d3db71f13d2764340a9880745bf54f2469d Mon Sep 17 00:00:00 2001 +From: Arnaud Ebalard +Date: Wed, 15 Dec 2010 12:16:30 +0000 +Subject: [PATCH] asix: add USB ID for Logitec LAN-GTJ U2A + +Logitec LAN-GTJ U2A (http://www.pro.logitec.co.jp/pro/g/gLAN-GTJU2A/) +USB 2.0 10/10/1000 Ethernet adapter is based on ASIX AX88178 chipset. + +This patch adds missing USB ID for the device. + +Signed-off-by: Arnaud Ebalard +Signed-off-by: David S. Miller +--- + drivers/net/usb/asix.c | 4 ++++ + 1 files changed, 4 insertions(+), 0 deletions(-) + +diff --git a/drivers/net/usb/asix.c b/drivers/net/usb/asix.c +index aea4645..6140b56 100644 +--- a/drivers/net/usb/asix.c ++++ b/drivers/net/usb/asix.c +@@ -1508,6 +1508,10 @@ static const struct usb_device_id products [] = { + USB_DEVICE (0x0b95, 0x1780), + .driver_info = (unsigned long) &ax88178_info, + }, { ++ // Logitec LAN-GTJ/U2A ++ USB_DEVICE (0x0789, 0x0160), ++ .driver_info = (unsigned long) &ax88178_info, ++}, { + // Linksys USB200M Rev 2 + USB_DEVICE (0x13b1, 0x0018), + .driver_info = (unsigned long) &ax88772_info, +-- +1.7.6 + diff --git a/befs-Validate-length-of-long-symbolic-links.patch b/befs-Validate-length-of-long-symbolic-links.patch new file mode 100644 index 000000000..f53dfbfad --- /dev/null +++ b/befs-Validate-length-of-long-symbolic-links.patch @@ -0,0 +1,50 @@ +From 338d0f0a6fbc82407864606f5b64b75aeb3c70f2 Mon Sep 17 00:00:00 2001 +From: Timo Warns +Date: Wed, 17 Aug 2011 17:59:56 +0200 +Subject: [PATCH] befs: Validate length of long symbolic links. + +Signed-off-by: Timo Warns +Signed-off-by: Linus Torvalds +--- + fs/befs/linuxvfs.c | 23 ++++++++++++++--------- + 1 files changed, 14 insertions(+), 9 deletions(-) + +diff --git a/fs/befs/linuxvfs.c b/fs/befs/linuxvfs.c +index 54b8c28..720d885 100644 +--- a/fs/befs/linuxvfs.c ++++ b/fs/befs/linuxvfs.c +@@ -474,17 +474,22 @@ befs_follow_link(struct dentry *dentry, struct nameidata *nd) + befs_data_stream *data = &befs_ino->i_data.ds; + befs_off_t len = data->size; + +- befs_debug(sb, "Follow long symlink"); +- +- link = kmalloc(len, GFP_NOFS); +- if (!link) { +- link = ERR_PTR(-ENOMEM); +- } else if (befs_read_lsymlink(sb, data, link, len) != len) { +- kfree(link); +- befs_error(sb, "Failed to read entire long symlink"); ++ if (len == 0) { ++ befs_error(sb, "Long symlink with illegal length"); + link = ERR_PTR(-EIO); + } else { +- link[len - 1] = '\0'; ++ befs_debug(sb, "Follow long symlink"); ++ ++ link = kmalloc(len, GFP_NOFS); ++ if (!link) { ++ link = ERR_PTR(-ENOMEM); ++ } else if (befs_read_lsymlink(sb, data, link, len) != len) { ++ kfree(link); ++ befs_error(sb, "Failed to read entire long symlink"); ++ link = ERR_PTR(-EIO); ++ } else { ++ link[len - 1] = '\0'; ++ } + } + } else { + link = befs_ino->i_data.symlink; +-- +1.7.6 + diff --git a/block-check-for-proper-length-of-iov-entries-earlier-in-blk_rq_map_user_iov.patch b/block-check-for-proper-length-of-iov-entries-earlier-in-blk_rq_map_user_iov.patch new file mode 100644 index 000000000..0ad4928a0 --- /dev/null +++ b/block-check-for-proper-length-of-iov-entries-earlier-in-blk_rq_map_user_iov.patch @@ -0,0 +1,38 @@ +From: Xiaotian Feng +Date: Mon, 29 Nov 2010 09:03:55 +0000 (+0100) +Subject: block: check for proper length of iov entries earlier in blk_rq_map_user_iov() +X-Git-Tag: v2.6.37-rc7~10^2~5 +X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=54787556 + +block: check for proper length of iov entries earlier in blk_rq_map_user_iov() + +commit 9284bcf checks for proper length of iov entries in +blk_rq_map_user_iov(). But if the map is unaligned, kernel +will break out the loop without checking for the proper length. +So we need to check the proper length before the unalign check. + +Signed-off-by: Xiaotian Feng +Cc: stable@kernel.org +Signed-off-by: Jens Axboe +--- + +diff --git a/block/blk-map.c b/block/blk-map.c +index 5d5dbe4..e663ac2 100644 +--- a/block/blk-map.c ++++ b/block/blk-map.c +@@ -201,12 +201,13 @@ int blk_rq_map_user_iov(struct request_queue *q, struct request *rq, + for (i = 0; i < iov_count; i++) { + unsigned long uaddr = (unsigned long)iov[i].iov_base; + ++ if (!iov[i].iov_len) ++ return -EINVAL; ++ + if (uaddr & queue_dma_alignment(q)) { + unaligned = 1; + break; + } +- if (!iov[i].iov_len) +- return -EINVAL; + } + + if (unaligned || (q->dma_pad_mask & len) || map_data) diff --git a/bluetooth-prevent-buffer-overflow-in-l2cap-config-request.patch b/bluetooth-prevent-buffer-overflow-in-l2cap-config-request.patch new file mode 100644 index 000000000..e861b72ed --- /dev/null +++ b/bluetooth-prevent-buffer-overflow-in-l2cap-config-request.patch @@ -0,0 +1,32 @@ +From: Dan Rosenberg +Date: Fri, 24 Jun 2011 12:38:05 +0000 (-0400) +Subject: Bluetooth: Prevent buffer overflow in l2cap config request +X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Fpadovan%2Fbluetooth-2.6.git;a=commitdiff_plain;h=7ac28817536797fd40e9646452183606f9e17f71 + +Bluetooth: Prevent buffer overflow in l2cap config request +[ backport to 2.6.35 ] + +A remote user can provide a small value for the command size field in +the command header of an l2cap configuration request, resulting in an +integer underflow when subtracting the size of the configuration request +header. This results in copying a very large amount of data via +memcpy() and destroying the kernel heap. Check for underflow. + +Signed-off-by: Dan Rosenberg +Cc: stable +Signed-off-by: Gustavo F. Padovan +--- + +diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c +index 56fdd91..7d8a66b 100644 +--- a/net/bluetooth/l2cap.c ++++ b/net/bluetooth/l2cap.c +@@ -2962,7 +2962,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr + + /* Reject if config buffer is too small. */ + len = cmd_len - sizeof(*req); +- if (l2cap_pi(sk)->conf_len + len > sizeof(l2cap_pi(sk)->conf_req)) { ++ if (len < 0 || l2cap_pi(sk)->conf_len + len > sizeof(l2cap_pi(sk)->conf_req)) { + l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, + l2cap_build_conf_rsp(sk, rsp, + L2CAP_CONF_REJECT, flags), rsp); diff --git a/bridge-fix-mglist-corruption-that-leads-to-memory-corruption.patch b/bridge-fix-mglist-corruption-that-leads-to-memory-corruption.patch new file mode 100644 index 000000000..198a93b35 --- /dev/null +++ b/bridge-fix-mglist-corruption-that-leads-to-memory-corruption.patch @@ -0,0 +1,42 @@ +bridge: Fix mglist corruption that leads to memory corruption + +The list mp->mglist is used to indicate whether a multicast group +is active on the bridge interface itself as opposed to one of the +constituent interfaces in the bridge. + +Unfortunately the operation that adds the mp->mglist node to the +list neglected to check whether it has already been added. This +leads to list corruption in the form of nodes pointing to itself. + +Normally this would be quite obvious as it would cause an infinite +loop when walking the list. However, as this list is never actually +walked (which means that we don't really need it, I'll get rid of +it in a subsequent patch), this instead is hidden until we perform +a delete operation on the affected nodes. + +As the same node may now be pointed to by more than one node, the +delete operations can then cause modification of freed memory. + +This was observed in practice to cause corruption in 512-byte slabs, +most commonly leading to crashes in jbd2. + +Thanks to Josef Bacik for pointing me in the right direction. + +Reported-by: Ian Page Hands +Signed-off-by: Herbert Xu + +diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c +index f701a21..802d3f8 100644 +--- a/net/bridge/br_multicast.c ++++ b/net/bridge/br_multicast.c +@@ -719,7 +719,8 @@ static int br_multicast_add_group(struct net_bridge *br, + goto err; + + if (!port) { +- hlist_add_head(&mp->mglist, &br->mglist); ++ if (hlist_unhashed(&mp->mglist)) ++ hlist_add_head(&mp->mglist, &br->mglist); + mod_timer(&mp->timer, now + br->multicast_membership_interval); + goto out; + } + diff --git a/btrfs-fix-error-handling-in-btrfs_get_sub.patch b/btrfs-fix-error-handling-in-btrfs_get_sub.patch new file mode 100644 index 000000000..a91999510 --- /dev/null +++ b/btrfs-fix-error-handling-in-btrfs_get_sub.patch @@ -0,0 +1,65 @@ +From 8f172904b45a6b530eaa345b23956682629bddee Mon Sep 17 00:00:00 2001 +From: Josef Bacik +Date: Fri, 22 Oct 2010 15:26:53 -0400 +Subject: Btrfs: fix error handling in btrfs_get_sb + +If we failed to find the root subvol id, or the subvol=, we would +deactivate the locked super and close the devices. The problem is at this point +we have gotten the SB all setup, which includes setting super_operations, so +when we'd deactiveate the super, we'd do a close_ctree() which closes the +devices, so we'd end up closing the devices twice. So if you do something like +this + +mount /dev/sda1 /mnt/test1 +mount /dev/sda1 /mnt/test2 -o subvol=xxx +umount /mnt/test1 + +it would blow up (if subvol xxx doesn't exist). This patch fixes that problem. +Thanks, + +Signed-off-by: Josef Bacik +--- + fs/btrfs/super.c | 7 +++---- + 1 files changed, 3 insertions(+), 4 deletions(-) + +diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c +index f2393b3..c246f25 100644 +--- a/fs/btrfs/super.c ++++ b/fs/btrfs/super.c +@@ -629,7 +629,7 @@ static int btrfs_get_sb(struct file_system_type *fs_type, int flags, + if (IS_ERR(root)) { + error = PTR_ERR(root); + deactivate_locked_super(s); +- goto error; ++ goto error_free_subvol_name; + } + /* if they gave us a subvolume name bind mount into that */ + if (strcmp(subvol_name, ".")) { +@@ -643,14 +643,14 @@ static int btrfs_get_sb(struct file_system_type *fs_type, int flags, + deactivate_locked_super(s); + error = PTR_ERR(new_root); + dput(root); +- goto error_close_devices; ++ goto error_free_subvol_name; + } + if (!new_root->d_inode) { + dput(root); + dput(new_root); + deactivate_locked_super(s); + error = -ENXIO; +- goto error_close_devices; ++ goto error_free_subvol_name; + } + dput(root); + root = new_root; +@@ -668,7 +668,6 @@ error_close_devices: + btrfs_close_devices(fs_devices); + error_free_subvol_name: + kfree(subvol_name); +-error: + return error; + } + +-- +1.7.3.3 + diff --git a/btrfs-fix-race-between-btrfs_get_sb-and-unmount.patch b/btrfs-fix-race-between-btrfs_get_sb-and-unmount.patch new file mode 100644 index 000000000..f9ab0d619 --- /dev/null +++ b/btrfs-fix-race-between-btrfs_get_sb-and-unmount.patch @@ -0,0 +1,41 @@ +From 3d07b06c5d62e98b46ef21980e5c2a904990149f Mon Sep 17 00:00:00 2001 +From: Kyle McMartin +Date: Fri, 10 Dec 2010 10:32:29 -0500 +Subject: [PATCH] Btrfs - fix race between btrfs_get_sb() and umount + +When mounting a btrfs file system btrfs_test_super() may attempt to +use sb->s_fs_info, the btrfs root, of a super block that is going away +and that has had the btrfs root set to NULL in its ->put_super(). But +if the super block is going away it cannot be an existing super block +so we can return false in this case. + +Signed-off-by: Ian Kent +Signed-off-by: Chris Mason + +Conflicts: + + fs/btrfs/super.c +--- + fs/btrfs/super.c | 6 ++++++ + 1 files changed, 6 insertions(+), 0 deletions(-) + +diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c +index 89e299f..f4a4dd3 100644 +--- a/fs/btrfs/super.c ++++ b/fs/btrfs/super.c +@@ -551,6 +551,12 @@ static int btrfs_test_super(struct super_block *s, void *data) + struct btrfs_root *test_root = data; + struct btrfs_root *root = btrfs_sb(s); + ++ /* ++ * If this super block is going away, return false as it ++ * can't match as an existing super block. ++ */ ++ if (!atomic_read(&s->s_active)) ++ return 0; + return root->fs_info->fs_devices == test_root->fs_info->fs_devices; + } + +-- +1.7.3.3 + diff --git a/btrfs-fix-typo-in-fallocate-to-make-it-honor-actual-size.patch b/btrfs-fix-typo-in-fallocate-to-make-it-honor-actual-size.patch new file mode 100644 index 000000000..186d40a5b --- /dev/null +++ b/btrfs-fix-typo-in-fallocate-to-make-it-honor-actual-size.patch @@ -0,0 +1,51 @@ +From: Josef Bacik +Date: Mon, 22 Nov 2010 18:50:32 +0000 (+0000) +Subject: Btrfs: fix typo in fallocate to make it honor actual size +X-Git-Tag: v2.6.37-rc4~6^2~7 +X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=55a61d1d06a3dc443d0db8aaa613365dcb83b98a + +Btrfs: fix typo in fallocate to make it honor actual size + +[ Trivial backport to 2.6.35/2.6.36 ] + +There is a typo in __btrfs_prealloc_file_range() where we set the i_size to +actual_len/cur_offset, and then just set it to cur_offset again, and do the same +with btrfs_ordered_update_i_size(). This fixes it back to keeping i_size in a +local variable and then updating i_size properly. Tested this with + +xfs_io -F -f -c "falloc 0 1" -c "pwrite 0 1" foo + +stat'ing foo gives us a size of 1 instead of 4096 like it was. Thanks, + +Signed-off-by: Josef Bacik +Signed-off-by: Chris Mason +--- + +diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c +index 37cc177..0058fb3 100644 +--- a/fs/btrfs/inode.c ++++ b/fs/btrfs/inode.c +@@ -7002,6 +7002,7 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode, + struct btrfs_root *root = BTRFS_I(inode)->root; + struct btrfs_key ins; + u64 cur_offset = start; ++ u64 i_size; + int ret = 0; + + while (num_bytes > 0) { +@@ -7043,11 +7044,11 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode, + (actual_len > inode->i_size) && + (cur_offset > inode->i_size)) { + if (cur_offset > actual_len) +- i_size_write(inode, actual_len); ++ i_size = actual_len; + else +- i_size_write(inode, cur_offset); +- i_size_write(inode, cur_offset); +- btrfs_ordered_update_i_size(inode, cur_offset, NULL); ++ i_size = cur_offset; ++ i_size_write(inode, i_size); ++ btrfs_ordered_update_i_size(inode, i_size, NULL); + } + + ret = btrfs_update_inode(trans, root, inode); diff --git a/btrfs-setup-blank-root-and-fs_info-for-mount-time.patch b/btrfs-setup-blank-root-and-fs_info-for-mount-time.patch new file mode 100644 index 000000000..183927438 --- /dev/null +++ b/btrfs-setup-blank-root-and-fs_info-for-mount-time.patch @@ -0,0 +1,117 @@ +From 2049a8887f699650cd66c3da220e84d5c140c546 Mon Sep 17 00:00:00 2001 +From: Kyle McMartin +Date: Fri, 10 Dec 2010 10:09:15 -0500 +Subject: [PATCH] Btrfs: setup blank root and fs_info for mount time + +There is a problem with how we use sget, it searches through the list of supers +attached to the fs_type looking for a super with the same fs_devices as what +we're trying to mount. This depends on sb->s_fs_info being filled, but we don't +fill that in until we get to btrfs_fill_super, so we could hit supers on the +fs_type super list that have a null s_fs_info. In order to fix that we need to +go ahead and setup a blank root with a blank fs_info to hold fs_devices, that +way our test will work out right and then we can set s_fs_info in +btrfs_set_super, and then open_ctree will simply use our pre-allocated root and +fs_info when setting everything up. Thanks, + +Signed-off-by: Josef Bacik +Signed-off-by: Chris Mason + +Conflicts: + + fs/btrfs/super.c +--- + fs/btrfs/disk-io.c | 6 ++---- + fs/btrfs/super.c | 34 +++++++++++++++++++++++++++++++--- + 2 files changed, 33 insertions(+), 7 deletions(-) + +diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c +index 34f7c37..b6c3dad 100644 +--- a/fs/btrfs/disk-io.c ++++ b/fs/btrfs/disk-io.c +@@ -1539,10 +1539,8 @@ struct btrfs_root *open_ctree(struct super_block *sb, + GFP_NOFS); + struct btrfs_root *csum_root = kzalloc(sizeof(struct btrfs_root), + GFP_NOFS); +- struct btrfs_root *tree_root = kzalloc(sizeof(struct btrfs_root), +- GFP_NOFS); +- struct btrfs_fs_info *fs_info = kzalloc(sizeof(*fs_info), +- GFP_NOFS); ++ struct btrfs_root *tree_root = btrfs_sb(sb); ++ struct btrfs_fs_info *fs_info = tree_root->fs_info; + struct btrfs_root *chunk_root = kzalloc(sizeof(struct btrfs_root), + GFP_NOFS); + struct btrfs_root *dev_root = kzalloc(sizeof(struct btrfs_root), +diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c +index f2393b3..89e299f 100644 +--- a/fs/btrfs/super.c ++++ b/fs/btrfs/super.c +@@ -548,12 +548,20 @@ static int btrfs_show_options(struct seq_file *seq, struct vfsmount *vfs) + + static int btrfs_test_super(struct super_block *s, void *data) + { +- struct btrfs_fs_devices *test_fs_devices = data; ++ struct btrfs_root *test_root = data; + struct btrfs_root *root = btrfs_sb(s); + +- return root->fs_info->fs_devices == test_fs_devices; ++ return root->fs_info->fs_devices == test_root->fs_info->fs_devices; + } + ++static int btrfs_set_super(struct super_block *s, void *data) ++{ ++ s->s_fs_info = data; ++ ++ return set_anon_super(s, data); ++} ++ ++ + /* + * Find a superblock for the given device / mount point. + * +@@ -567,6 +575,8 @@ static int btrfs_get_sb(struct file_system_type *fs_type, int flags, + struct super_block *s; + struct dentry *root; + struct btrfs_fs_devices *fs_devices = NULL; ++ struct btrfs_root *tree_root = NULL; ++ struct btrfs_fs_info *fs_info = NULL; + fmode_t mode = FMODE_READ; + char *subvol_name = NULL; + u64 subvol_objectid = 0; +@@ -595,8 +605,24 @@ static int btrfs_get_sb(struct file_system_type *fs_type, int flags, + goto error_close_devices; + } + ++ /* ++ * Setup a dummy root and fs_info for test/set super. This is because ++ * we don't actually fill this stuff out until open_ctree, but we need ++ * it for searching for existing supers, so this lets us do that and ++ * then open_ctree will properly initialize everything later. ++ */ ++ fs_info = kzalloc(sizeof(struct btrfs_fs_info), GFP_NOFS); ++ tree_root = kzalloc(sizeof(struct btrfs_root), GFP_NOFS); ++ if (!fs_info || !tree_root) { ++ error = -ENOMEM; ++ goto error_close_devices; ++ } ++ fs_info->tree_root = tree_root; ++ fs_info->fs_devices = fs_devices; ++ tree_root->fs_info = fs_info; ++ + bdev = fs_devices->latest_bdev; +- s = sget(fs_type, btrfs_test_super, set_anon_super, fs_devices); ++ s = sget(fs_type, btrfs_test_super, btrfs_set_super, tree_root); + if (IS_ERR(s)) + goto error_s; + +@@ -666,6 +692,8 @@ error_s: + error = PTR_ERR(s); + error_close_devices: + btrfs_close_devices(fs_devices); ++ kfree(fs_info); ++ kfree(tree_root); + error_free_subvol_name: + kfree(subvol_name); + error: +-- +1.7.3.3 + diff --git a/cifs-fix-possible-memory-corruption-in-CIFSFindNext.patch b/cifs-fix-possible-memory-corruption-in-CIFSFindNext.patch new file mode 100644 index 000000000..06fc5c74a --- /dev/null +++ b/cifs-fix-possible-memory-corruption-in-CIFSFindNext.patch @@ -0,0 +1,82 @@ +Path: news.gmane.org!not-for-mail +From: Jeff Layton +Newsgroups: gmane.linux.kernel.cifs +Subject: [PATCH] cifs: fix possible memory corruption in CIFSFindNext +Date: Tue, 23 Aug 2011 07:21:28 -0400 +Lines: 37 +Approved: news@gmane.org +Message-ID: <1314098488-1547-1-git-send-email-jlayton@redhat.com> +NNTP-Posting-Host: lo.gmane.org +X-Trace: dough.gmane.org 1314098501 27164 80.91.229.12 (23 Aug 2011 11:21:41 GMT) +X-Complaints-To: usenet@dough.gmane.org +NNTP-Posting-Date: Tue, 23 Aug 2011 11:21:41 +0000 (UTC) +Cc: linux-cifs-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, dcl-HN4QTLPn1qTvY7RNz7mR4EEOCMrvLtNR@public.gmane.org +To: smfrench-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org +Original-X-From: linux-cifs-owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org Tue Aug 23 13:21:37 2011 +Return-path: +Envelope-to: glkc-linux-cifs-1dZseelyfdZg9hUCZPvPmw@public.gmane.org +Original-Received: from vger.kernel.org ([209.132.180.67]) + by lo.gmane.org with esmtp (Exim 4.69) + (envelope-from ) + id 1Qvp33-0003JC-05 + for glkc-linux-cifs-1dZseelyfdZg9hUCZPvPmw@public.gmane.org; Tue, 23 Aug 2011 13:21:37 +0200 +Original-Received: (majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org) by vger.kernel.org via listexpand + id S1752435Ab1HWLVg (ORCPT ); + Tue, 23 Aug 2011 07:21:36 -0400 +Original-Received: from mail-gy0-f174.google.com ([209.85.160.174]:43114 "EHLO + mail-gy0-f174.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org + with ESMTP id S1751065Ab1HWLVf (ORCPT + ); Tue, 23 Aug 2011 07:21:35 -0400 +Original-Received: by gya6 with SMTP id 6so4228912gya.19 + for ; Tue, 23 Aug 2011 04:21:35 -0700 (PDT) +Original-Received: by 10.101.144.18 with SMTP id w18mr3505731ann.133.1314098494691; + Tue, 23 Aug 2011 04:21:34 -0700 (PDT) +Original-Received: from salusa.poochiereds.net (cpe-075-177-182-191.nc.res.rr.com [75.177.182.191]) + by mx.google.com with ESMTPS id d33sm48355ano.35.2011.08.23.04.21.32 + (version=SSLv3 cipher=OTHER); + Tue, 23 Aug 2011 04:21:33 -0700 (PDT) +X-Mailer: git-send-email 1.7.6 +Original-Sender: linux-cifs-owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org +Precedence: bulk +List-ID: +X-Mailing-List: linux-cifs-u79uwXL29TY76Z2rM5mHXA@public.gmane.org +Xref: news.gmane.org gmane.linux.kernel.cifs:4006 +Archived-At: + +The name_len variable in CIFSFindNext is a signed int that gets set to +the resume_name_len in the cifs_search_info. The resume_name_len however +is unsigned and for some infolevels is populated directly from a 32 bit +value sent by the server. + +If the server sends a very large value for this, then that value could +look negative when converted to a signed int. That would make that +value pass the PATH_MAX check later in CIFSFindNext. The name_len would +then be used as a length value for a memcpy. It would then be treated +as unsigned again, and the memcpy scribbles over a ton of memory. + +Fix this by making the name_len an unsigned value in CIFSFindNext. + +Cc: +Reported-by: Darren Lavender +Signed-off-by: Jeff Layton +--- + fs/cifs/cifssmb.c | 3 ++- + 1 files changed, 2 insertions(+), 1 deletions(-) + +diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c +index f4d0988..950464d 100644 +--- a/fs/cifs/cifssmb.c ++++ b/fs/cifs/cifssmb.c +@@ -4089,7 +4089,8 @@ int CIFSFindNext(const int xid, struct cifs_tcon *tcon, + T2_FNEXT_RSP_PARMS *parms; + char *response_data; + int rc = 0; +- int bytes_returned, name_len; ++ int bytes_returned; ++ unsigned int name_len; + __u16 params, byte_count; + + cFYI(1, "In FindNext"); +-- +1.7.6 + diff --git a/config-debug b/config-debug index f8c7c00b1..6c6209e6b 100644 --- a/config-debug +++ b/config-debug @@ -87,5 +87,7 @@ CONFIG_PM_ADVANCED_DEBUG=y CONFIG_CEPH_FS_PRETTYDEBUG=y CONFIG_QUOTA_DEBUG=y +# CONFIG_PCI_DEFAULT_USE_CRS is not set + CONFIG_KGDB_KDB=y CONFIG_KDB_KEYBOARD=y diff --git a/config-generic b/config-generic index f948700b2..b7701bdc7 100644 --- a/config-generic +++ b/config-generic @@ -684,7 +684,7 @@ CONFIG_MD_RAID0=m CONFIG_MD_RAID1=m CONFIG_MD_RAID10=m CONFIG_MD_RAID456=m -CONFIG_MULTICORE_RAID456=y +# CONFIG_MULTICORE_RAID456 is not set CONFIG_ASYNC_RAID6_TEST=m CONFIG_BLK_DEV_DM=y CONFIG_DM_CRYPT=m @@ -1423,11 +1423,11 @@ CONFIG_ATMEL=m CONFIG_B43=m CONFIG_B43_PCMCIA=y CONFIG_B43_SDIO=y -CONFIG_B43_DEBUG=y +# CONFIG_B43_DEBUG is not set CONFIG_B43_PHY_LP=y # CONFIG_B43_FORCE_PIO is not set CONFIG_B43LEGACY=m -CONFIG_B43LEGACY_DEBUG=y +# CONFIG_B43LEGACY_DEBUG is not set CONFIG_B43LEGACY_DMA=y CONFIG_B43LEGACY_PIO=y CONFIG_B43LEGACY_DMA_AND_PIO_MODE=y @@ -2193,7 +2193,7 @@ CONFIG_WDTPCI=m # CONFIG_ACQUIRE_WDT is not set # CONFIG_ADVANTECH_WDT is not set # CONFIG_EUROTECH_WDT is not set -# CONFIG_IB700_WDT is not set +CONFIG_IB700_WDT=m # CONFIG_MIXCOMWD is not set # CONFIG_SCx200_WDT is not set # CONFIG_60XX_WDT is not set @@ -2308,6 +2308,7 @@ CONFIG_DRM_NOUVEAU=m CONFIG_DRM_NOUVEAU_BACKLIGHT=y CONFIG_DRM_NOUVEAU_DEBUG=y CONFIG_DRM_I2C_CH7006=m +CONFIG_DRM_I2C_SIL164=m CONFIG_DRM_VMWGFX=m # @@ -2366,6 +2367,7 @@ CONFIG_VIDEO_EM28XX_DVB=m CONFIG_VIDEO_CX231XX=m CONFIG_VIDEO_CX231XX_ALSA=m CONFIG_VIDEO_CX231XX_DVB=m +CONFIG_VIDEO_CX231XX_RC=y CONFIG_VIDEO_HEXIUM_ORION=m CONFIG_VIDEO_HEXIUM_GEMINI=m CONFIG_VIDEO_IVTV=m @@ -2380,6 +2382,7 @@ CONFIG_VIDEO_SAA6588=m CONFIG_VIDEO_SAA7134=m CONFIG_VIDEO_SAA7134_ALSA=m CONFIG_VIDEO_SAA7134_DVB=m +CONFIG_VIDEO_SAA7134_RC=y CONFIG_VIDEO_STRADIS=m CONFIG_VIDEO_USBVISION=m CONFIG_VIDEO_W9966=m @@ -2394,6 +2397,11 @@ CONFIG_VIDEO_ZORAN_ZR36060=m CONFIG_VIDEO_FB_IVTV=m CONFIG_VIDEO_SAA7164=m CONFIG_VIDEO_TLG2300=m +# CONFIG_VIDEO_TIMBERDALE is not set +CONFIG_VIDEO_SR030PC30=m +# Doesn't build on 2.6.35 +# CONFIG_VIDEO_VIA_CAMERA is not set +# CONFIG_VIDEO_NOON010PC30 is not set CONFIG_USB_VIDEO_CLASS=m CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y @@ -2409,6 +2417,7 @@ CONFIG_MEDIA_ATTACH=y CONFIG_MEDIA_TUNER_CUSTOMISE=y CONFIG_MEDIA_TUNER_SIMPLE=m CONFIG_MEDIA_TUNER_TDA8290=m +CONFIG_MEDIA_TUNER_TDA18218=m CONFIG_MEDIA_TUNER_TEA5761=m CONFIG_MEDIA_TUNER_TEA5767=m CONFIG_MEDIA_TUNER_MT20XX=m @@ -2491,6 +2500,10 @@ CONFIG_DVB_ATBM8830=m CONFIG_DVB_TDA665x=m CONFIG_DVB_STV0299=m CONFIG_DVB_MB86A16=m +CONFIG_DVB_USB_LME2510=m +CONFIG_DVB_S5H1432=m +CONFIG_DVB_MB86A20S=m +CONFIG_DVB_IX2505V=m # # Supported Frontend Modules @@ -2517,6 +2530,7 @@ CONFIG_DVB_LGS8GL5=m CONFIG_DVB_DUMMY_FE=m CONFIG_DVB_FIREDTV=m CONFIG_DVB_NGENE=m +# CONFIG_DVB_CXD2099 is not set # # Supported SAA7146 based PCI Adapters @@ -2571,6 +2585,8 @@ CONFIG_DVB_PT1=m CONFIG_MANTIS_CORE=m CONFIG_DVB_MANTIS=m CONFIG_DVB_HOPPER=m +CONFIG_DVB_USB_TECHNISAT_USB2=m +CONFIG_DVB_DIB9000=m CONFIG_VIDEO_SAA7146=m CONFIG_VIDEO_SAA7146_VV=m @@ -2581,14 +2597,22 @@ CONFIG_VIDEO_PVRUSB2_SYSFS=y # CONFIG_VIDEO_PVRUSB2_DEBUGIFC is not set CONFIG_RC_MAP=m +CONFIG_RC_CORE=m CONFIG_IR_NEC_DECODER=m CONFIG_IR_RC5_DECODER=m CONFIG_IR_RC6_DECODER=m CONFIG_IR_JVC_DECODER=m CONFIG_IR_SONY_DECODER=m +CONFIG_IR_RC5_SZ_DECODER=m CONFIG_IR_LIRC_CODEC=m +CONFIG_IR_ENE=m CONFIG_IR_IMON=m +CONFIG_IR_ITE_CIR=m CONFIG_IR_MCEUSB=m +CONFIG_IR_NUVOTON=m +CONFIG_IR_STREAMZAP=m +CONFIG_IR_WINBOND_CIR=m +CONFIG_RC_LOOPBACK=m CONFIG_V4L_MEM2MEM_DRIVERS=y # CONFIG_VIDEO_MEM2MEM_TESTDEV is not set @@ -3031,6 +3055,8 @@ CONFIG_USB_GL860=m CONFIG_USB_GSPCA_JEILINJ=m CONFIG_USB_GSPCA_SPCA1528=m CONFIG_USB_GSPCA_SQ930X=m +CONFIG_USB_GSPCA_KONICA=m +CONFIG_USB_GSPCA_XIRLINK_CIT=m CONFIG_USB_IBMCAM=m CONFIG_USB_KONICAWC=m @@ -3038,6 +3064,7 @@ CONFIG_USB_KONICAWC=m CONFIG_USB_S2255=m CONFIG_USB_SE401=m # CONFIG_VIDEO_SH_MOBILE_CEU is not set +# CONFIG_VIDEO_SH_MOBILE_CSI2 is not set # CONFIG_USB_STV680 is not set # CONFIG_USB_SN9C102 is not set CONFIG_USB_ZR364XX=m @@ -3052,6 +3079,9 @@ CONFIG_SOC_CAMERA_OV772X=m CONFIG_SOC_CAMERA_MT9T112=m CONFIG_SOC_CAMERA_RJ54N1=m CONFIG_SOC_CAMERA_OV9640=m +CONFIG_SOC_CAMERA_IMX074=m +CONFIG_SOC_CAMERA_OV6650=m +CONFIG_SOC_CAMERA_OV2640=m # # USB Network adaptors @@ -3321,7 +3351,8 @@ CONFIG_QUOTACTL=y CONFIG_DNOTIFY=y # Autofsv3 is obsolete. # CONFIG_AUTOFS_FS is not set -CONFIG_AUTOFS4_FS=m +# systemd is dependant upon AUTOFS, so build it in. +CONFIG_AUTOFS4_FS=y CONFIG_EXOFS_FS=m # CONFIG_EXOFS_DEBUG is not set CONFIG_NILFS2_FS=m @@ -3601,6 +3632,7 @@ CONFIG_CRYPTO_FIPS=y CONFIG_CRYPTO_HW=y CONFIG_CRYPTO_BLKCIPHER=y CONFIG_CRYPTO_MANAGER=m +CONFIG_CRYPTO_MANAGER_TESTS=y # CONFIG_CRYPTO_CRYPTD is not set CONFIG_CRYPTO_AES=m CONFIG_CRYPTO_ARC4=m @@ -3722,7 +3754,7 @@ CONFIG_BLK_CGROUP=y # CONFIG_SYSFS_DEPRECATED_V2 is not set CONFIG_RELAY=y -# CONFIG_PRINTK_TIME is not set +CONFIG_PRINTK_TIME=y CONFIG_ENABLE_MUST_CHECK=y # CONFIG_ENABLE_WARN_DEPRECATED is not set @@ -3742,7 +3774,7 @@ CONFIG_IBMASR=m CONFIG_PM_DEBUG=y CONFIG_PM_TRACE=y # CONFIG_PM_VERBOSE is not set -CONFIG_PM_TEST_SUSPEND=y +# CONFIG_PM_TEST_SUSPEND is not set CONFIG_PM_RUNTIME=y ## BEGIN ISA Junk. @@ -3851,6 +3883,7 @@ CONFIG_RADIO_ADAPTERS=y # CONFIG_RADIO_TYPHOON is not set # CONFIG_RADIO_ZOLTRIX is not set # CONFIG_RADIO_SAA7706H is not set +# CONFIG_RADIO_WL1273 is not set # CONFIG_SND_OPL4_LIB is not set # CONFIG_SND_AD1816A is not set @@ -3987,8 +4020,8 @@ CONFIG_AUXDISPLAY=y CONFIG_UIO=m CONFIG_UIO_CIF=m CONFIG_UIO_SMX=m -CONFIG_UIO_PDRV=m -CONFIG_UIO_PDRV_GENIRQ=m +# CONFIG_UIO_PDRV is not set +# CONFIG_UIO_PDRV_GENIRQ is not set CONFIG_UIO_AEC=m CONFIG_UIO_SERCOS3=m CONFIG_UIO_PCI_GENERIC=m @@ -4000,20 +4033,16 @@ CONFIG_UIO_PCI_GENERIC=m # LIRC CONFIG_LIRC_STAGING=y CONFIG_LIRC_BT829=m -CONFIG_LIRC_ENE0100=m CONFIG_LIRC_I2C=m CONFIG_LIRC_IGORPLUGUSB=m CONFIG_LIRC_IMON=m -CONFIG_LIRC_IT87=m -CONFIG_LIRC_ITE8709=m -CONFIG_LIRC_MCEUSB=m CONFIG_LIRC_ZILOG=m CONFIG_LIRC_PARALLEL=m CONFIG_LIRC_SERIAL=m CONFIG_LIRC_SERIAL_TRANSMITTER=y CONFIG_LIRC_SASEM=m CONFIG_LIRC_SIR=m -CONFIG_LIRC_STREAMZAP=m +# CONFIG_LIRC_STREAMZAP is not set CONFIG_LIRC_TTUSBIR=m # CONFIG_SAMPLES is not set @@ -4175,7 +4204,7 @@ CONFIG_USB_ATMEL=m # CONFIG_RCU_CPU_STALL_DETECTOR is not set # CONFIG_DEBUG_BLOCK_EXT_DEVT is not set # CONFIG_FUNCTION_GRAPH_TRACER is not set -CONFIG_BOOT_TRACER=y +# CONFIG_BOOT_TRACER is not set CONFIG_EARLY_PRINTK_DBGP=y CONFIG_SECURITYFS=y @@ -4253,7 +4282,7 @@ CONFIG_DEBUG_NX_TEST=m CONFIG_DEBUG_BOOT_PARAMS=y CONFIG_DETECT_SOFTLOCKUP=y # CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set -CONFIG_DETECT_HUNG_TASK=y +# CONFIG_DETECT_HUNG_TASK is not set # CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set CONFIG_ATOMIC64_SELFTEST=y @@ -4269,7 +4298,7 @@ CONFIG_BLK_DEV_DRBD=m # CONFIG_DEBUG_GPIO is not set # CONFIG_W1_MASTER_GPIO is not set # CONFIG_LEDS_GPIO is not set -# CONFIG_GPIO_SYSFS is not set +CONFIG_GPIO_SYSFS=y # CONFIG_GPIO_MAX732X is not set # CONFIG_GPIO_PCA953X is not set # CONFIG_GPIO_PCF857X is not set diff --git a/config-i686-PAE b/config-i686-PAE index 1e58e65b2..eebaa6fba 100644 --- a/config-i686-PAE +++ b/config-i686-PAE @@ -3,3 +3,6 @@ CONFIG_HIGHMEM64G=y CONFIG_XEN_DEV_EVTCHN=m CONFIG_XEN_SYS_HYPERVISOR=y + +# I2O only works on non-PAE 32-bit x86 +# CONFIG_I2O is not set diff --git a/config-local b/config-local new file mode 100644 index 000000000..8c32be5be --- /dev/null +++ b/config-local @@ -0,0 +1,2 @@ +# This file is intentionally left empty in the stock kernel. Its a nicety +# added for those wanting to do custom rebuilds with altered config opts. diff --git a/config-nodebug b/config-nodebug index 5c7f4364e..99a780cf0 100644 --- a/config-nodebug +++ b/config-nodebug @@ -2,90 +2,92 @@ CONFIG_SND_VERBOSE_PRINTK=y CONFIG_SND_DEBUG=y CONFIG_SND_PCM_XRUN_DEBUG=y -CONFIG_DEBUG_MUTEXES=y -CONFIG_DEBUG_RT_MUTEXES=y -CONFIG_DEBUG_LOCK_ALLOC=y -CONFIG_PROVE_LOCKING=y -CONFIG_DEBUG_VM=y -CONFIG_DEBUG_SPINLOCK=y -CONFIG_PROVE_RCU=y +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_PROVE_RCU is not set # CONFIG_PROVE_RCU_REPEATEDLY is not set -CONFIG_DEBUG_PER_CPU_MAPS=y +# CONFIG_DEBUG_PER_CPU_MAPS is not set CONFIG_CPUMASK_OFFSTACK=y -CONFIG_CPU_NOTIFIER_ERROR_INJECT=m +# CONFIG_CPU_NOTIFIER_ERROR_INJECT is not set -CONFIG_FAULT_INJECTION=y -CONFIG_FAILSLAB=y -CONFIG_FAIL_PAGE_ALLOC=y -CONFIG_FAIL_MAKE_REQUEST=y -CONFIG_FAULT_INJECTION_DEBUG_FS=y -CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y -CONFIG_FAIL_IO_TIMEOUT=y +# CONFIG_FAULT_INJECTION is not set +# CONFIG_FAILSLAB is not set +# CONFIG_FAIL_PAGE_ALLOC is not set +# CONFIG_FAIL_MAKE_REQUEST is not set +# CONFIG_FAULT_INJECTION_DEBUG_FS is not set +# CONFIG_FAULT_INJECTION_STACKTRACE_FILTER is not set +# CONFIG_FAIL_IO_TIMEOUT is not set -CONFIG_SLUB_DEBUG_ON=y +# CONFIG_SLUB_DEBUG_ON is not set -CONFIG_LOCK_STAT=y +# CONFIG_LOCK_STAT is not set -CONFIG_DEBUG_STACK_USAGE=y +# CONFIG_DEBUG_STACK_USAGE is not set -CONFIG_ACPI_DEBUG=y +# CONFIG_ACPI_DEBUG is not set # CONFIG_ACPI_DEBUG_FUNC_TRACE is not set -CONFIG_DEBUG_SG=y +# CONFIG_DEBUG_SG is not set # CONFIG_DEBUG_PAGEALLOC is not set -CONFIG_DEBUG_WRITECOUNT=y -CONFIG_DEBUG_OBJECTS=y +# CONFIG_DEBUG_WRITECOUNT is not set +# CONFIG_DEBUG_OBJECTS is not set # CONFIG_DEBUG_OBJECTS_SELFTEST is not set -CONFIG_DEBUG_OBJECTS_FREE=y -CONFIG_DEBUG_OBJECTS_TIMERS=y +# CONFIG_DEBUG_OBJECTS_FREE is not set +# CONFIG_DEBUG_OBJECTS_TIMERS is not set CONFIG_DEBUG_OBJECTS_ENABLE_DEFAULT=1 -CONFIG_X86_PTDUMP=y +# CONFIG_X86_PTDUMP is not set -CONFIG_CAN_DEBUG_DEVICES=y +# CONFIG_CAN_DEBUG_DEVICES is not set -CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set -CONFIG_SYSCTL_SYSCALL_CHECK=y +# CONFIG_SYSCTL_SYSCALL_CHECK is not set -CONFIG_DEBUG_NOTIFIERS=y +# CONFIG_DEBUG_NOTIFIERS is not set -CONFIG_DMA_API_DEBUG=y +# CONFIG_DMA_API_DEBUG is not set -CONFIG_MMIOTRACE=y +# CONFIG_MMIOTRACE is not set -CONFIG_DEBUG_CREDENTIALS=y +# CONFIG_DEBUG_CREDENTIALS is not set # off in both production debug and nodebug builds, # on in rawhide nodebug builds -CONFIG_DEBUG_FORCE_WEAK_PER_CPU=y +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set -CONFIG_EXT4_DEBUG=y +# CONFIG_EXT4_DEBUG is not set -CONFIG_DEBUG_PERF_USE_VMALLOC=y +# CONFIG_DEBUG_PERF_USE_VMALLOC is not set -CONFIG_JBD2_DEBUG=y +# CONFIG_JBD2_DEBUG is not set -CONFIG_DEBUG_CFQ_IOSCHED=y +# CONFIG_DEBUG_CFQ_IOSCHED is not set -CONFIG_DRBD_FAULT_INJECTION=y +# CONFIG_DRBD_FAULT_INJECTION is not set -CONFIG_ATH_DEBUG=y -CONFIG_IWLWIFI_DEVICE_TRACING=y +# CONFIG_ATH_DEBUG is not set +# CONFIG_IWLWIFI_DEVICE_TRACING is not set -CONFIG_DEBUG_OBJECTS_WORK=y -CONFIG_DEBUG_STRICT_USER_COPY_CHECKS=y +# CONFIG_DEBUG_OBJECTS_WORK is not set +# CONFIG_DEBUG_STRICT_USER_COPY_CHECKS is not set -CONFIG_DMADEVICES_DEBUG=y -CONFIG_DMADEVICES_VDEBUG=y +# CONFIG_DMADEVICES_DEBUG is not set +# CONFIG_DMADEVICES_VDEBUG is not set CONFIG_PM_ADVANCED_DEBUG=y -CONFIG_CEPH_FS_PRETTYDEBUG=y -CONFIG_QUOTA_DEBUG=y +# CONFIG_CEPH_FS_PRETTYDEBUG is not set +# CONFIG_QUOTA_DEBUG is not set + +# CONFIG_PCI_DEFAULT_USE_CRS is not set CONFIG_KGDB_KDB=y CONFIG_KDB_KEYBOARD=y diff --git a/config-sparc64-generic b/config-sparc64-generic index 1d21fa781..1a2cc5cdb 100644 --- a/config-sparc64-generic +++ b/config-sparc64-generic @@ -200,4 +200,9 @@ CONFIG_SERIAL_GRLIB_GAISLER_APBUART=m CONFIG_GRETH=m CONFIG_FB_XVR1000=y -CONFIG_CRYPTO_DEV_NIAGARA2=y +CONFIG_CRYPTO_DEV_NIAGARA2=m + +# Bellow is changes made to get the kernel building on sparc again, they need to have upstream fixes +# CONFIG_DEBUG_STRICT_USER_COPY_CHECKS is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_INFINIBAND is not set diff --git a/config-x86-generic b/config-x86-generic index 58a8cf11b..65e788a08 100644 --- a/config-x86-generic +++ b/config-x86-generic @@ -37,7 +37,7 @@ CONFIG_M686=y # CONFIG_MCYRIXIII is not set # CONFIG_MVIAC3_2 is not set CONFIG_SMP=y -CONFIG_NR_CPUS=32 +CONFIG_NR_CPUS=64 CONFIG_X86_GENERIC=y # CONFIG_X86_PPRO_FENCE is not set CONFIG_HPET=y @@ -100,6 +100,16 @@ CONFIG_SECCOMP=y CONFIG_CAPI_EICON=y +# I2O enabled only for 32-bit x86, disabled for PAE kernel +CONFIG_I2O=m +CONFIG_I2O_BLOCK=m +CONFIG_I2O_SCSI=m +CONFIG_I2O_PROC=m +CONFIG_I2O_CONFIG=y +CONFIG_I2O_EXT_ADAPTEC=y +CONFIG_I2O_CONFIG_OLD_IOCTL=y +CONFIG_I2O_BUS=m + # # APM (Advanced Power Management) BIOS Support # @@ -479,6 +489,8 @@ CONFIG_TOSHIBA_BT_RFKILL=m CONFIG_VGA_SWITCHEROO=y CONFIG_LPC_SCH=m -CONFIG_INTEL_IDLE=m +CONFIG_INTEL_IDLE=y CONFIG_PCI_CNB20LE_QUIRK=y + +CONFIG_IRQ_TIME_ACCOUNTING=y diff --git a/config-x86_64-generic b/config-x86_64-generic index 204dfff62..4c4924ccb 100644 --- a/config-x86_64-generic +++ b/config-x86_64-generic @@ -15,7 +15,7 @@ CONFIG_NUMA=y CONFIG_K8_NUMA=y CONFIG_X86_64_ACPI_NUMA=y # CONFIG_NUMA_EMU is not set -CONFIG_NR_CPUS=512 +CONFIG_NR_CPUS=256 CONFIG_X86_POWERNOW_K8=m CONFIG_X86_P4_CLOCKMOD=m CONFIG_IA32_EMULATION=y @@ -403,7 +403,11 @@ CONFIG_CRYPTO_GHASH_CLMUL_NI_INTEL=m CONFIG_VGA_SWITCHEROO=y CONFIG_LPC_SCH=m -CONFIG_INTEL_IDLE=m +CONFIG_INTEL_IDLE=y CONFIG_I7300_IDLE=m CONFIG_PCI_CNB20LE_QUIRK=y + +CONFIG_HP_ILO=m + +CONFIG_IRQ_TIME_ACCOUNTING=y diff --git a/create-sys-fs-cgroup-to-mount-cgroupfs-on.patch b/create-sys-fs-cgroup-to-mount-cgroupfs-on.patch new file mode 100644 index 000000000..e1788a316 --- /dev/null +++ b/create-sys-fs-cgroup-to-mount-cgroupfs-on.patch @@ -0,0 +1,56 @@ +From: Greg KH +Date: Thu, 5 Aug 2010 20:53:35 +0000 (-0700) +Subject: cgroupfs: create /sys/fs/cgroup to mount cgroupfs on +X-Git-Tag: v2.6.36-rc1~521^2~7 +X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=676db4af043014e852f67ba0349dae0071bd11f3 + +cgroupfs: create /sys/fs/cgroup to mount cgroupfs on + +We really shouldn't be asking userspace to create new root filesystems. +So follow along with all of the other in-kernel filesystems, and provide +a mount point in sysfs. + +For cgroupfs, this should be in /sys/fs/cgroup/ This change provides +that mount point when the cgroup filesystem is registered in the kernel. + +Acked-by: Paul Menage +Acked-by: Dhaval Giani +Cc: Li Zefan +Cc: Lennart Poettering +Cc: Kay Sievers +Signed-off-by: Greg Kroah-Hartman +--- + +diff --git a/kernel/cgroup.c b/kernel/cgroup.c +index a8ce099..d83cab0 100644 +--- a/kernel/cgroup.c ++++ b/kernel/cgroup.c +@@ -1623,6 +1623,8 @@ static struct file_system_type cgroup_fs_type = { + .kill_sb = cgroup_kill_sb, + }; + ++static struct kobject *cgroup_kobj; ++ + static inline struct cgroup *__d_cgrp(struct dentry *dentry) + { + return dentry->d_fsdata; +@@ -3894,9 +3896,18 @@ int __init cgroup_init(void) + hhead = css_set_hash(init_css_set.subsys); + hlist_add_head(&init_css_set.hlist, hhead); + BUG_ON(!init_root_id(&rootnode)); ++ ++ cgroup_kobj = kobject_create_and_add("cgroup", fs_kobj); ++ if (!cgroup_kobj) { ++ err = -ENOMEM; ++ goto out; ++ } ++ + err = register_filesystem(&cgroup_fs_type); +- if (err < 0) ++ if (err < 0) { ++ kobject_put(cgroup_kobj); + goto out; ++ } + + proc_create("cgroups", 0, NULL, &proc_cgroupstats_operations); + diff --git a/crypto-Move-md5_transform-to-lib-md5.c.patch b/crypto-Move-md5_transform-to-lib-md5.c.patch new file mode 100644 index 000000000..ef12c3b00 --- /dev/null +++ b/crypto-Move-md5_transform-to-lib-md5.c.patch @@ -0,0 +1,258 @@ +From 44c943562c2e204cddf46db131c53c326b47bd47 Mon Sep 17 00:00:00 2001 +From: "David S. Miller" +Date: Fri, 16 Sep 2011 17:10:29 -0400 +Subject: [PATCH 1/2] crypto: Move md5_transform to lib/md5.c + +We are going to use this for TCP/IP sequence number and fragment ID +generation. + +Signed-off-by: David S. Miller +Signed-off-by: Greg Kroah-Hartman + +Backported to 2.6.25.14 by Josh Boyer +--- + crypto/md5.c | 92 +------------------------------------------ + include/linux/cryptohash.h | 5 ++ + lib/Makefile | 2 +- + lib/md5.c | 95 ++++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 102 insertions(+), 92 deletions(-) + create mode 100644 lib/md5.c + +diff --git a/crypto/md5.c b/crypto/md5.c +index 30efc7d..7febeaa 100644 +--- a/crypto/md5.c ++++ b/crypto/md5.c +@@ -21,99 +21,9 @@ + #include + #include + #include ++#include + #include + +-#define F1(x, y, z) (z ^ (x & (y ^ z))) +-#define F2(x, y, z) F1(z, x, y) +-#define F3(x, y, z) (x ^ y ^ z) +-#define F4(x, y, z) (y ^ (x | ~z)) +- +-#define MD5STEP(f, w, x, y, z, in, s) \ +- (w += f(x, y, z) + in, w = (w<>(32-s)) + x) +- +-static void md5_transform(u32 *hash, u32 const *in) +-{ +- u32 a, b, c, d; +- +- a = hash[0]; +- b = hash[1]; +- c = hash[2]; +- d = hash[3]; +- +- MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); +- MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); +- MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); +- MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); +- MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); +- MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); +- MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); +- MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); +- MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); +- MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); +- MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); +- MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); +- MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); +- MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); +- MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); +- MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); +- +- MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); +- MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); +- MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); +- MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); +- MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); +- MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); +- MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); +- MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); +- MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); +- MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); +- MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); +- MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); +- MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); +- MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); +- MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); +- MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); +- +- MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); +- MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); +- MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); +- MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); +- MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); +- MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); +- MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); +- MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); +- MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); +- MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); +- MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); +- MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); +- MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); +- MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); +- MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); +- MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); +- +- MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); +- MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); +- MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); +- MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); +- MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); +- MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); +- MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); +- MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); +- MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); +- MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); +- MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); +- MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); +- MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); +- MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); +- MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); +- MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); +- +- hash[0] += a; +- hash[1] += b; +- hash[2] += c; +- hash[3] += d; +-} +- + /* XXX: this stuff can be optimized */ + static inline void le32_to_cpu_array(u32 *buf, unsigned int words) + { +diff --git a/include/linux/cryptohash.h b/include/linux/cryptohash.h +index ec78a4b..d2984fb 100644 +--- a/include/linux/cryptohash.h ++++ b/include/linux/cryptohash.h +@@ -8,6 +8,11 @@ + void sha_init(__u32 *buf); + void sha_transform(__u32 *digest, const char *data, __u32 *W); + ++#define MD5_DIGEST_WORDS 4 ++#define MD5_MESSAGE_BYTES 64 ++ ++void md5_transform(__u32 *hash, __u32 const *in); ++ + __u32 half_md4_transform(__u32 buf[4], __u32 const in[8]); + + #endif +diff --git a/lib/Makefile b/lib/Makefile +index 0bfabba..d07eef3 100644 +--- a/lib/Makefile ++++ b/lib/Makefile +@@ -10,7 +10,7 @@ endif + lib-y := ctype.o string.o vsprintf.o cmdline.o \ + rbtree.o radix-tree.o dump_stack.o \ + idr.o int_sqrt.o extable.o prio_tree.o \ +- sha1.o irq_regs.o reciprocal_div.o argv_split.o \ ++ sha1.o md5.o irq_regs.o reciprocal_div.o argv_split.o \ + proportions.o prio_heap.o ratelimit.o show_mem.o \ + is_single_threaded.o plist.o decompress.o flex_array.o + +diff --git a/lib/md5.c b/lib/md5.c +new file mode 100644 +index 0000000..c777180 +--- /dev/null ++++ b/lib/md5.c +@@ -0,0 +1,95 @@ ++#include ++#include ++#include ++ ++#define F1(x, y, z) (z ^ (x & (y ^ z))) ++#define F2(x, y, z) F1(z, x, y) ++#define F3(x, y, z) (x ^ y ^ z) ++#define F4(x, y, z) (y ^ (x | ~z)) ++ ++#define MD5STEP(f, w, x, y, z, in, s) \ ++ (w += f(x, y, z) + in, w = (w<>(32-s)) + x) ++ ++void md5_transform(__u32 *hash, __u32 const *in) ++{ ++ u32 a, b, c, d; ++ ++ a = hash[0]; ++ b = hash[1]; ++ c = hash[2]; ++ d = hash[3]; ++ ++ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); ++ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); ++ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); ++ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); ++ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); ++ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); ++ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); ++ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); ++ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); ++ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); ++ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); ++ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); ++ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); ++ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); ++ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); ++ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); ++ ++ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); ++ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); ++ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); ++ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); ++ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); ++ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); ++ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); ++ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); ++ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); ++ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); ++ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); ++ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); ++ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); ++ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); ++ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); ++ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); ++ ++ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); ++ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); ++ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); ++ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); ++ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); ++ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); ++ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); ++ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); ++ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); ++ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); ++ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); ++ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); ++ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); ++ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); ++ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); ++ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); ++ ++ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); ++ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); ++ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); ++ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); ++ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); ++ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); ++ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); ++ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); ++ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); ++ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); ++ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); ++ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); ++ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); ++ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); ++ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); ++ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); ++ ++ hash[0] += a; ++ hash[1] += b; ++ hash[2] += c; ++ hash[3] += d; ++} ++EXPORT_SYMBOL(md5_transform); +-- +1.7.6 + diff --git a/crypto-ghash-Avoid-null-pointer-dereference-if-no-ke.patch b/crypto-ghash-Avoid-null-pointer-dereference-if-no-ke.patch new file mode 100644 index 000000000..8c56d66b2 --- /dev/null +++ b/crypto-ghash-Avoid-null-pointer-dereference-if-no-ke.patch @@ -0,0 +1,105 @@ +From 3e65dd9e26cf5ba791f44c5e15e29d07b84824aa Mon Sep 17 00:00:00 2001 +From: Nick Bowler +Date: Thu, 20 Oct 2011 14:16:55 +0200 +Subject: [PATCH] crypto: ghash - Avoid null pointer dereference if no key is + set + +commit 7ed47b7d142ec99ad6880bbbec51e9f12b3af74c upstream. + +The ghash_update function passes a pointer to gf128mul_4k_lle which will +be NULL if ghash_setkey is not called or if the most recent call to +ghash_setkey failed to allocate memory. This causes an oops. Fix this +up by returning an error code in the null case. + +This is trivially triggered from unprivileged userspace through the +AF_ALG interface by simply writing to the socket without setting a key. + +The ghash_final function has a similar issue, but triggering it requires +a memory allocation failure in ghash_setkey _after_ at least one +successful call to ghash_update. + + BUG: unable to handle kernel NULL pointer dereference at 00000670 + IP: [] gf128mul_4k_lle+0x23/0x60 [gf128mul] + *pde = 00000000 + Oops: 0000 [#1] PREEMPT SMP + Modules linked in: ghash_generic gf128mul algif_hash af_alg nfs lockd nfs_acl sunrpc bridge ipv6 stp llc + + Pid: 1502, comm: hashatron Tainted: G W 3.1.0-rc9-00085-ge9308cf #32 Bochs Bochs + EIP: 0060:[] EFLAGS: 00000202 CPU: 0 + EIP is at gf128mul_4k_lle+0x23/0x60 [gf128mul] + EAX: d69db1f0 EBX: d6b8ddac ECX: 00000004 EDX: 00000000 + ESI: 00000670 EDI: d6b8ddac EBP: d6b8ddc8 ESP: d6b8dda4 + DS: 007b ES: 007b FS: 00d8 GS: 0033 SS: 0068 + Process hashatron (pid: 1502, ti=d6b8c000 task=d6810000 task.ti=d6b8c000) + Stack: + 00000000 d69db1f0 00000163 00000000 d6b8ddc8 c101a520 d69db1f0 d52aa000 + 00000ff0 d6b8dde8 d88d310f d6b8a3f8 d52aa000 00001000 d88d502c d6b8ddfc + 00001000 d6b8ddf4 c11676ed d69db1e8 d6b8de24 c11679ad d52aa000 00000000 + Call Trace: + [] ? kmap_atomic_prot+0x37/0xa6 + [] ghash_update+0x85/0xbe [ghash_generic] + [] crypto_shash_update+0x18/0x1b + [] shash_ahash_update+0x22/0x36 + [] shash_async_update+0xb/0xd + [] hash_sendpage+0xba/0xf2 [algif_hash] + [] kernel_sendpage+0x39/0x4e + [] ? 0xd88cdfff + [] sock_sendpage+0x37/0x3e + [] ? kernel_sendpage+0x4e/0x4e + [] pipe_to_sendpage+0x56/0x61 + [] splice_from_pipe_feed+0x58/0xcd + [] ? splice_from_pipe_begin+0x10/0x10 + [] __splice_from_pipe+0x36/0x55 + [] ? splice_from_pipe_begin+0x10/0x10 + [] splice_from_pipe+0x51/0x64 + [] ? default_file_splice_write+0x2c/0x2c + [] generic_splice_sendpage+0x13/0x15 + [] ? splice_from_pipe_begin+0x10/0x10 + [] do_splice_from+0x5d/0x67 + [] sys_splice+0x2bf/0x363 + [] ? sysenter_exit+0xf/0x16 + [] ? trace_hardirqs_on_caller+0x10e/0x13f + [] sysenter_do_call+0x12/0x32 + Code: 83 c4 0c 5b 5e 5f c9 c3 55 b9 04 00 00 00 89 e5 57 8d 7d e4 56 53 8d 5d e4 83 ec 18 89 45 e0 89 55 dc 0f b6 70 0f c1 e6 04 01 d6 a5 be 0f 00 00 00 4e 89 d8 e8 48 ff ff ff 8b 45 e0 89 da 0f + EIP: [] gf128mul_4k_lle+0x23/0x60 [gf128mul] SS:ESP 0068:d6b8dda4 + CR2: 0000000000000670 + ---[ end trace 4eaa2a86a8e2da24 ]--- + note: hashatron[1502] exited with preempt_count 1 + BUG: scheduling while atomic: hashatron/1502/0x10000002 + INFO: lockdep is turned off. + [...] + +Signed-off-by: Nick Bowler +Signed-off-by: Herbert Xu +Signed-off-by: Greg Kroah-Hartman +--- + crypto/ghash-generic.c | 6 ++++++ + 1 files changed, 6 insertions(+), 0 deletions(-) + +diff --git a/crypto/ghash-generic.c b/crypto/ghash-generic.c +index be44256..7835b8f 100644 +--- a/crypto/ghash-generic.c ++++ b/crypto/ghash-generic.c +@@ -67,6 +67,9 @@ static int ghash_update(struct shash_desc *desc, + struct ghash_ctx *ctx = crypto_shash_ctx(desc->tfm); + u8 *dst = dctx->buffer; + ++ if (!ctx->gf128) ++ return -ENOKEY; ++ + if (dctx->bytes) { + int n = min(srclen, dctx->bytes); + u8 *pos = dst + (GHASH_BLOCK_SIZE - dctx->bytes); +@@ -119,6 +122,9 @@ static int ghash_final(struct shash_desc *desc, u8 *dst) + struct ghash_ctx *ctx = crypto_shash_ctx(desc->tfm); + u8 *buf = dctx->buffer; + ++ if (!ctx->gf128) ++ return -ENOKEY; ++ + ghash_flush(ctx, dctx); + memcpy(dst, buf, GHASH_BLOCK_SIZE); + +-- +1.7.6.4 + diff --git a/depessimize-rds_copy_page_user.patch b/depessimize-rds_copy_page_user.patch new file mode 100644 index 000000000..aec8bff4d --- /dev/null +++ b/depessimize-rds_copy_page_user.patch @@ -0,0 +1,78 @@ +From 799c10559d60f159ab2232203f222f18fa3c4a5f Mon Sep 17 00:00:00 2001 +From: Linus Torvalds +Date: Fri, 15 Oct 2010 11:09:28 -0700 +Subject: [PATCH] De-pessimize rds_page_copy_user + +Don't try to "optimize" rds_page_copy_user() by using kmap_atomic() and +the unsafe atomic user mode accessor functions. It's actually slower +than the straightforward code on any reasonable modern CPU. + +Back when the code was written (although probably not by the time it was +actually merged, though), 32-bit x86 may have been the dominant +architecture. And there kmap_atomic() can be a lot faster than kmap() +(unless you have very good locality, in which case the virtual address +caching by kmap() can overcome all the downsides). + +But these days, x86-64 may not be more populous, but it's getting there +(and if you care about performance, it's definitely already there - +you'd have upgraded your CPU's already in the last few years). And on +x86-64, the non-kmap_atomic() version is faster, simply because the code +is simpler and doesn't have the "re-try page fault" case. + +People with old hardware are not likely to care about RDS anyway, and +the optimization for the 32-bit case is simply buggy, since it doesn't +verify the user addresses properly. + +Reported-by: Dan Rosenberg +Acked-by: Andrew Morton +Cc: stable@kernel.org +Signed-off-by: Linus Torvalds +--- + net/rds/page.c | 27 +++++++-------------------- + 1 files changed, 7 insertions(+), 20 deletions(-) + +diff --git a/net/rds/page.c b/net/rds/page.c +index 595a952..1dfbfea 100644 +--- a/net/rds/page.c ++++ b/net/rds/page.c +@@ -57,30 +57,17 @@ int rds_page_copy_user(struct page *page, unsigned long offset, + unsigned long ret; + void *addr; + +- if (to_user) ++ addr = kmap(page); ++ if (to_user) { + rds_stats_add(s_copy_to_user, bytes); +- else ++ ret = copy_to_user(ptr, addr + offset, bytes); ++ } else { + rds_stats_add(s_copy_from_user, bytes); +- +- addr = kmap_atomic(page, KM_USER0); +- if (to_user) +- ret = __copy_to_user_inatomic(ptr, addr + offset, bytes); +- else +- ret = __copy_from_user_inatomic(addr + offset, ptr, bytes); +- kunmap_atomic(addr, KM_USER0); +- +- if (ret) { +- addr = kmap(page); +- if (to_user) +- ret = copy_to_user(ptr, addr + offset, bytes); +- else +- ret = copy_from_user(addr + offset, ptr, bytes); +- kunmap(page); +- if (ret) +- return -EFAULT; ++ ret = copy_from_user(addr + offset, ptr, bytes); + } ++ kunmap(page); + +- return 0; ++ return ret ? -EFAULT : 0; + } + EXPORT_SYMBOL_GPL(rds_page_copy_user); + +-- +1.7.3.2 + diff --git a/disable-xhci-by-default.patch b/disable-xhci-by-default.patch new file mode 100644 index 000000000..dd1c80866 --- /dev/null +++ b/disable-xhci-by-default.patch @@ -0,0 +1,31 @@ +diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c +index 11482b6..b05ff9c 100644 +--- a/drivers/usb/host/xhci-pci.c ++++ b/drivers/usb/host/xhci-pci.c +@@ -179,9 +179,25 @@ static struct pci_driver xhci_pci_driver = { + .shutdown = usb_hcd_pci_shutdown, + }; + ++ ++static int enable; ++module_param(enable, int, S_IRUGO); ++MODULE_PARM_DESC(enable, "Enable XHCI host controller"); ++ + int xhci_register_pci(void) + { +- return pci_register_driver(&xhci_pci_driver); ++ /* xhci will prevent suspend/resume if it's loaded. ++ * force user to pass xhci.enable=1 to the kernel in order ++ * to get usb3.0 support for the time being. ++ * ++ * ugly yes, but there's few enough users out there using ++ * usb3.0, and a lot who just have the hardware breaking ++ * their suspend. ++ */ ++ if (enable) ++ return pci_register_driver(&xhci_pci_driver); ++ else ++ return 0; + } + + void xhci_unregister_pci(void) diff --git a/dm-allow-setting-of-uuid-via-rename-if-not-already-set.patch b/dm-allow-setting-of-uuid-via-rename-if-not-already-set.patch new file mode 100644 index 000000000..fd0f1af41 --- /dev/null +++ b/dm-allow-setting-of-uuid-via-rename-if-not-already-set.patch @@ -0,0 +1,205 @@ +From ce5fa9851090cc5f3de4139bf0f343eb78d1c568 Mon Sep 17 00:00:00 2001 +From: Peter Jones +Date: Mon, 11 Oct 2010 15:49:28 -0400 +Subject: [PATCH] device-mapper: Allow setting of UUID via rename if not already set + +This makes it possible to use DM_DEV_RENAME to add a uuid to a device so +long as one has not been previously set either with DM_DEV_CREATE or +with DM_DEV_RENAME. This is needed because sometimes in it's necessary +to create the device before the uuid is known, and in such cases the +uuid must be filled in after the creation. + +Also bump the minor number to 19. + +Signed-off-by: Peter Jones +--- + drivers/md/dm-ioctl.c | 95 ++++++++++++++++++++++++++++++++-------------- + include/linux/dm-ioctl.h | 11 ++++- + 2 files changed, 74 insertions(+), 32 deletions(-) + +diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c +index bb6bdc8..d102269 100644 +--- a/drivers/md/dm-ioctl.c ++++ b/drivers/md/dm-ioctl.c +@@ -298,15 +298,15 @@ retry: + static int dm_hash_rename(uint32_t cookie, uint32_t *flags, const char *old, + const char *new) + { +- char *new_name, *old_name; ++ char *new_data, *old_data; + struct hash_cell *hc; + struct dm_table *table; + + /* + * duplicate new. + */ +- new_name = kstrdup(new, GFP_KERNEL); +- if (!new_name) ++ new_data = kstrdup(new, GFP_KERNEL); ++ if (!new_data) + return -ENOMEM; + + down_write(&_hash_lock); +@@ -314,13 +314,18 @@ static int dm_hash_rename(uint32_t cookie, uint32_t *flags, const char *old, + /* + * Is new free ? + */ +- hc = __get_name_cell(new); ++ if (*flags & DM_NEW_UUID_FLAG) ++ hc = __get_uuid_cell(new); ++ else ++ hc = __get_name_cell(new); + if (hc) { +- DMWARN("asked to rename to an already existing name %s -> %s", ++ DMWARN("Unable to change %s on device, %s to one that " ++ "already exists: %s", ++ (*flags & DM_NEW_UUID_FLAG) ? "uuid" : "name", + old, new); + dm_put(hc->md); + up_write(&_hash_lock); +- kfree(new_name); ++ kfree(new_data); + return -EBUSY; + } + +@@ -329,22 +334,46 @@ static int dm_hash_rename(uint32_t cookie, uint32_t *flags, const char *old, + */ + hc = __get_name_cell(old); + if (!hc) { +- DMWARN("asked to rename a non existent device %s -> %s", ++ DMWARN("Unable to rename non-existent device, %s to %s", + old, new); + up_write(&_hash_lock); +- kfree(new_name); ++ kfree(new_data); + return -ENXIO; + } + +- /* +- * rename and move the name cell. +- */ +- list_del(&hc->name_list); +- old_name = hc->name; +- mutex_lock(&dm_hash_cells_mutex); +- hc->name = new_name; +- mutex_unlock(&dm_hash_cells_mutex); +- list_add(&hc->name_list, _name_buckets + hash_str(new_name)); ++ if (*flags & DM_NEW_UUID_FLAG) { ++ /* ++ * Does this device already have a uuid? ++ */ ++ if (hc->uuid) { ++ DMWARN("Unable to change uuid of device, %s because " ++ "uuid is already set to %s", ++ old, hc->uuid); ++ dm_put(hc->md); ++ up_write(&_hash_lock); ++ kfree(new_data); ++ return -EINVAL; ++ } ++ /* ++ * change uuid and move the uuid cell. ++ */ ++ list_del(&hc->uuid_list); ++ old_data = hc->uuid; ++ mutex_lock(&dm_hash_cells_mutex); ++ hc->uuid = new_data; ++ mutex_unlock(&dm_hash_cells_mutex); ++ list_add(&hc->uuid_list, _uuid_buckets + hash_str(new_data)); ++ } else { ++ /* ++ * rename and move the name cell. ++ */ ++ list_del(&hc->name_list); ++ old_data = hc->name; ++ mutex_lock(&dm_hash_cells_mutex); ++ hc->name = new_data; ++ mutex_unlock(&dm_hash_cells_mutex); ++ list_add(&hc->name_list, _name_buckets + hash_str(new_data)); ++ } + + /* + * Wake up any dm event waiters. +@@ -360,7 +388,7 @@ static int dm_hash_rename(uint32_t cookie, uint32_t *flags, const char *old, + + dm_put(hc->md); + up_write(&_hash_lock); +- kfree(old_name); ++ kfree(old_data); + return 0; + } + +@@ -773,23 +801,32 @@ static int invalid_str(char *str, void *end) + static int dev_rename(struct dm_ioctl *param, size_t param_size) + { + int r; +- char *new_name = (char *) param + param->data_start; ++ char *new_data = (char *) param + param->data_start; + +- if (new_name < param->data || +- invalid_str(new_name, (void *) param + param_size) || +- strlen(new_name) > DM_NAME_LEN - 1) { +- DMWARN("Invalid new logical volume name supplied."); +- return -EINVAL; +- } ++ if (param->flags & DM_NEW_UUID_FLAG) { ++ if (new_data < param->data || ++ invalid_str(new_data, (void *) param + param_size) || ++ strlen(new_data) > DM_UUID_LEN - 1) { ++ DMWARN("Invalid new device uuid supplied."); ++ return -EINVAL; ++ } ++ } else { ++ if (new_data < param->data || ++ invalid_str(new_data, (void *) param + param_size) || ++ strlen(new_data) > DM_NAME_LEN - 1) { ++ DMWARN("Invalid new device name supplied."); ++ return -EINVAL; ++ } + +- r = check_name(new_name); +- if (r) +- return r; ++ r = check_name(new_data); ++ if (r) ++ return r; ++ } + + param->data_size = 0; + + return dm_hash_rename(param->event_nr, ¶m->flags, param->name, +- new_name); ++ new_data); + } + + static int dev_set_geometry(struct dm_ioctl *param, size_t param_size) +diff --git a/include/linux/dm-ioctl.h b/include/linux/dm-ioctl.h +index 2c445e1..3bbcb3a 100644 +--- a/include/linux/dm-ioctl.h ++++ b/include/linux/dm-ioctl.h +@@ -266,9 +266,9 @@ enum { + #define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl) + + #define DM_VERSION_MAJOR 4 +-#define DM_VERSION_MINOR 17 +-#define DM_VERSION_PATCHLEVEL 0 +-#define DM_VERSION_EXTRA "-ioctl (2010-03-05)" ++#define DM_VERSION_MINOR 19 ++#define DM_VERSION_PATCHLEVEL 1 ++#define DM_VERSION_EXTRA "-ioctl (2010-10-12)" + + /* Status bits */ + #define DM_READONLY_FLAG (1 << 0) /* In/Out */ +@@ -321,4 +321,9 @@ enum { + */ + #define DM_UEVENT_GENERATED_FLAG (1 << 13) /* Out */ + ++/* ++ * If set, rename operates on uuid, not name. ++ */ ++#define DM_NEW_UUID_FLAG (1 << 14) /* In */ ++ + #endif /* _LINUX_DM_IOCTL_H */ +-- +1.7.2.3 + diff --git a/dmar-disable-when-ricoh-multifunction.patch b/dmar-disable-when-ricoh-multifunction.patch new file mode 100644 index 000000000..e0bbf3020 --- /dev/null +++ b/dmar-disable-when-ricoh-multifunction.patch @@ -0,0 +1,35 @@ +From 8d2f6746f7f82e1aee2dc40a937b5954cfc73414 Mon Sep 17 00:00:00 2001 +From: Kyle McMartin +Date: Sun, 17 Oct 2010 15:55:32 -0400 +Subject: [PATCH] dmar: disable if ricoh multifunction detected + +--- + drivers/pci/intel-iommu.c | 10 ++++++++++ + 1 files changed, 10 insertions(+), 0 deletions(-) + +diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c +index 8e499e8..076c5de 100644 +--- a/drivers/pci/intel-iommu.c ++++ b/drivers/pci/intel-iommu.c +@@ -3755,6 +3755,18 @@ static void __devinit quirk_iommu_rwbf(struct pci_dev *dev) + + DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2a40, quirk_iommu_rwbf); + ++/* https://bugzilla.redhat.com/show_bug.cgi?id=605888 */ ++static void __devinit quirk_ricoh_multifunction(struct pci_dev *dev) ++{ ++ printk(KERN_INFO "intel_iommu: broken Ricoh device %04X detected, disabling...\n", ++ dev->device); ++ dmar_disabled = 1; ++} ++DECLARE_PCI_FIXUP_HEADER(0x1180, 0xe822, quirk_ricoh_multifunction); ++DECLARE_PCI_FIXUP_HEADER(0x1180, 0xe230, quirk_ricoh_multifunction); ++DECLARE_PCI_FIXUP_HEADER(0x1180, 0xe832, quirk_ricoh_multifunction); ++DECLARE_PCI_FIXUP_HEADER(0x1180, 0xe476, quirk_ricoh_multifunction); ++ + /* On Tylersburg chipsets, some BIOSes have been known to enable the + ISOCH DMAR unit for the Azalia sound device, but not give it any + TLB entries, which causes it to deadlock. Check for that. We do +-- +1.7.3.1 + diff --git a/drm-edid-invalid.patch b/drm-edid-invalid.patch new file mode 100644 index 000000000..8dcaa35ba --- /dev/null +++ b/drm-edid-invalid.patch @@ -0,0 +1,54 @@ +diff -up linux-2.6.35.x86_64/drivers/gpu/drm/drm_edid.c.da linux-2.6.35.x86_64/drivers/gpu/drm/drm_edid.c +--- linux-2.6.35.x86_64/drivers/gpu/drm/drm_edid.c.da 2010-08-01 18:11:14.000000000 -0400 ++++ linux-2.6.35.x86_64/drivers/gpu/drm/drm_edid.c 2010-11-11 20:46:10.000000000 -0500 +@@ -229,7 +229,7 @@ drm_do_probe_ddc_edid(struct i2c_adapter + .addr = DDC_ADDR, + .flags = I2C_M_RD, + .len = len, +- .buf = buf + start, ++ .buf = buf, + } + }; + +@@ -242,7 +242,7 @@ drm_do_probe_ddc_edid(struct i2c_adapter + static u8 * + drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) + { +- int i, j = 0; ++ int i, j = 0, valid_extensions = 0; + u8 *block, *new; + + if ((block = kmalloc(EDID_LENGTH, GFP_KERNEL)) == NULL) +@@ -269,14 +269,28 @@ drm_do_get_edid(struct drm_connector *co + + for (j = 1; j <= block[0x7e]; j++) { + for (i = 0; i < 4; i++) { +- if (drm_do_probe_ddc_edid(adapter, block, j, +- EDID_LENGTH)) ++ if (drm_do_probe_ddc_edid(adapter, ++ block + (valid_extensions + 1) * EDID_LENGTH, ++ j, EDID_LENGTH)) + goto out; +- if (drm_edid_block_valid(block + j * EDID_LENGTH)) ++ if (drm_edid_block_valid(block + (valid_extensions + 1) * EDID_LENGTH)) { ++ valid_extensions++; + break; ++ } + } + if (i == 4) +- goto carp; ++ printk(KERN_WARNING ++ "%s: Ignoring invalid EDID block %d.\n", ++ drm_get_connector_name(connector), j); ++ } ++ ++ if (valid_extensions != block[0x7e]) { ++ block[EDID_LENGTH-1] += block[0x7e] - valid_extensions; ++ block[0x7e] = valid_extensions; ++ new = krealloc(block, (valid_extensions + 1) * EDID_LENGTH, GFP_KERNEL); ++ if (!new) ++ goto out; ++ block = new; + } + + return block; diff --git a/drm-i2c-ch7006-fix.patch b/drm-i2c-ch7006-fix.patch new file mode 100644 index 000000000..11eef90e7 --- /dev/null +++ b/drm-i2c-ch7006-fix.patch @@ -0,0 +1,57 @@ +From 9fa9e790eb301bade8fe4ea0fd9ecb72617f0928 Mon Sep 17 00:00:00 2001 +From: Francisco Jerez +Date: Thu, 5 Aug 2010 22:57:08 +0200 +Subject: [PATCH 3/5] drm-i2c-ch7006-fix + +drm/i2c/ch7006: Don't use POWER_LEVEL_FULL_POWER_OFF on early chip versions. + +Signed-off-by: Francisco Jerez +--- + drivers/gpu/drm/i2c/ch7006_drv.c | 1 + + drivers/gpu/drm/i2c/ch7006_mode.c | 5 ++++- + drivers/gpu/drm/i2c/ch7006_priv.h | 1 + + 3 files changed, 6 insertions(+), 1 deletions(-) + +diff --git a/drivers/gpu/drm/i2c/ch7006_drv.c b/drivers/gpu/drm/i2c/ch7006_drv.c +index 81681a0..8c760c7 100644 +--- a/drivers/gpu/drm/i2c/ch7006_drv.c ++++ b/drivers/gpu/drm/i2c/ch7006_drv.c +@@ -454,6 +454,7 @@ static int ch7006_encoder_init(struct i2c_client *client, + priv->hmargin = 50; + priv->vmargin = 50; + priv->last_dpms = -1; ++ priv->chip_version = ch7006_read(client, CH7006_VERSION_ID); + + if (ch7006_tv_norm) { + for (i = 0; i < NUM_TV_NORMS; i++) { +diff --git a/drivers/gpu/drm/i2c/ch7006_mode.c b/drivers/gpu/drm/i2c/ch7006_mode.c +index e447dfb..c860f24 100644 +--- a/drivers/gpu/drm/i2c/ch7006_mode.c ++++ b/drivers/gpu/drm/i2c/ch7006_mode.c +@@ -316,7 +316,10 @@ void ch7006_setup_power_state(struct drm_encoder *encoder) + } + + } else { +- *power |= bitfs(CH7006_POWER_LEVEL, FULL_POWER_OFF); ++ if (priv->chip_version >= 0x20) ++ *power |= bitfs(CH7006_POWER_LEVEL, FULL_POWER_OFF); ++ else ++ *power |= bitfs(CH7006_POWER_LEVEL, POWER_OFF); + } + } + +diff --git a/drivers/gpu/drm/i2c/ch7006_priv.h b/drivers/gpu/drm/i2c/ch7006_priv.h +index b06d3d9..9487123 100644 +--- a/drivers/gpu/drm/i2c/ch7006_priv.h ++++ b/drivers/gpu/drm/i2c/ch7006_priv.h +@@ -95,6 +95,7 @@ struct ch7006_priv { + int flicker; + int scale; + ++ int chip_version; + int last_dpms; + }; + +-- +1.7.2 + diff --git a/drm-i915-blacklist-lighting-edp-pipes.patch b/drm-i915-blacklist-lighting-edp-pipes.patch new file mode 100644 index 000000000..153eb7768 --- /dev/null +++ b/drm-i915-blacklist-lighting-edp-pipes.patch @@ -0,0 +1,50 @@ +https://bugzilla.redhat.com/show_bug.cgi?id=639146 + +diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c +index ce8ff0e..3bbd2c4 100644 +--- a/drivers/gpu/drm/i915/i915_dma.c ++++ b/drivers/gpu/drm/i915/i915_dma.c +@@ -1353,6 +1353,27 @@ static bool i915_switcheroo_can_switch(struct pci_dev *pdev) + return can_switch; + } + ++/* check the VBT to see whether the eDP is on DP-D port */ ++static bool intel_dpd_is_edp(struct drm_device *dev) ++{ ++ struct drm_i915_private *dev_priv = dev->dev_private; ++ struct child_device_config *p_child; ++ int i; ++ ++ if (!dev_priv->child_dev_num) ++ return false; ++ ++ for (i = 0; i < dev_priv->child_dev_num; i++) { ++ p_child = dev_priv->child_dev + i; ++ ++ if (p_child->dvo_port == PORT_IDPD && ++ p_child->device_type == DEVICE_TYPE_eDP) ++ return true; ++ } ++ ++ return false; ++} ++ + static int i915_load_modeset_init(struct drm_device *dev, + unsigned long prealloc_start, + unsigned long prealloc_size, +@@ -1409,6 +1430,15 @@ static int i915_load_modeset_init(struct drm_device *dev, + if (ret) + DRM_INFO("failed to find VBIOS tables\n"); + ++ /* XXX: eDP doesn't even work in git HEAD, ++ * bail out and pray that text mode still works... ++ */ ++ if (intel_dpd_is_edp(dev)) { ++ DRM_ERROR("eDP support is currently non-functional, please boot with \"nomodeset xdriver=vesa\"\n"); ++ ret = -EINVAL; ++ goto cleanup_ringbuffer; ++ } ++ + /* if we have > 1 VGA cards, then disable the radeon VGA resources */ + ret = vga_client_register(dev->pdev, dev, NULL, i915_vga_set_decode); + if (ret) diff --git a/drm-i915-disable-sr-polling.patch b/drm-i915-disable-sr-polling.patch new file mode 100644 index 000000000..71d1fabd7 --- /dev/null +++ b/drm-i915-disable-sr-polling.patch @@ -0,0 +1,70 @@ +From ab7959dd389be36c0bc63e3e883b7891d2c1bfc4 Mon Sep 17 00:00:00 2001 +From: Chris Wilson +Date: Wed, 8 Sep 2010 09:45:11 +0100 +Subject: [PATCH] drm/i915: Disable output polling across suspend & resume + +Suspending (especially hibernating) may take a finite amount of time, +during which a hotplug event may trigger and we will attempt to handle +it with inconsistent state. Disable hotplug polling around suspend and +resume. + +Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=30070 +Reported-by: Rui Tiago Matos +Signed-off-by: Chris Wilson +--- + drivers/gpu/drm/i915/i915_dma.c | 2 -- + drivers/gpu/drm/i915/i915_drv.c | 11 ++++++++++- + 2 files changed, 10 insertions(+), 3 deletions(-) + +diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c +index ce8ff0e..c569617 100644 +--- a/drivers/gpu/drm/i915/i915_dma.c ++++ b/drivers/gpu/drm/i915/i915_dma.c +@@ -1334,10 +1334,8 @@ static void i915_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_ + /* i915 resume handler doesn't set to D0 */ + pci_set_power_state(dev->pdev, PCI_D0); + i915_resume(dev); +- drm_kms_helper_poll_enable(dev); + } else { + printk(KERN_ERR "i915: switched off\n"); +- drm_kms_helper_poll_disable(dev); + i915_suspend(dev, pmm); + } + } +diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c +index 194e0c4..3ad3ebe 100644 +--- a/drivers/gpu/drm/i915/i915_drv.c ++++ b/drivers/gpu/drm/i915/i915_drv.c +@@ -263,6 +263,8 @@ int i915_suspend(struct drm_device *dev, pm_message_t state) + if (state.event == PM_EVENT_PRETHAW) + return 0; + ++ drm_kms_helper_poll_disable(dev); ++ + error = i915_drm_freeze(dev); + if (error) + return error; +@@ -306,12 +308,19 @@ static int i915_drm_thaw(struct drm_device *dev) + + int i915_resume(struct drm_device *dev) + { ++ int ret; ++ + if (pci_enable_device(dev->pdev)) + return -EIO; + + pci_set_master(dev->pdev); + +- return i915_drm_thaw(dev); ++ ret = i915_drm_thaw(dev); ++ if (ret) ++ return ret; ++ ++ drm_kms_helper_poll_enable(dev); ++ return 0; + } + + /** +-- +1.7.2.3 + diff --git a/drm-i915-sanity-check-pread-pwrite.patch b/drm-i915-sanity-check-pread-pwrite.patch new file mode 100644 index 000000000..b5eb2dda8 --- /dev/null +++ b/drm-i915-sanity-check-pread-pwrite.patch @@ -0,0 +1,89 @@ +From ce9d419dbecc292cc3e06e8b1d6d123d3fa813a4 Mon Sep 17 00:00:00 2001 +From: Chris Wilson +Date: Sun, 26 Sep 2010 20:50:05 +0100 +Subject: drm/i915: Sanity check pread/pwrite + +From: Chris Wilson + +commit ce9d419dbecc292cc3e06e8b1d6d123d3fa813a4 upstream. + +Move the access control up from the fast paths, which are no longer +universally taken first, up into the caller. This then duplicates some +sanity checking along the slow paths, but is much simpler. +Tracked as CVE-2010-2962. + +Reported-by: Kees Cook +Signed-off-by: Chris Wilson +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/gpu/drm/i915/i915_gem.c | 28 ++++++++++++++++++++-------- + 1 file changed, 20 insertions(+), 8 deletions(-) + +--- a/drivers/gpu/drm/i915/i915_gem.c ++++ b/drivers/gpu/drm/i915/i915_gem.c +@@ -465,8 +465,15 @@ i915_gem_pread_ioctl(struct drm_device * + */ + if (args->offset > obj->size || args->size > obj->size || + args->offset + args->size > obj->size) { +- drm_gem_object_unreference_unlocked(obj); +- return -EINVAL; ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ if (!access_ok(VERIFY_WRITE, ++ (char __user *)(uintptr_t)args->data_ptr, ++ args->size)) { ++ ret = -EFAULT; ++ goto err; + } + + if (i915_gem_object_needs_bit17_swizzle(obj)) { +@@ -478,8 +485,8 @@ i915_gem_pread_ioctl(struct drm_device * + file_priv); + } + ++err: + drm_gem_object_unreference_unlocked(obj); +- + return ret; + } + +@@ -568,8 +575,6 @@ i915_gem_gtt_pwrite_fast(struct drm_devi + + user_data = (char __user *) (uintptr_t) args->data_ptr; + remain = args->size; +- if (!access_ok(VERIFY_READ, user_data, remain)) +- return -EFAULT; + + + mutex_lock(&dev->struct_mutex); +@@ -928,8 +933,15 @@ i915_gem_pwrite_ioctl(struct drm_device + */ + if (args->offset > obj->size || args->size > obj->size || + args->offset + args->size > obj->size) { +- drm_gem_object_unreference_unlocked(obj); +- return -EINVAL; ++ ret = -EINVAL; ++ goto err; ++ } ++ ++ if (!access_ok(VERIFY_READ, ++ (char __user *)(uintptr_t)args->data_ptr, ++ args->size)) { ++ ret = -EFAULT; ++ goto err; + } + + /* We can only do the GTT pwrite on untiled buffers, as otherwise +@@ -963,8 +975,8 @@ i915_gem_pwrite_ioctl(struct drm_device + DRM_INFO("pwrite failed %d\n", ret); + #endif + ++err: + drm_gem_object_unreference_unlocked(obj); +- + return ret; + } + diff --git a/drm-lower-severity-radeon-lockup.diff b/drm-lower-severity-radeon-lockup.diff new file mode 100644 index 000000000..a550d48f7 --- /dev/null +++ b/drm-lower-severity-radeon-lockup.diff @@ -0,0 +1,11 @@ +--- linux-2.6.35.noarch/drivers/gpu/drm/radeon/radeon_fence.c~ 2011-10-21 14:08:15.229351796 -0400 ++++ linux-2.6.35.noarch/drivers/gpu/drm/radeon/radeon_fence.c 2011-10-21 14:08:35.140287089 -0400 +@@ -232,7 +232,7 @@ retry: + */ + if (seq == rdev->fence_drv.last_seq && radeon_gpu_is_lockup(rdev)) { + /* good news we believe it's a lockup */ +- WARN(1, "GPU lockup (waiting for 0x%08X last fence id 0x%08X)\n", fence->seq, seq); ++ printk(KERN_WARNING, "GPU lockup (waiting for 0x%08X last fence id 0x%08X)\n", fence->seq, seq); + /* FIXME: what should we do ? marking everyone + * as signaled for now + */ diff --git a/drm-nouveau-connector-fix.patch b/drm-nouveau-connector-fix.patch new file mode 100644 index 000000000..0f67d44dc --- /dev/null +++ b/drm-nouveau-connector-fix.patch @@ -0,0 +1,21 @@ +diff -up linux-2.6.35.x86_64/drivers/gpu/drm/nouveau/nouveau_connector.c.da linux-2.6.35.x86_64/drivers/gpu/drm/nouveau/nouveau_connector.c +--- linux-2.6.35.x86_64/drivers/gpu/drm/nouveau/nouveau_connector.c.da 2010-11-08 19:55:42.000000000 -0500 ++++ linux-2.6.35.x86_64/drivers/gpu/drm/nouveau/nouveau_connector.c 2010-11-08 19:55:49.000000000 -0500 +@@ -298,7 +298,7 @@ detect_analog: + } + + static enum drm_connector_status +-nouveau_connector_detect_lvds(struct drm_connector *connector) ++nouveau_connector_detect_lvds(struct drm_connector *connector, bool force) + { + struct drm_device *dev = connector->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; +@@ -319,7 +319,7 @@ nouveau_connector_detect_lvds(struct drm + + /* Try retrieving EDID via DDC */ + if (!dev_priv->vbios.fp_no_ddc) { +- status = nouveau_connector_detect(connector); ++ status = nouveau_connector_detect(connector, force); + if (status == connector_status_connected) + goto out; + } diff --git a/drm-nouveau-evo-hang.patch b/drm-nouveau-evo-hang.patch new file mode 100644 index 000000000..aedc9c5b0 --- /dev/null +++ b/drm-nouveau-evo-hang.patch @@ -0,0 +1,32 @@ +From d0301ece9e093c484f880893dc86d97848360892 Mon Sep 17 00:00:00 2001 +From: Ben Skeggs +Date: Fri, 19 Nov 2010 18:50:57 +1000 +Subject: [PATCH 2/2] drm-nouveau-evo-hang + +On some GF8+ boards, the display engine will stop processing its push +buffer if a wrap-around occurs at a certain point. The exact cause +is not known. + +This patch by David Dillow (rhbz#537065) is a safe enough work-around +until it can be solved properly. + +Signed-off-by: Ben Skeggs +--- + drivers/gpu/drm/nouveau/nv50_display.c | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) + +diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c +index 11d366a..4e5402c 100644 +--- a/drivers/gpu/drm/nouveau/nv50_display.c ++++ b/drivers/gpu/drm/nouveau/nv50_display.c +@@ -364,6 +364,7 @@ nv50_display_init(struct drm_device *dev) + nv_wr32(dev, 0x610300, nv_rd32(dev, 0x610300) & ~1); + + evo->dma.max = (4096/4) - 2; ++ evo->dma.max &= ~7; + evo->dma.put = 0; + evo->dma.cur = evo->dma.put; + evo->dma.free = evo->dma.max - evo->dma.cur; +-- +1.7.3.2 + diff --git a/drm-nouveau-imac-g4.patch b/drm-nouveau-imac-g4.patch new file mode 100644 index 000000000..316605e74 --- /dev/null +++ b/drm-nouveau-imac-g4.patch @@ -0,0 +1,167 @@ +From b4166db6d1f951a460e5e7f52bb4b4d96f27f55a Mon Sep 17 00:00:00 2001 +From: Francisco Jerez +Date: Fri, 19 Nov 2010 18:08:47 +1000 +Subject: [PATCH 1/2] drm-nouveau-imac-g4 + +drm/nouveau: fabricate DCB encoder table for iMac G4 + +In typical Apple fashion there's no standard information about what +encoders are present on this machine, this patch adds a quirk to +provide it. + +Signed-off-by: Francisco Jerez +Signed-off-by: Ben Skeggs +--- + drivers/gpu/drm/nouveau/nouveau_bios.c | 102 ++++++++++++-------------------- + 1 files changed, 38 insertions(+), 64 deletions(-) + +diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c +index 72905c9..8c287f8 100644 +--- a/drivers/gpu/drm/nouveau/nouveau_bios.c ++++ b/drivers/gpu/drm/nouveau/nouveau_bios.c +@@ -5985,52 +5985,17 @@ static struct dcb_entry *new_dcb_entry(struct dcb_table *dcb) + return entry; + } + +-static void fabricate_vga_output(struct dcb_table *dcb, int i2c, int heads) ++static void fabricate_dcb_output(struct dcb_table *dcb, int type, int i2c, ++ int heads, int or) + { + struct dcb_entry *entry = new_dcb_entry(dcb); + +- entry->type = 0; ++ entry->type = type; + entry->i2c_index = i2c; + entry->heads = heads; +- entry->location = DCB_LOC_ON_CHIP; +- entry->or = 1; +-} +- +-static void fabricate_dvi_i_output(struct dcb_table *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 dcb_table *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 */ ++ if (type != OUTPUT_ANALOG) ++ entry->location = !DCB_LOC_ON_CHIP; /* ie OFF CHIP */ ++ entry->or = or; + } + + static bool +@@ -6297,8 +6262,36 @@ apply_dcb_encoder_quirks(struct drm_device *dev, int idx, u32 *conn, u32 *conf) + return true; + } + ++static void ++fabricate_dcb_encoder_table(struct drm_device *dev, struct nvbios *bios) ++{ ++ struct dcb_table *dcb = &bios->dcb; ++ int all_heads = (nv_two_heads(dev) ? 3 : 1); ++ ++#ifdef __powerpc__ ++ /* Apple iMac G4 NV17 */ ++ if (of_machine_is_compatible("PowerMac4,5")) { ++ fabricate_dcb_output(dcb, OUTPUT_TMDS, 0, all_heads, 1); ++ fabricate_dcb_output(dcb, OUTPUT_ANALOG, 1, all_heads, 2); ++ return; ++ } ++#endif ++ ++ /* Make up some sane defaults */ ++ fabricate_dcb_output(dcb, OUTPUT_ANALOG, LEGACY_I2C_CRT, 1, 1); ++ ++ if (nv04_tv_identify(dev, bios->legacy.i2c_indices.tv) >= 0) ++ fabricate_dcb_output(dcb, OUTPUT_TV, LEGACY_I2C_TV, ++ all_heads, 0); ++ ++ else if (bios->tmds.output0_script_ptr || ++ bios->tmds.output1_script_ptr) ++ fabricate_dcb_output(dcb, OUTPUT_TMDS, LEGACY_I2C_PANEL, ++ all_heads, 1); ++} ++ + static int +-parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads) ++parse_dcb_table(struct drm_device *dev, struct nvbios *bios) + { + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct dcb_table *dcb = &bios->dcb; +@@ -6318,12 +6311,7 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads) + + /* this situation likely means a really old card, pre DCB */ + if (dcbptr == 0x0) { +- NV_INFO(dev, "Assuming a CRT output exists\n"); +- fabricate_vga_output(dcb, LEGACY_I2C_CRT, 1); +- +- if (nv04_tv_identify(dev, bios->legacy.i2c_indices.tv) >= 0) +- fabricate_tv_output(dcb, twoHeads); +- ++ fabricate_dcb_encoder_table(dev, bios); + return 0; + } + +@@ -6383,21 +6371,7 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads) + */ + 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); +- ++ fabricate_dcb_encoder_table(dev, bios); + return 0; + } + +@@ -6787,7 +6761,7 @@ nouveau_bios_init(struct drm_device *dev) + if (ret) + return ret; + +- ret = parse_dcb_table(dev, bios, nv_two_heads(dev)); ++ ret = parse_dcb_table(dev, bios); + if (ret) + return ret; + +-- +1.7.3.2 + diff --git a/drm-nouveau-init5c.patch b/drm-nouveau-init5c.patch new file mode 100644 index 000000000..a17cfde32 --- /dev/null +++ b/drm-nouveau-init5c.patch @@ -0,0 +1,72 @@ +From 2120b3b32d96d523b18c82beb99a2d1c6135eb49 Mon Sep 17 00:00:00 2001 +From: Ben Skeggs +Date: Mon, 21 Mar 2011 21:31:21 +1000 +Subject: [PATCH] drm/nouveau: implement init table opcode 0x5c + +Signed-off-by: Ben Skeggs +--- + drivers/gpu/drm/nouveau/nouveau_bios.c | 27 ++++++++++++++++++++++++--- + 1 files changed, 24 insertions(+), 3 deletions(-) + +diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c +index 8314a49..eca191a 100644 +--- a/drivers/gpu/drm/nouveau/nouveau_bios.c ++++ b/drivers/gpu/drm/nouveau/nouveau_bios.c +@@ -269,7 +269,7 @@ struct init_tbl_entry { + int (*handler)(struct nvbios *, uint16_t, struct init_exec *); + }; + +-static int parse_init_table(struct nvbios *, unsigned int, struct init_exec *); ++static int parse_init_table(struct nvbios *, uint16_t, struct init_exec *); + + #define MACRO_INDEX_SIZE 2 + #define MACRO_SIZE 8 +@@ -2011,6 +2011,27 @@ init_sub_direct(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) + } + + static int ++init_jump(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) ++{ ++ /* ++ * INIT_JUMP opcode: 0x5C ('\') ++ * ++ * offset (8 bit): opcode ++ * offset + 1 (16 bit): offset (in bios) ++ * ++ * Continue execution of init table from 'offset' ++ */ ++ ++ uint16_t jmp_offset = ROM16(bios->data[offset + 1]); ++ ++ if (!iexec->execute) ++ return 3; ++ ++ BIOSLOG(bios, "0x%04X: Jump to 0x%04X\n", offset, jmp_offset); ++ return jmp_offset - offset; ++} ++ ++static int + init_i2c_if(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) + { + /* +@@ -3659,6 +3680,7 @@ static struct init_tbl_entry itbl_entry[] = { + { "INIT_ZM_REG_SEQUENCE" , 0x58, init_zm_reg_sequence }, + /* INIT_INDIRECT_REG (0x5A, 7, 0, 0) removed due to no example of use */ + { "INIT_SUB_DIRECT" , 0x5B, init_sub_direct }, ++ { "INIT_JUMP" , 0x5C, init_jump }, + { "INIT_I2C_IF" , 0x5E, init_i2c_if }, + { "INIT_COPY_NV_REG" , 0x5F, init_copy_nv_reg }, + { "INIT_ZM_INDEX_IO" , 0x62, init_zm_index_io }, +@@ -3700,8 +3722,7 @@ static struct init_tbl_entry itbl_entry[] = { + #define MAX_TABLE_OPS 1000 + + static int +-parse_init_table(struct nvbios *bios, unsigned int offset, +- struct init_exec *iexec) ++parse_init_table(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) + { + /* + * Parses all commands in an init table. +-- +1.7.4.1 + diff --git a/drm-nouveau-nv50-crtc-update-delay.patch b/drm-nouveau-nv50-crtc-update-delay.patch new file mode 100644 index 000000000..319770cba --- /dev/null +++ b/drm-nouveau-nv50-crtc-update-delay.patch @@ -0,0 +1,33 @@ +From 8f51b07995c55369bfd27921ef10b76c2b203fe8 Mon Sep 17 00:00:00 2001 +From: Ben Skeggs +Date: Tue, 17 Aug 2010 14:20:29 +1000 +Subject: [PATCH] drm-nouveau-nv50-crtc-update-delay + +rhbz#614552 + +Adds a short delay before doing framebuffer-only CRTC updates. Fixes +an issue that effects some cards (Quadro NVS295/FX580) where two of +these updates in quick succession hangs the display engine. + +Signed-off-by: Ben Skeggs +--- + drivers/gpu/drm/nouveau/nv50_crtc.c | 3 +++ + 1 files changed, 3 insertions(+), 0 deletions(-) + +diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c +index 2423c92..5c2aa1e 100644 +--- a/drivers/gpu/drm/nouveau/nv50_crtc.c ++++ b/drivers/gpu/drm/nouveau/nv50_crtc.c +@@ -540,6 +540,9 @@ nv50_crtc_do_mode_set_base(struct drm_crtc *crtc, int x, int y, + nouveau_bo_unpin(ofb->nvbo); + } + ++ if (update) ++ mdelay(1); ++ + 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; +-- +1.7.3.1 + diff --git a/drm-nouveau-nv86-bug.patch b/drm-nouveau-nv86-bug.patch new file mode 100644 index 000000000..aee3bedaf --- /dev/null +++ b/drm-nouveau-nv86-bug.patch @@ -0,0 +1,231 @@ +From e7e3837f6395e44b5b5fb7cdbcccaba5baf76803 Mon Sep 17 00:00:00 2001 +From: Ben Skeggs +Date: Fri, 22 Oct 2010 10:26:24 +1000 +Subject: [PATCH] drm-nouveau-nv86-bug + +drm/nv50: implement possible workaround for NV86 PGRAPH TLB flush hang + +Signed-off-by: Ben Skeggs +--- + drivers/gpu/drm/nouveau/nouveau_drv.h | 5 +++ + drivers/gpu/drm/nouveau/nouveau_mem.c | 14 +++----- + drivers/gpu/drm/nouveau/nouveau_sgdma.c | 8 ++-- + drivers/gpu/drm/nouveau/nouveau_state.c | 10 ++++++ + drivers/gpu/drm/nouveau/nv50_fifo.c | 5 +++ + drivers/gpu/drm/nouveau/nv50_graph.c | 52 +++++++++++++++++++++++++++++++ + drivers/gpu/drm/nouveau/nv50_instmem.c | 1 - + 7 files changed, 82 insertions(+), 13 deletions(-) + +diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h +index be53e92..4273390 100644 +--- a/drivers/gpu/drm/nouveau/nouveau_drv.h ++++ b/drivers/gpu/drm/nouveau/nouveau_drv.h +@@ -304,6 +304,7 @@ struct nouveau_fifo_engine { + void (*destroy_context)(struct nouveau_channel *); + int (*load_context)(struct nouveau_channel *); + int (*unload_context)(struct drm_device *); ++ void (*tlb_flush)(struct drm_device *dev); + }; + + struct nouveau_pgraph_object_method { +@@ -336,6 +337,7 @@ struct nouveau_pgraph_engine { + void (*destroy_context)(struct nouveau_channel *); + int (*load_context)(struct nouveau_channel *); + int (*unload_context)(struct drm_device *); ++ void (*tlb_flush)(struct drm_device *dev); + + void (*set_region_tiling)(struct drm_device *dev, int i, uint32_t addr, + uint32_t size, uint32_t pitch); +@@ -944,6 +946,7 @@ 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 *); ++extern void nv50_fifo_tlb_flush(struct drm_device *dev); + + /* nvc0_fifo.c */ + extern int nvc0_fifo_init(struct drm_device *); +@@ -1021,6 +1024,8 @@ 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 *); + extern int nv50_grctx_init(struct nouveau_grctx *); ++extern void nv50_graph_tlb_flush(struct drm_device *dev); ++extern void nv86_graph_tlb_flush(struct drm_device *dev); + + /* nvc0_graph.c */ + extern int nvc0_graph_init(struct drm_device *); +diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c +index 4f0ae39..514ad9f 100644 +--- a/drivers/gpu/drm/nouveau/nouveau_mem.c ++++ b/drivers/gpu/drm/nouveau/nouveau_mem.c +@@ -173,11 +173,10 @@ nv50_mem_vm_bind_linear(struct drm_device *dev, uint64_t virt, uint32_t size, + } + } + } +- dev_priv->engine.instmem.flush(dev); + +- nv50_vm_flush(dev, 5); +- nv50_vm_flush(dev, 0); +- nv50_vm_flush(dev, 4); ++ dev_priv->engine.instmem.flush(dev); ++ dev_priv->engine.fifo.tlb_flush(dev); ++ dev_priv->engine.graph.tlb_flush(dev); + nv50_vm_flush(dev, 6); + return 0; + } +@@ -207,11 +206,10 @@ nv50_mem_vm_unbind(struct drm_device *dev, uint64_t virt, uint32_t size) + pte++; + } + } +- dev_priv->engine.instmem.flush(dev); + +- nv50_vm_flush(dev, 5); +- nv50_vm_flush(dev, 0); +- nv50_vm_flush(dev, 4); ++ dev_priv->engine.instmem.flush(dev); ++ dev_priv->engine.fifo.tlb_flush(dev); ++ dev_priv->engine.graph.tlb_flush(dev); + nv50_vm_flush(dev, 6); + } + +diff --git a/drivers/gpu/drm/nouveau/nouveau_sgdma.c b/drivers/gpu/drm/nouveau/nouveau_sgdma.c +index 7f028fe..d55a583 100644 +--- a/drivers/gpu/drm/nouveau/nouveau_sgdma.c ++++ b/drivers/gpu/drm/nouveau/nouveau_sgdma.c +@@ -120,8 +120,8 @@ nouveau_sgdma_bind(struct ttm_backend *be, struct ttm_mem_reg *mem) + dev_priv->engine.instmem.flush(nvbe->dev); + + if (dev_priv->card_type == NV_50) { +- nv50_vm_flush(dev, 5); /* PGRAPH */ +- nv50_vm_flush(dev, 0); /* PFIFO */ ++ dev_priv->engine.fifo.tlb_flush(dev); ++ dev_priv->engine.graph.tlb_flush(dev); + } + + nvbe->bound = true; +@@ -162,8 +162,8 @@ nouveau_sgdma_unbind(struct ttm_backend *be) + dev_priv->engine.instmem.flush(nvbe->dev); + + if (dev_priv->card_type == NV_50) { +- nv50_vm_flush(dev, 5); +- nv50_vm_flush(dev, 0); ++ dev_priv->engine.fifo.tlb_flush(dev); ++ dev_priv->engine.graph.tlb_flush(dev); + } + + nvbe->bound = false; +diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c +index be85960..47c353c 100644 +--- a/drivers/gpu/drm/nouveau/nouveau_state.c ++++ b/drivers/gpu/drm/nouveau/nouveau_state.c +@@ -333,6 +333,15 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) + engine->graph.destroy_context = nv50_graph_destroy_context; + engine->graph.load_context = nv50_graph_load_context; + engine->graph.unload_context = nv50_graph_unload_context; ++ if (dev_priv->chipset != 0x86) ++ engine->graph.tlb_flush = nv50_graph_tlb_flush; ++ else { ++ /* from what i can see nvidia do this on every ++ * pre-NVA3 board except NVAC, but, we've only ++ * ever seen problems on NV86 ++ */ ++ engine->graph.tlb_flush = nv86_graph_tlb_flush; ++ } + engine->fifo.channels = 128; + engine->fifo.init = nv50_fifo_init; + engine->fifo.takedown = nv50_fifo_takedown; +@@ -344,6 +353,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) + engine->fifo.destroy_context = nv50_fifo_destroy_context; + engine->fifo.load_context = nv50_fifo_load_context; + engine->fifo.unload_context = nv50_fifo_unload_context; ++ engine->fifo.tlb_flush = nv50_fifo_tlb_flush; + engine->display.early_init = nv50_display_early_init; + engine->display.late_takedown = nv50_display_late_takedown; + engine->display.create = nv50_display_create; +diff --git a/drivers/gpu/drm/nouveau/nv50_fifo.c b/drivers/gpu/drm/nouveau/nv50_fifo.c +index a46a961..1da65bd 100644 +--- a/drivers/gpu/drm/nouveau/nv50_fifo.c ++++ b/drivers/gpu/drm/nouveau/nv50_fifo.c +@@ -464,3 +464,8 @@ nv50_fifo_unload_context(struct drm_device *dev) + return 0; + } + ++void ++nv50_fifo_tlb_flush(struct drm_device *dev) ++{ ++ nv50_vm_flush(dev, 5); ++} +diff --git a/drivers/gpu/drm/nouveau/nv50_graph.c b/drivers/gpu/drm/nouveau/nv50_graph.c +index cbf5ae2..8b669d0 100644 +--- a/drivers/gpu/drm/nouveau/nv50_graph.c ++++ b/drivers/gpu/drm/nouveau/nv50_graph.c +@@ -402,3 +402,55 @@ struct nouveau_pgraph_object_class nv50_graph_grclass[] = { + { 0x8597, false, NULL }, /* tesla (nva3, nva5, nva8) */ + {} + }; ++ ++void ++nv50_graph_tlb_flush(struct drm_device *dev) ++{ ++ nv50_vm_flush(dev, 0); ++} ++ ++void ++nv86_graph_tlb_flush(struct drm_device *dev) ++{ ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer; ++ bool idle, timeout = false; ++ unsigned long flags; ++ u64 start; ++ u32 tmp; ++ ++ spin_lock_irqsave(&dev_priv->context_switch_lock, flags); ++ nv_mask(dev, 0x400500, 0x00000001, 0x00000000); ++ ++ start = ptimer->read(dev); ++ do { ++ idle = true; ++ ++ for (tmp = nv_rd32(dev, 0x400380); tmp && idle; tmp >>= 3) { ++ if ((tmp & 7) == 1) ++ idle = false; ++ } ++ ++ for (tmp = nv_rd32(dev, 0x400384); tmp && idle; tmp >>= 3) { ++ if ((tmp & 7) == 1) ++ idle = false; ++ } ++ ++ for (tmp = nv_rd32(dev, 0x400388); tmp && idle; tmp >>= 3) { ++ if ((tmp & 7) == 1) ++ idle = false; ++ } ++ } while (!idle && !(timeout = ptimer->read(dev) - start > 2000000000)); ++ ++ if (timeout) { ++ NV_ERROR(dev, "PGRAPH TLB flush idle timeout fail: " ++ "0x%08x 0x%08x 0x%08x 0x%08x\n", ++ nv_rd32(dev, 0x400700), nv_rd32(dev, 0x400380), ++ nv_rd32(dev, 0x400384), nv_rd32(dev, 0x400388)); ++ } ++ ++ nv50_vm_flush(dev, 0); ++ ++ nv_mask(dev, 0x400500, 0x00000001, 0x00000001); ++ spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); ++} +diff --git a/drivers/gpu/drm/nouveau/nv50_instmem.c b/drivers/gpu/drm/nouveau/nv50_instmem.c +index ac3de05..c836ddc 100644 +--- a/drivers/gpu/drm/nouveau/nv50_instmem.c ++++ b/drivers/gpu/drm/nouveau/nv50_instmem.c +@@ -402,7 +402,6 @@ nv50_instmem_bind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj) + } + dev_priv->engine.instmem.flush(dev); + +- nv50_vm_flush(dev, 4); + nv50_vm_flush(dev, 6); + + gpuobj->im_bound = 1; +-- +1.7.3.1 + diff --git a/drm-nouveau-nva3-noaccel.patch b/drm-nouveau-nva3-noaccel.patch new file mode 100644 index 000000000..7dd9678f4 --- /dev/null +++ b/drm-nouveau-nva3-noaccel.patch @@ -0,0 +1,106 @@ +From 07a51882863d9e45b0715dcffbb66491adf2fb4e Mon Sep 17 00:00:00 2001 +From: Ben Skeggs +Date: Wed, 30 Jun 2010 13:34:05 +1000 +Subject: [PATCH] drm/nouveau: disable acceleration on NVA3/NVA5/NVA8 by default + +There's an GPU lockup problem for which the cause is currently unknown +on these chipsets. + +Until it's resolved, it's better to leave the user with a working system +without acceleration than to have random lockups. + +With this patch, acceleration will be off by default if a known problem +chipset is detected, but can be re-enabled with nouveau.noaccel=0 on +the kernel commandline. + +Signed-off-by: Ben Skeggs +--- + drivers/gpu/drm/nouveau/nouveau_drv.c | 2 +- + drivers/gpu/drm/nouveau/nouveau_drv.h | 1 + + drivers/gpu/drm/nouveau/nouveau_state.c | 23 +++++++++++++++++++---- + 3 files changed, 21 insertions(+), 5 deletions(-) + +diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c +index 946748a..9b69328 100644 +--- a/drivers/gpu/drm/nouveau/nouveau_drv.c ++++ b/drivers/gpu/drm/nouveau/nouveau_drv.c +@@ -72,7 +72,7 @@ int nouveau_ignorelid = 0; + module_param_named(ignorelid, nouveau_ignorelid, int, 0400); + + MODULE_PARM_DESC(noaccel, "Disable all acceleration"); +-int nouveau_noaccel = 0; ++int nouveau_noaccel = -1; + module_param_named(noaccel, nouveau_noaccel, int, 0400); + + MODULE_PARM_DESC(nofbaccel, "Disable fbcon acceleration"); +diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h +index 24b3d03..0cf1bee 100644 +--- a/drivers/gpu/drm/nouveau/nouveau_drv.h ++++ b/drivers/gpu/drm/nouveau/nouveau_drv.h +@@ -504,6 +504,7 @@ enum nouveau_card_type { + + struct drm_nouveau_private { + struct drm_device *dev; ++ bool noaccel; + + /* the card type, takes NV_* as values */ + enum nouveau_card_type card_type; +diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c +index be85960..896f6ae 100644 +--- a/drivers/gpu/drm/nouveau/nouveau_state.c ++++ b/drivers/gpu/drm/nouveau/nouveau_state.c +@@ -563,7 +563,7 @@ nouveau_card_init(struct drm_device *dev) + if (ret) + goto out_timer; + +- if (nouveau_noaccel) ++ if (dev_priv->noaccel) + engine->graph.accel_blocked = true; + else { + /* PGRAPH */ +@@ -613,10 +613,10 @@ out_irq: + out_display: + engine->display.destroy(dev); + out_fifo: +- if (!nouveau_noaccel) ++ if (!dev_priv->noaccel) + engine->fifo.takedown(dev); + out_graph: +- if (!nouveau_noaccel) ++ if (!dev_priv->noaccel) + engine->graph.takedown(dev); + out_fb: + engine->fb.takedown(dev); +@@ -655,7 +655,7 @@ static void nouveau_card_takedown(struct drm_device *dev) + dev_priv->channel = NULL; + } + +- if (!nouveau_noaccel) { ++ if (!dev_priv->noaccel) { + engine->fifo.takedown(dev); + engine->graph.takedown(dev); + } +@@ -861,6 +861,21 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) + if (ret) + goto err_mmio; + ++ if (nouveau_noaccel == -1) { ++ switch (dev_priv->chipset) { ++ case 0xa3: ++ case 0xa5: ++ case 0xa8: ++ dev_priv->noaccel = true; ++ break; ++ default: ++ dev_priv->noaccel = false; ++ break; ++ } ++ } else { ++ dev_priv->noaccel = (nouveau_noaccel != 0); ++ } ++ + /* Map PRAMIN BAR, or on older cards, the aperture withing BAR0 */ + if (dev_priv->card_type >= NV_40) { + int ramin_bar = 2; +-- +1.7.2.2 diff --git a/drm-nouveau-nvaf-grclass.patch b/drm-nouveau-nvaf-grclass.patch new file mode 100644 index 000000000..87e05f47f --- /dev/null +++ b/drm-nouveau-nvaf-grclass.patch @@ -0,0 +1,25 @@ +From 7abba51e3fc9e3fdd43c63eeb1a680a2e258a833 Mon Sep 17 00:00:00 2001 +From: Ben Skeggs +Date: Fri, 19 Nov 2010 18:59:15 +1000 +Subject: [PATCH] drm-nouveau-nvaf-grclass + +Signed-off-by: Ben Skeggs +--- + drivers/gpu/drm/nouveau/nv50_graph.c | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) + +diff --git a/drivers/gpu/drm/nouveau/nv50_graph.c b/drivers/gpu/drm/nouveau/nv50_graph.c +index 8b669d0..235be5f 100644 +--- a/drivers/gpu/drm/nouveau/nv50_graph.c ++++ b/drivers/gpu/drm/nouveau/nv50_graph.c +@@ -400,6 +400,7 @@ struct nouveau_pgraph_object_class nv50_graph_grclass[] = { + { 0x8297, false, NULL }, /* tesla (nv8x/nv9x) */ + { 0x8397, false, NULL }, /* tesla (nva0, nvaa, nvac) */ + { 0x8597, false, NULL }, /* tesla (nva3, nva5, nva8) */ ++ { 0x8697, false, NULL }, /* tesla (nvaf) */ + {} + }; + +-- +1.7.3.2 + diff --git a/drm-nouveau-race-fix.patch b/drm-nouveau-race-fix.patch new file mode 100644 index 000000000..bf6cc4f83 --- /dev/null +++ b/drm-nouveau-race-fix.patch @@ -0,0 +1,141 @@ +From 4733f633c4bfb0672d5bd88a8d19a03e27a3c1d0 Mon Sep 17 00:00:00 2001 +From: Ben Skeggs +Date: Fri, 23 Jul 2010 09:06:52 +1000 +Subject: [PATCH 2/2] drm-nouveau-race-fix + +drm/nouveau: fix race condition when under memory pressure + +rhbz#602663 + +When VRAM is running out it's possible that the client's push buffers get +evicted to main memory. When they're validated back in, the GPU may +be used for the copy back to VRAM, but the existing synchronisation code +only deals with inter-channel sync, not sync between PFIFO and PGRAPH on +the same channel. This leads to PFIFO fetching from command buffers that +haven't quite been copied by PGRAPH yet. + +This patch marks push buffers as so, and forces any GPU-assisted buffer +moves to be done on a different channel, which triggers the correct +synchronisation to happen before we submit them. + +After discussion with another nouveau developer, it was agreed that while +this patch is fine in itself, that we'd prefer to work out a nicer, but +likely much more invasive, fix upstream. + +Signed-off-by: Ben Skeggs +--- + drivers/gpu/drm/nouveau/nouveau_bo.c | 15 +++++++++++++ + drivers/gpu/drm/nouveau/nouveau_drv.h | 1 + + drivers/gpu/drm/nouveau/nouveau_gem.c | 36 +++++++++++++++++++++++--------- + 3 files changed, 42 insertions(+), 10 deletions(-) + +diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c +index 553a01d..5e62d1b 100644 +--- a/drivers/gpu/drm/nouveau/nouveau_bo.c ++++ b/drivers/gpu/drm/nouveau/nouveau_bo.c +@@ -36,6 +36,21 @@ + #include + #include + ++int ++nouveau_bo_sync_gpu(struct nouveau_bo *nvbo, struct nouveau_channel *chan) ++{ ++ struct nouveau_fence *prev_fence = nvbo->bo.sync_obj; ++ int ret; ++ ++ if (!prev_fence || nouveau_fence_channel(prev_fence) == chan) ++ return 0; ++ ++ spin_lock(&nvbo->bo.lock); ++ ret = ttm_bo_wait(&nvbo->bo, false, false, false); ++ spin_unlock(&nvbo->bo.lock); ++ return ret; ++} ++ + static void + nouveau_bo_del_ttm(struct ttm_buffer_object *bo) + { +diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h +index 2eb622b..70a16f3 100644 +--- a/drivers/gpu/drm/nouveau/nouveau_drv.h ++++ b/drivers/gpu/drm/nouveau/nouveau_drv.h +@@ -1167,6 +1167,7 @@ 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); ++extern int nouveau_bo_sync_gpu(struct nouveau_bo *, struct nouveau_channel *); + + /* nouveau_fence.c */ + struct nouveau_fence; +diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c +index 62ac673..613f878 100644 +--- a/drivers/gpu/drm/nouveau/nouveau_gem.c ++++ b/drivers/gpu/drm/nouveau/nouveau_gem.c +@@ -361,16 +361,11 @@ validate_list(struct nouveau_channel *chan, struct list_head *list, + + 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)) { +- NV_ERROR(dev, "fail wait other chan\n"); +- return ret; +- } ++ ret = nouveau_bo_sync_gpu(nvbo, chan); ++ if (unlikely(ret)) { ++ NV_ERROR(dev, "fail pre-validate sync\n"); ++ return ret; + } + + ret = nouveau_gem_set_domain(nvbo->gem, b->read_domains, +@@ -381,7 +376,7 @@ validate_list(struct nouveau_channel *chan, struct list_head *list, + return ret; + } + +- nvbo->channel = chan; ++ nvbo->channel = (b->read_domains & (1 << 31)) ? NULL : chan; + ret = ttm_bo_validate(&nvbo->bo, &nvbo->placement, + false, false, false); + nvbo->channel = NULL; +@@ -390,6 +385,12 @@ validate_list(struct nouveau_channel *chan, struct list_head *list, + return ret; + } + ++ ret = nouveau_bo_sync_gpu(nvbo, chan); ++ if (unlikely(ret)) { ++ NV_ERROR(dev, "fail post-validate sync\n"); ++ return ret; ++ } ++ + if (nvbo->bo.offset == b->presumed.offset && + ((nvbo->bo.mem.mem_type == TTM_PL_VRAM && + b->presumed.domain & NOUVEAU_GEM_DOMAIN_VRAM) || +@@ -615,6 +616,21 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data, + + mutex_lock(&dev->struct_mutex); + ++ /* Mark push buffers as being used on PFIFO, the validation code ++ * will then make sure that if the pushbuf bo moves, that they ++ * happen on the kernel channel, which will in turn cause a sync ++ * to happen before we try and submit the push buffer. ++ */ ++ for (i = 0; i < req->nr_push; i++) { ++ if (push[i].bo_index >= req->nr_buffers) { ++ NV_ERROR(dev, "push %d buffer not in list\n", i); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ bo[push[i].bo_index].read_domains |= (1 << 31); ++ } ++ + /* Validate buffer list */ + ret = nouveau_gem_pushbuf_validate(chan, file_priv, bo, req->buffers, + req->nr_buffers, &op, &do_reloc); +-- +1.7.2.2 + diff --git a/drm-nouveau-updates.patch b/drm-nouveau-updates.patch index 1b704ff0a..ec86a44d9 100644 --- a/drm-nouveau-updates.patch +++ b/drm-nouveau-updates.patch @@ -1,56 +1,1200 @@ +From b1bfbda896a9d9d8e8bd86dd08aac2b2f9928ce1 Mon Sep 17 00:00:00 2001 +From: Ben Skeggs +Date: Tue, 1 Jun 2010 15:32:24 +1000 +Subject: [PATCH] drm-nouveau-updates +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +drm/nouveau: use drm_mm in preference to custom code doing the same thing + +Signed-off-by: Ben Skeggs + +drm/nouveau: remove left-over !DRIVER_MODESET paths + +It's far preferable to have the driver do nothing at all for "nomodeset". + +Signed-off-by: Ben Skeggs + +drm/nouveau: missed some braces + +Luckily this had absolutely no effect whatsoever :) + +Reported-by: Marcin Slusarz +Signed-off-by: Ben Skeggs + +drm/nouveau: move LVDS detection back to connector detect() time + +Signed-off-by: Ben Skeggs + +drm/nouveau: Put the dithering check back in nouveau_connector_create. + +a7b9f9e5adef dropped it by accident. + +Signed-off-by: Francisco Jerez +Tested-by: Thibaut Girka +Signed-off-by: Ben Skeggs + +drm/nouveau: Don't clear AGPCMD completely on INIT_RESET. + +We just need to clear the SBA and ENABLE bits to reset the AGP +controller: If the AGP bridge was configured to use "fast writes", +clearing the FW bit would break the subsequent MMIO writes and +eventually end with a lockup. + +Note that all the BIOSes I've seen do the same as we did (it works for +them because they don't use MMIO), OTOH the blob leaves FW untouched. + +Signed-off-by: Francisco Jerez +Signed-off-by: Ben Skeggs + +drm/nouveau: Ignore broken legacy I2C entries. + +The nv05 card in the bug report [1] doesn't have usable I2C port +register offsets (they're all filled with zeros). Ignore them and use +the defaults. + +[1] http://bugs.launchpad.net/bugs/569505 + +Signed-off-by: Francisco Jerez +Signed-off-by: Ben Skeggs + +drm/nouveau: set encoder for lvds + +fixes oops in nouveau_connector_get_modes with nv_encoder is NULL + +Signed-off-by: Albert Damen +Signed-off-by: Francisco Jerez +Signed-off-by: Ben Skeggs + +drm/nouveau: tidy connector/encoder creation a little + +Create connectors before encoders to avoid having to do another loop across +encoder list whenever we create a new connector. This allows us to pass +the connector to the encoder creation functions, and avoid using a +create_resources() callback since we can now call it directly. + +This can also potentially modify the connector ordering on nv50. On cards +where the DCB connector and encoder tables are in the same order, things +will be unchanged. However, there's some cards where the ordering between +the tables differ, and in one case, leads us to naming the connectors +"wrongly". + +Signed-off-by: Ben Skeggs + +drm/nouveau: downgrade severity of most init table parser errors + +As long as we know the length of the opcode, we're probably better off +trying to parse the remainder of an init table rather than aborting in +the middle of it. + +Signed-off-by: Ben Skeggs + +drm/nv50: fix DP->DVI if output has been programmed for native DP previously + +Signed-off-by: Ben Skeggs + +drm/nv50: DCB quirk for Dell M6300 + +Uncertain if this is a weirdo configuration, or a BIOS bug. If it's not +a BIOS bug, we still don't know how to make it work anyway so ignore a +"conflicting" DCB entry to prevent a display hang. + +Signed-off-by: Ben Skeggs + +drm: disable encoder rather than dpms off in drm_crtc_prepare_encoders() + +Original behaviour will be preserved for drivers that don't implement +disable() hooks for an encoder. + +Signed-off-by: Ben Skeggs + +drm/nv50: supply encoder disable() hook for SOR outputs + +Allows us to remove a driver hack that used to be necessary to disable +encoders in certain situations before setting up a mode. The DRM has +better knowledge of when this is needed than the driver does. + +This fixes a number of display switching issues. + +Signed-off-by: Ben Skeggs + +drm/nv50: fix regression caused by ed15e77b6ee7c4fa6f50c18b3325e7f96ed3aade + +It became possible for us to have connectors present without any encoders +attached (TV out, we don't support TVDAC yet), which caused the DDX to +segfault. + +Signed-off-by: Ben Skeggs + +drm/nv04: fix regression caused by ed15e77b6ee7c4fa6f50c18b3325e7f96ed3aade + +Signed-off-by: Ben Skeggs + +drm/nv50: when debugging on, log which crtc we connect an encoder to + +Signed-off-by: Ben Skeggs + +drm/nv17-nv40: Avoid using active CRTCs for load detection. + +Signed-off-by: Francisco Jerez +Signed-off-by: Ben Skeggs + +drm/nv04-nv40: Prevent invalid DAC/TVDAC combinations. + +Signed-off-by: Francisco Jerez +Signed-off-by: Ben Skeggs + +drm/nv04-nv40: Disable connector polling when there're no spare CRTCs left. + +Load detection needs the connector wired to a CRTC, when there are no +inactive CRTCs left that means we need to cut some other head off for +a while, causing intermittent flickering. + +Signed-off-by: Francisco Jerez +Signed-off-by: Ben Skeggs + +drm/nv50: fix memory detection for cards with >=4GiB VRAM + +Signed-off-by: Ben Skeggs + +drm/nouveau: Fix a couple of sparse warnings. + +Signed-off-by: Francisco Jerez +Signed-off-by: Ben Skeggs + +drm/nouveau: INIT_CONFIGURE_PREINIT/CLK/MEM on newer BIOSes is not an error. + +No need to spam the logs when they're found, they're equivalent to +INIT_DONE. + +Signed-off-by: Francisco Jerez +Signed-off-by: Ben Skeggs + +drm/nv04-nv40: Drop redundant logging. + +Signed-off-by: Francisco Jerez +Signed-off-by: Ben Skeggs + +drm/nouveau: Move the fence wait before migration resource clean-up. + +Avoids an oops in the fence wait failure path (bug 26521). + +Signed-off-by: Francisco Jerez +Tested-by: Marcin Slusarz +Signed-off-by: Ben Skeggs + +drm/nouveau: Workaround broken TV load detection on a "Zotac FX5200". + +The blob seems to have the same problem so it's probably a hardware +issue (bug 28810). + +Signed-off-by: Francisco Jerez +Signed-off-by: Ben Skeggs + +drm/nv50: send evo "update" command after each disconnect + +It turns out that the display engine signals an interrupt for disconnects +too. In order to make it easier to process the display interrupts +correctly, we want to ensure we only get one operation per interrupt +sequence - this is what this commit achieves. + +Signed-off-by: Ben Skeggs + +drm/nv50: rewrite display irq handler + +The previous handler basically worked correctly for a full-blown mode +change. However, it did nothing at all when a partial (encoder only) +reconfiguation was necessary, leading to the display hanging on certain +types of mode switch. + +Signed-off-by: Ben Skeggs + +drm/nouveau: move DP script invocation to nouveau_dp.c + +Signed-off-by: Ben Skeggs + +drm/nv50: set DP display power state during DPMS + +Signed-off-by: Ben Skeggs + +drm/nouveau: add scaler-only modes for eDP too + +Signed-off-by: Ben Skeggs + +drm/nouveau: remove dev_priv->init_state and friends + +Nouveau will no longer load at all if card initialisation fails, so all +these checks are unnecessary. + +Signed-off-by: Ben Skeggs + +drm/nv50: implement DAC disconnect fix missed in earlier commit + +Signed-off-by: Ben Skeggs + +drm/nouveau: reduce usage of fence spinlock to when absolutely necessary + +Signed-off-by: Ben Skeggs + +drm/nouveau: place notifiers in system memory by default + +Signed-off-by: Ben Skeggs + +drm/nouveau: add instmem flush() hook + +This removes the previous prepare_access() and finish_access() hooks, and +replaces it with a much simpler flush() hook. + +All the chipset-specific code before nv50 has its use removed completely, +as it's not required there at all. + +Signed-off-by: Ben Skeggs + +drm/nv50: move tlb flushing to a helper function + +Signed-off-by: Ben Skeggs + +drm/nouveau: remove ability to use external firmware + +This was always really a developer option, and if it's really necessary we +can hack this in ourselves. + +Signed-off-by: Ben Skeggs + +drm/nouveau: allocate fixed amount of PRAMIN per channel on all chipsets + +Previously only done on nv50+ + +This commit also switches unknown NV2x/NV3x chipsets to noaccel mode. + +Signed-off-by: Ben Skeggs + +drm/nouveau: remove unused fbdev_info + +Signed-off-by: Ben Skeggs + +drm/nv50: cleanup nv50_fifo.c + +Signed-off-by: Ben Skeggs + +drm/nv20-nv30: move context table object out of dev_priv + +Signed-off-by: Ben Skeggs + +drm/nv50: fix dp_set_tmds to work on the right OR + +Signed-off-by: Ben Skeggs + +drm/nouveau: fix mtrr cleanup path + +Signed-off-by: Ben Skeggs + +drm/nv50: move dp_set_tmds() function to happen in the last display irq + +It seems on some chipsets that doing this from the 0x20 handler causes the +display engine to not ever signal the final 0x40 stage. + +Signed-off-by: Ben Skeggs + +drm/nouveau: initialise display before enabling interrupts + +In some situations it's possible we can receive a spurious hotplug IRQ +before we're ready to handle it, leading to an oops. + +Calling the display init before enabling interrupts should clear any +pending IRQs on the GPU and prevent this from happening. + +Signed-off-by: Ben Skeggs + +drm/nouveau: Disable PROM access on init. + +On older cards ( + +drm/i2c/ch7006: Fix up suspend/resume. + +Signed-off-by: Francisco Jerez + +drm/nv04: Enable context switching on PFIFO init. + +Fixes a lockup when coming back from suspend. + +Signed-off-by: Francisco Jerez + +drm/nv50: fix RAMHT size + +Signed-off-by: Ben Skeggs + +drm/nv50: use correct PRAMIN flush register on original nv50 + +Signed-off-by: Ben Skeggs + +drm/nouveau: remove quirk to fabricate DVI-A output on DCB 1.5 boards + +There's a report of this quirk breaking modesetting on at least one board. +After discussion with Francisco Jerez, we've decided to remove it: + + it's not worth limiting the quirk to just where we know it can + work? i'm happy either way really :) + hmm, don't think so, most if not all DCB15 cards have just one DAC + and with that quirk there's no way to tell if the load comes from + the VGA or DVI port + +Signed-off-by: Ben Skeggs +Acked-by: Francisco Jerez + +drm/nouveau: support fetching LVDS EDID from ACPI + +Based on a patch from Matthew Garrett. + +Signed-off-by: Ben Skeggs +Acked-by: Matthew Garrett + +drm/nv50: fix regression that break LVDS in some places + +A previous commit started additionally using the SOR link when trying to +match the correct output script. However, we never fill in this field +for LVDS so we can never match a script at all. + +Signed-off-by: Ben Skeggs + +drm/nouveau: Fix a sparse warning. + +It doesn't like variable length arrays. + +Signed-off-by: Francisco Jerez + +drm/nouveau: Don't pick an interlaced mode as the panel native mode. + +Rescaling interlaced modes isn't going to work correctly, and even if +it did, come on, interlaced flat panels? are you pulling my leg? + +Signed-off-by: Francisco Jerez + +drm/nouveau: Add another Zotac FX5200 TV-out quirk. + +Signed-off-by: Francisco Jerez + +drm/nouveau: Add some PFB register defines. + +Also collect all the PFB registers in a single place and remove some +duplicated definitions. + +Signed-off-by: Francisco Jerez + +drm/nv04-nv3x: Implement init-compute-mem. + +Init-compute-mem was the last piece missing for nv0x-nv3x card +cold-booting. This implementation is somewhat lacking but it's been +reported to work on most chipsets it was tested in. Let me know if it +breaks suspend to RAM for you. + +Signed-off-by: Francisco Jerez +Tested-by: Patrice Mandin +Tested-by: Ben Skeggs +Tested-by: Xavier Chantry +Tested-by: Marcin KoÅ›cielnicki + +drm/i2c/ch7006: Don't assume that the specified config points to static memory. + +Signed-off-by: Francisco Jerez + +drm/nouveau: Add some generic I2C gadget detection code. + +Clean up and move the external TV encoder detection code to +nouveau_i2c.c, it's also going to be useful for external TMDS and DDC +detection. + +Signed-off-by: Francisco Jerez + +drm/nouveau: Remove useless CRTC_OWNER logging. + +Signed-off-by: Francisco Jerez + +drm/nouveau: No need to lock/unlock the VGA CRTC regs all the time. + +Locking only makes sense in the VBIOS parsing code as it's executed +before CRTC init. + +Signed-off-by: Francisco Jerez + +drm/nouveau: Reset CRTC owner to 0 before BIOS init. + +Fixes suspend+multihead on some boards that also use BIOS scripts for +modesetting. + +Signed-off-by: Francisco Jerez + +drm/nouveau: fix build without CONFIG_ACPI + +Signed-off-by: Ben Skeggs + +drm/nouveau: add nv_mask register accessor + +Signed-off-by: Ben Skeggs + +drm/nv50: add function to control GPIO IRQ reporting + +Signed-off-by: Ben Skeggs + +drm/nouveau: disable hotplug detect around DP link training + +Signed-off-by: Ben Skeggs + +drm/nv30: Init the PFB+0x3xx memory timing regs. + +Fixes the randomly flashing vertical lines seen on some nv3x after a +cold-boot. + +Signed-off-by: Francisco Jerez + +drm/nouveau: Reset AGP before running the init scripts. + +BIOS scripts usually make an attempt to reset the AGP controller, +however on some nv4x cards doing it properly involves switching FW off +and on: if we do that without updating the AGP bridge settings +accordingly (e.g. with the corresponding calls to agp_enable()) we +will be locking ourselves out of the card MMIO space. Do it from +nouveau_mem_reset_agp() before the init scripts are executed. + +Signed-off-by: Francisco Jerez + +drm/nouveau: Put back the old 2-messages I2C slave test. + +I was hoping we could detect I2C devices at a given address without +actually writing data into them, but apparently some DDC slaves get +confused with 0-bytes transactions. Put the good old test back. + +Reported-by: Ben Skeggs +Signed-off-by: Francisco Jerez + +drm/nouveau: Move display init to a new nouveau_engine. + +Signed-off-by: Francisco Jerez + +drm/nouveau: Get rid of the remaining VGA CRTC locking. + +Signed-off-by: Francisco Jerez + +drm/nouveau: Fix TV-out detection on unposted cards lacking a usable DCB table. + +Signed-off-by: Francisco Jerez + +drm/nv50: correct wait condition for instmem flush + +Reported-by: Marcin KoÅ›cielnicki +Signed-off-by: Ben Skeggs + +drm/nouveau: introduce gpio engine + +Signed-off-by: Ben Skeggs + +drm/nv50: fix some not-error error messages + +Signed-off-by: Ben Skeggs + +drm/nv50: use custom i2c algo for dp auxch + +This makes it easier to see how this is working, and lets us transfer the +EDID in blocks of 16 bytes. + +The primary reason for this change is because debug logs are rather hard +to read with the hundreds of single-byte auxch transactions that occur. + +Signed-off-by: Ben Skeggs + +drm/nouveau: set TASK_(UN)INTERRUPTIBLE before schedule_timeout() + +set_current_state() is called only once before the first iteration. +After return from schedule_timeout() current state is TASK_RUNNING. If +we are going to wait again, set_current_state() must be called. + +Signed-off-by: Kulikov Vasiliy +Signed-off-by: Francisco Jerez + +drm/nouveau: remove unused ttm bo list + +Signed-off-by: Ben Skeggs + +drm/nouveau: Fix AGP reset when AGP FW is already enabled on init. + +Previously nouveau_mem_reset_agp() was only disabling AGP fast writes +when coming back from suspend. However, the "locked out of the card +because of FW" problem can also be reproduced on init if you +unload/reload nouveau.ko several times. This patch makes the AGP code +reset FW on init. + +Signed-off-by: Francisco Jerez + +drm/nouveau: unwind on load errors + +nouveau_load() just returned directly if there was an error instead of +releasing resources. + +Signed-off-by: Dan Carpenter +Reviewed-by: Marcin Slusarz +Signed-off-by: Francisco Jerez + +drm/nouveau: Don't pass misaligned offsets to io_mapping_map_atomic_wc(). + +Signed-off-by: Francisco Jerez + +drm/nouveau: Fix the INIT_CONFIGURE_PREINIT BIOS opcode. + +Signed-off-by: Francisco Jerez + +drm/nouveau: Ack the context switch interrupt before switching contexts. + +Leaving the IRQ unack'ed while switching contexts makes the switch +fail randomly on some nv1x. + +Signed-off-by: Francisco Jerez + +drm/nv10: Fix up switching of NV10TCL_DMA_VTXBUF. + +Not very nice, but I don't think there's a simpler workaround. + +Signed-off-by: Francisco Jerez + +drm/nv17-nv4x: Attempt to init some external TMDS transmitters. + +sil164 and friends are the most common, usually they just need to be +poked once because a fixed configuration is enough for any modes and +clocks, so they worked without this patch if the BIOS had done a good +job on POST. Display couldn't survive a suspend/resume cycle though. +Unfortunately, BIOS scripts are useless here. + +Signed-off-by: Francisco Jerez + +drm/nouveau: No need to set slave TV encoder configs explicitly. + +Signed-off-by: Francisco Jerez + +drm/nv30: Workaround dual TMDS brain damage. + +Signed-off-by: Francisco Jerez + +drm/nvc0: starting point for GF100 support, everything stubbed + +Signed-off-by: Ben Skeggs + +drm/nvc0: allow INIT_GPIO + +Signed-off-by: Ben Skeggs + +drm/nvc0: implement memory detection + +Signed-off-by: Ben Skeggs + +drm/nvc0: rudimentary instmem support + +Signed-off-by: Ben Skeggs + +drm/nvc0: fix evo dma object so we display something + +Signed-off-by: Ben Skeggs + +drm/nvc0: implement crtc pll setting + +Signed-off-by: Ben Skeggs + +drm/nouveau: implement init table op 0x57, INIT_LTIME + +Signed-off-by: Ben Skeggs +Signed-off-by: Marcin KoÅ›cielnicki + +drm/nouveau: implement init table opcodex 0x5e and 0x9a + +Signed-off-by: Ben Skeggs +Signed-off-by: Marcin KoÅ›cielnicki + +drm/nvc0: backup bar3 channel on suspend + +Signed-off-by: Ben Skeggs + +drm/nouveau: reduce severity of some "error" messages + +There's some known configurations where the lack of these tables/scripts +is perfectly normal, reduce visibilty of complaint messages to debug. + +Signed-off-by: Ben Skeggs + +drm/nouveau: Init dcb->or on cards that have no usable DCB table. + +We need a valid OR value because there're a few nv17 cards with DCB v1.4. + +Signed-off-by: Francisco Jerez + +drm/nv04: Fix up SGRAM density detection. + +Signed-off-by: Francisco Jerez + +drm/nv30: Fix PFB init for nv31. + +Fixes a regression introduced by 58bbb63720c8997e0136fe1884101e7ca40d68fd +(fdo bug 29324). + +Reported-by: Johannes Obermayr +Signed-off-by: Francisco Jerez + +drm/nouveau: Fix DCB TMDS config parsing. + +Thinko caused by 43bda05428a3d2021f3c12220073e0251c65df8b. + +Signed-off-by: Francisco Jerez + +drm/nvc0: fix typo in PRAMIN flush + +Signed-off-by: Ben Skeggs + +drm/nouveau: Don't try DDC on the dummy I2C channel. + +Signed-off-by: Francisco Jerez + +drm/nv50: fix minor thinko from nvc0 changes + +drm/nouveau: check for error when allocating/mapping dummy page + +Signed-off-by: Ben Skeggs + +drm/nouveau: remove warning about unknown tmds table revisions + +This message is apparently confusing people, and is being blamed for some +modesetting issues. Lets remove the message, and instead replace it +with an unconditional printout of the table revision. + +Signed-off-by: Ben Skeggs + +drm/nouveau: punt some more log messages to debug level + +Signed-off-by: Ben Skeggs + +drm/nv50-nvc0: ramht_size is meant to be in bytes, not entries + +Fixes an infinite loop that can happen in RAMHT lookup. + +Signed-off-by: Ben Skeggs + +drm/nouveau: Add TV-out quirk for an MSI nForce2 IGP. + +The blob also thinks there's a TV connected, so hardware bug... + +Signed-off-by: Francisco Jerez + +drm/nouveau: Workaround missing GPIO tables on an Apple iMac G4 NV18. + +This should fix the reported TV-out load detection false positives +(fdo bug 29455). + +Reported-by: Vlado Plaga +Signed-off-by: Francisco Jerez + +drm/nvc0: fix thinko in instmem suspend/resume + +Signed-off-by: Ben Skeggs + +drm/nv50: calculate vram reordering block size + +Will be used at a later point when we plug in an alternative VRAM memory +manager for GeForce 8+ boards. + +Based on pscnv code to do the same. + +Signed-off-by: Ben Skeggs +Signed-off-by: Marcin KoÅ›cielnicki + +drm/nv50: add dcb type 14 to enum to prevent compiler complaint + +Signed-off-by: Ben Skeggs + +drm/nouveau: Use a helper function to match PCI device/subsystem IDs. + +Signed-off-by: Francisco Jerez + +drm/nv30: Apply modesetting to the correct slave encoder + +Signed-off-by: Patrice Mandin +Reviewed-by: Francisco Jerez + +drm/nouveau: Fix backlight control on PPC machines with an internal TMDS panel. + +This commit fixes fdo bug 29685. + +Reported-by: Vlado Plaga +Signed-off-by: Francisco Jerez + +drm/nouveau: Fix TMDS on some DCB1.5 boards. + +The TMDS output of an nv11 was being detected as LVDS, because it uses +DCB type 2 for TMDS instead of type 4. + +Reported-by: Bertrand VIEILLE +Signed-off-by: Francisco Jerez + +drm/nv20: Don't use pushbuf calls on the original nv20. + +The "return" command is buggy on the original nv20, it jumps back to +the caller address as expected, but it doesn't clear the subroutine +active bit making the subsequent pushbuf calls fail with a "stack" +overflow. + +Signed-off-by: Francisco Jerez + +drm/nouveau: Fix suspend on some nv4x AGP cards. + +On some nv4x cards (specifically, the ones that use an internal +PCIE->AGP bridge) the AGP controller state isn't preserved after a +suspend/resume cycle, and the AGP control registers have moved from +0x18xx to 0x100xx, so the FW check in nouveau_mem_reset_agp() doesn't +quite work. Check "dev->agp->mode" instead. + +Signed-off-by: Francisco Jerez + +drm/nv20: Use the nv30 CRTC bandwidth calculation code. + +nv2x CRTC FIFOs are as large as in nv3x (4kB it seems), and the FIFO +control registers have the same layout: we can make them share the +same implementation. + +Previously we were using the nv1x code, but the calculated FIFO +watermarks are usually too low for nv2x and they cause horrible +scanout artifacts. They've gone unnoticed until now because we've been +leaving one of the bandwidth regs uninitialized (CRE 47, which +contains the most significant bits of FFLWM), so everything seemed to +work fine except in some cases after a cold boot, depending on the +memory bandwidth and pixel clocks used. + +Signed-off-by: Francisco Jerez + +drm/nv50: add new accelerated bo move funtion + +Hopefully this one will be better able to cope with moving tiled buffers +around without getting them all scrambled as a result. + +Signed-off-by: Ben Skeggs + +drm/nouveau: move check for no-op bo move before memcpy fallback + +Signed-off-by: Ben Skeggs + +drm/nouveau: remove second map of notifier bo + +Signed-off-by: Ben Skeggs + +drm/nouveau: require explicit unmap of kmapped bos + +Signed-off-by: Ben Skeggs + +drm/nv17-nv4x: Fix analog load detection false positive on rare occasions. + +On some boards the residual current DAC outputs can draw when they're +disconnected can be high enough to give a false load detection +positive (I've only seen it in the S-video luma output of some cards, +but just to be sure). The output line capacitance is limited and +sampling twice should fix it reliably. + +Signed-off-by: Francisco Jerez + +drm/nv40: Try to set up CRE_LCD even if it has unknown bits set. + +They don't seem to do anything useful, and we really want to program +CRE_LCD if we aren't lucky enough to find the right CRTC binding +already set. + +Signed-off-by: Francisco Jerez + +drm/nouveau: have nv_mask return original register value + +Signed-off-by: Ben Skeggs + +drm/nv50: initialize ramht_refs list for faked 0 channel + +We need it for PFIFO_INTR_CACHE_ERROR interrupt handling, +because nouveau_fifo_swmthd looks for matching gpuobj in +ramht_refs list. +It fixes kernel panic in nouveau_gpuobj_ref_find. + +Signed-off-by: Marcin Slusarz +Signed-off-by: Ben Skeggs + +drm/nouveau: move ramht code out of nouveau_object.c, nothing to see here + +Reviewed-by: Francisco Jerez +Signed-off-by: Ben Skeggs + +drm/nouveau: modify object accessors, offset in bytes rather than dwords + +Reviewed-by: Francisco Jerez +Signed-off-by: Ben Skeggs + +drm/nv50: demagic grctx, and add NVAF support + +Signed-off-by: Ben Skeggs +Signed-off-by: Marcin KoÅ›cielnicki + +drm/nv50: move vm trap to nv50_fb.c + +Signed-off-by: Ben Skeggs + +drm/nv50: report BAR access faults + +Signed-off-by: Ben Skeggs + +drm/nouveau: rebase per-channel pramin heap offsets to 0 + +Reviewed-by: Francisco Jerez +Signed-off-by: Ben Skeggs + +drm/nouveau: remove nouveau_gpuobj_ref completely, replace with sanity + +Reviewed-by: Francisco Jerez +Signed-off-by: Ben Skeggs + +drm/nouveau: simplify fake gpu objects + +Reviewed-by: Francisco Jerez +Signed-off-by: Ben Skeggs + +drm/nv50: allow gpuobjs that aren't mapped into aperture + +Reviewed-by: Francisco Jerez +Signed-off-by: Ben Skeggs + +drm/nouveau: rework init ordering so nv50_instmem.c can be less bad + +Reviewed-by: Francisco Jerez +Signed-off-by: Ben Skeggs + +drm/nouveau: tidy ram{ht,fc,ro} a bit + +Reviewed-by: Francisco Jerez +Signed-off-by: Ben Skeggs + +drm/nouveau: add spinlock around ramht modifications + +Reviewed-by: Francisco Jerez +Signed-off-by: Ben Skeggs + +drm/nouveau: fix gpuobj refcount to use atomics + +Reviewed-by: Francisco Jerez +Signed-off-by: Ben Skeggs + +drm/nouveau: protect gpuobj list + global instmem heap with spinlock + +Reviewed-by: Francisco Jerez +Signed-off-by: Ben Skeggs + +drm/nouveau: remove nouveau_gpuobj_late_takedown + +Reviewed-by: Francisco Jerez +Signed-off-by: Ben Skeggs + +drm/nouveau: protect ramht_find() from oopsing if on channel without ramht + +This doesn't actually happen now, but there's a test case for an earlier +kernel where a GPU error is signalled on one of nv50's fake channels, and +the ramht lookup by the IRQ handler triggered an oops. + +This adds a check for RAMHT's existance on a channel before looking up +an object handle. + +Signed-off-by: Ben Skeggs + +drm/nv50: fix SOR count for early chipsets + +Signed-off-by: Ben Skeggs + +drm/nouveau: Break some long lines in the TV-out code. + +Signed-off-by: Francisco Jerez + +drm/nouveau: Don't remove ramht entries from the neighboring channels. + +Signed-off-by: Francisco Jerez + +drm/nouveau: Don't enable AGP FW on nv18. + +FW seems to be broken on nv18, it causes random lockups and breaks +suspend/resume even with the blob. + +Signed-off-by: Francisco Jerez + +drm/nouveau: Add module parameter to override the default AGP rate. + +Signed-off-by: Francisco Jerez + +drm/nouveau: PRAMIN is available from the start on pre-nv50. + +This makes sure that RAMHT is cleared correctly on start up. + +Signed-off-by: Francisco Jerez + +drm/nouveau: Remove implicit argument from nv_wait(). + +Signed-off-by: Francisco Jerez + +drm/nouveau: Simplify tile region handling. + +Instead of emptying the caches to avoid a race with the PFIFO puller, +go straight ahead and try to recover from it when it happens. Also, +kill pfifo->cache_flush and tile->lock, we don't need them anymore. + +Signed-off-by: Francisco Jerez + +drm/nouveau: handle fifo pusher errors better + +The most important part of this change is that we now instruct PFIFO to +drop all pending fetches, rather than attempting to skip a single dword +and hope that things would magically sort themselves out - they usually +don't, and we end up with PFIFO being completely hung. + +This commit also adds somewhat more useful logging when these exceptions +occur. + +Signed-off-by: Ben Skeggs + +fix compile error due to upstream differences + +drm/nouveau: we can't free ACPI EDID, so make a copy that we can + +The rest of the connector code assumes we can kfree() the EDID pointer. +This causes things to blow up with the ACPI EDID pointer we get +passed. + +Signed-off-by: Ben Skeggs + +drm/nv50: mark PCIEGART pages non-present rather than using dummy page + +Signed-off-by: Ben Skeggs + +drm/nouveau: zero dummy page + +Signed-off-by: Ben Skeggs + +drm/nv50: fix 100c90 write on nva3 + +Signed-off-by: Ben Skeggs + +drm/nouveau: Fix build regression, undefined reference to `acpi_video_get_edid' + +Build breakage: + +drivers/built-in.o: In function `nouveau_acpi_edid': +(.text+0x13404e): undefined reference to `acpi_video_get_edid' +make: *** [.tmp_vmlinux1] Error 1 + +Introduced by: + +a6ed76d7ffc62ffa474b41d31b011b6853c5de32 is the first bad commit +commit a6ed76d7ffc62ffa474b41d31b011b6853c5de32 +Author: Ben Skeggs +Date: Mon Jul 12 15:33:07 2010 +1000 + + drm/nouveau: support fetching LVDS EDID from ACPI + + Based on a patch from Matthew Garrett. + + Signed-off-by: Ben Skeggs + Acked-by: Matthew Garrett + +It doesn't seem to revert cleanly, but the problem lies in these +two config entries: + +CONFIG_ACPI=y +CONFIG_ACPI_VIDEO=m + +Adding a select for ACPI_VIDEO appears to be the best solution, and +is comparable to what is done in DRM_I915. Builds, boots, and appears to +work correctly. + +Signed-off-by: Philip J. Turmel +Signed-off-by: Francisco Jerez + +drm/nv50: flush bar1 vm / dma object setup before poking 0x1708 + +Should fix issues noticed on NVAC (MacBook Pro / ION) since gpuobj +rework. + +Signed-off-by: Ben Skeggs + +drm/nouveau: correct INIT_DP_CONDITION subcondition 5 + +Fixes DP output on a GTX 465 board I have. + +Signed-off-by: Ben Skeggs + +drm/nouveau: add debugfs file to forcibly evict everything from vram + +Very useful for debugging buffer migration issues. + +Signed-off-by: Ben Skeggs + +drm/nv50: assume smaller tiles for bo moves + +Somehow fixes some corruption seen in KDE.. + +Signed-off-by: Ben Skeggs + +drm/nouveau: fix panels using straps-based mode detection + +nouveau_bios_fp_mode() zeroes the mode struct before filling in relevant +entries. This nukes the mode id initialised by drm_mode_create(), and +causes warnings from idr when we try to remove the mode. + +Signed-off-by: Ben Skeggs + +drm/nv10: Don't oops if the card wants to switch to a channel with no grctx. + +Signed-off-by: Francisco Jerez + +drm/nouveau: enable enhanced framing only if DP display supports it + +Reported-by: Adam Jackson +Signed-off-by: Ben Skeggs + +drm/nouveau: fix typo in c2aa91afea5f7e7ae4530fabd37414a79c03328c + +Signed-off-by: Ben Skeggs + +drm/nouveau: fix required mode bandwidth calculation for DP + +This should fix eDP on certain laptops with 18-bit panels, we were rejecting +the panel's native mode due to thinking there was insufficient bandwidth +for it. + +Signed-off-by: Ben Skeggs + +drm/nv30-nv40: Fix postdivider mask when writing engine/memory PLLs. + +Signed-off-by: Francisco Jerez + +drm/nv0x-nv4x: Leave the 0x40 bit untouched when changing CRE_LCD. + +It's an unrelated PLL filtering control bit, leave it alone when +changing the CRTC-encoder binding. + +Signed-off-by: Francisco Jerez + +drm/nv50: prevent (IB_PUT == IB_GET) for occurring unless idle + +Should fix a DMA race condition I've never seen myself, but could be +the culprit in some random hangs that have been reported. + +Signed-off-by: Ben Skeggs + +drm/nouveau: Add a module option to force card POST. + +Signed-off-by: Ben Skeggs + +drm/nouveau: Try to fetch an EDID from OF if DDC fails. + +More Apple brain damage, it fixes the modesetting failure on an eMac +G4 (fdo bug 29810). + +Reported-by: Zoltan Varnagy +Signed-off-by: Francisco Jerez + +drm/nouveau: better handling of unmappable vram + +Signed-off-by: Ben Skeggs + +drm/nouveau: fix chipset vs card_type thinko + +Signed-off-by: Ben Skeggs +--- drivers/gpu/drm/drm_crtc_helper.c | 22 +- drivers/gpu/drm/i2c/ch7006_drv.c | 22 +- drivers/gpu/drm/i2c/ch7006_priv.h | 2 +- - drivers/gpu/drm/nouveau/Makefile | 2 +- - drivers/gpu/drm/nouveau/nouveau_acpi.c | 38 ++- - drivers/gpu/drm/nouveau/nouveau_bios.c | 636 +++++++++++++++++++++------ - drivers/gpu/drm/nouveau/nouveau_bios.h | 4 +- - drivers/gpu/drm/nouveau/nouveau_bo.c | 9 +- - drivers/gpu/drm/nouveau/nouveau_calc.c | 4 +- - drivers/gpu/drm/nouveau/nouveau_channel.c | 5 - - drivers/gpu/drm/nouveau/nouveau_connector.c | 404 ++++++++---------- - drivers/gpu/drm/nouveau/nouveau_connector.h | 7 +- - drivers/gpu/drm/nouveau/nouveau_dma.c | 8 +- - drivers/gpu/drm/nouveau/nouveau_dp.c | 24 +- - drivers/gpu/drm/nouveau/nouveau_drv.c | 31 +- - drivers/gpu/drm/nouveau/nouveau_drv.h | 90 ++--- - drivers/gpu/drm/nouveau/nouveau_encoder.h | 10 +- - drivers/gpu/drm/nouveau/nouveau_fbcon.c | 2 +- - drivers/gpu/drm/nouveau/nouveau_fence.c | 31 +- - drivers/gpu/drm/nouveau/nouveau_gem.c | 11 +- - drivers/gpu/drm/nouveau/nouveau_grctx.c | 160 ------- - drivers/gpu/drm/nouveau/nouveau_i2c.c | 34 ++ - drivers/gpu/drm/nouveau/nouveau_i2c.h | 3 + - drivers/gpu/drm/nouveau/nouveau_mem.c | 275 ++---------- - drivers/gpu/drm/nouveau/nouveau_notifier.c | 30 +- - drivers/gpu/drm/nouveau/nouveau_object.c | 105 ++--- - drivers/gpu/drm/nouveau/nouveau_reg.h | 91 +++-- - drivers/gpu/drm/nouveau/nouveau_sgdma.c | 46 +-- - drivers/gpu/drm/nouveau/nouveau_state.c | 172 +++----- - drivers/gpu/drm/nouveau/nv04_crtc.c | 5 + - drivers/gpu/drm/nouveau/nv04_dac.c | 37 ++- - drivers/gpu/drm/nouveau/nv04_dfp.c | 12 +- - drivers/gpu/drm/nouveau/nv04_display.c | 64 ++-- - drivers/gpu/drm/nouveau/nv04_fifo.c | 20 +- + drivers/gpu/drm/nouveau/Kconfig | 1 + + drivers/gpu/drm/nouveau/Makefile | 12 +- + drivers/gpu/drm/nouveau/nouveau_acpi.c | 38 +- + drivers/gpu/drm/nouveau/nouveau_bios.c | 914 ++++++-- + drivers/gpu/drm/nouveau/nouveau_bios.h | 6 +- + drivers/gpu/drm/nouveau/nouveau_bo.c | 251 ++- + drivers/gpu/drm/nouveau/nouveau_calc.c | 10 +- + drivers/gpu/drm/nouveau/nouveau_channel.c | 18 +- + drivers/gpu/drm/nouveau/nouveau_connector.c | 473 ++-- + drivers/gpu/drm/nouveau/nouveau_connector.h | 10 +- + drivers/gpu/drm/nouveau/nouveau_debugfs.c | 16 + + drivers/gpu/drm/nouveau/nouveau_dma.c | 23 +- + drivers/gpu/drm/nouveau/nouveau_dp.c | 138 +- + drivers/gpu/drm/nouveau/nouveau_drv.c | 49 +- + drivers/gpu/drm/nouveau/nouveau_drv.h | 310 ++-- + drivers/gpu/drm/nouveau/nouveau_encoder.h | 17 +- + drivers/gpu/drm/nouveau/nouveau_fbcon.c | 4 +- + drivers/gpu/drm/nouveau/nouveau_fence.c | 35 +- + drivers/gpu/drm/nouveau/nouveau_gem.c | 15 +- + drivers/gpu/drm/nouveau/nouveau_grctx.c | 160 -- + drivers/gpu/drm/nouveau/nouveau_grctx.h | 2 +- + drivers/gpu/drm/nouveau/nouveau_hw.c | 15 +- + drivers/gpu/drm/nouveau/nouveau_i2c.c | 83 +- + drivers/gpu/drm/nouveau/nouveau_i2c.h | 11 +- + drivers/gpu/drm/nouveau/nouveau_irq.c | 129 +- + drivers/gpu/drm/nouveau/nouveau_mem.c | 529 ++--- + drivers/gpu/drm/nouveau/nouveau_notifier.c | 37 +- + drivers/gpu/drm/nouveau/nouveau_object.c | 853 +++----- + drivers/gpu/drm/nouveau/nouveau_ramht.c | 289 +++ + drivers/gpu/drm/nouveau/nouveau_ramht.h | 55 + + drivers/gpu/drm/nouveau/nouveau_reg.h | 118 +- + drivers/gpu/drm/nouveau/nouveau_sgdma.c | 118 +- + drivers/gpu/drm/nouveau/nouveau_state.c | 398 ++-- + drivers/gpu/drm/nouveau/nv04_crtc.c | 11 +- + drivers/gpu/drm/nouveau/nv04_dac.c | 61 +- + drivers/gpu/drm/nouveau/nv04_dfp.c | 147 +- + drivers/gpu/drm/nouveau/nv04_display.c | 90 +- + drivers/gpu/drm/nouveau/nv04_fbcon.c | 9 +- + drivers/gpu/drm/nouveau/nv04_fifo.c | 88 +- drivers/gpu/drm/nouveau/nv04_graph.c | 5 +- - drivers/gpu/drm/nouveau/nv04_instmem.c | 21 +- + drivers/gpu/drm/nouveau/nv04_instmem.c | 167 +- drivers/gpu/drm/nouveau/nv04_mc.c | 4 + - drivers/gpu/drm/nouveau/nv04_tv.c | 125 ++---- - drivers/gpu/drm/nouveau/nv10_fifo.c | 10 - - drivers/gpu/drm/nouveau/nv17_tv.c | 46 ++- - drivers/gpu/drm/nouveau/nv20_graph.c | 96 +++-- - drivers/gpu/drm/nouveau/nv40_fifo.c | 8 - - drivers/gpu/drm/nouveau/nv40_graph.c | 58 +-- + drivers/gpu/drm/nouveau/nv04_tv.c | 139 +- + drivers/gpu/drm/nouveau/nv10_fifo.c | 29 +- + drivers/gpu/drm/nouveau/nv10_gpio.c | 92 + + drivers/gpu/drm/nouveau/nv10_graph.c | 177 +- + drivers/gpu/drm/nouveau/nv17_gpio.c | 92 - + drivers/gpu/drm/nouveau/nv17_tv.c | 179 +- + drivers/gpu/drm/nouveau/nv17_tv.h | 15 +- + drivers/gpu/drm/nouveau/nv17_tv_modes.c | 48 +- + drivers/gpu/drm/nouveau/nv20_graph.c | 576 +++--- + drivers/gpu/drm/nouveau/nv30_fb.c | 95 + + drivers/gpu/drm/nouveau/nv40_fifo.c | 28 +- + drivers/gpu/drm/nouveau/nv40_graph.c | 72 +- + drivers/gpu/drm/nouveau/nv40_grctx.c | 6 +- drivers/gpu/drm/nouveau/nv40_mc.c | 2 +- - drivers/gpu/drm/nouveau/nv50_crtc.c | 42 +-- - drivers/gpu/drm/nouveau/nv50_dac.c | 43 ++- - drivers/gpu/drm/nouveau/nv50_display.c | 385 ++++++++++------- - drivers/gpu/drm/nouveau/nv50_fifo.c | 126 ++---- - drivers/gpu/drm/nouveau/nv50_graph.c | 86 ++--- - drivers/gpu/drm/nouveau/nv50_instmem.c | 67 +-- - drivers/gpu/drm/nouveau/nv50_sor.c | 105 +++--- - drivers/gpu/drm/nouveau/nvreg.h | 22 - - 52 files changed, 1748 insertions(+), 1919 deletions(-) + drivers/gpu/drm/nouveau/nv50_crtc.c | 67 +- + drivers/gpu/drm/nouveau/nv50_cursor.c | 2 +- + drivers/gpu/drm/nouveau/nv50_dac.c | 47 +- + drivers/gpu/drm/nouveau/nv50_display.c | 496 +++-- + drivers/gpu/drm/nouveau/nv50_display.h | 6 +- + drivers/gpu/drm/nouveau/nv50_fb.c | 40 + + drivers/gpu/drm/nouveau/nv50_fbcon.c | 4 +- + drivers/gpu/drm/nouveau/nv50_fifo.c | 396 ++-- + drivers/gpu/drm/nouveau/nv50_gpio.c | 35 + + drivers/gpu/drm/nouveau/nv50_graph.c | 131 +- + drivers/gpu/drm/nouveau/nv50_grctx.c | 3305 +++++++++++++++++---------- + drivers/gpu/drm/nouveau/nv50_instmem.c | 471 ++--- + drivers/gpu/drm/nouveau/nv50_sor.c | 109 +- + drivers/gpu/drm/nouveau/nvc0_fb.c | 38 + + drivers/gpu/drm/nouveau/nvc0_fifo.c | 89 + + drivers/gpu/drm/nouveau/nvc0_graph.c | 74 + + drivers/gpu/drm/nouveau/nvc0_instmem.c | 229 ++ + drivers/gpu/drm/nouveau/nvreg.h | 23 +- + 77 files changed, 7584 insertions(+), 5293 deletions(-) + delete mode 100644 drivers/gpu/drm/nouveau/nouveau_grctx.c + create mode 100644 drivers/gpu/drm/nouveau/nouveau_ramht.c + create mode 100644 drivers/gpu/drm/nouveau/nouveau_ramht.h + create mode 100644 drivers/gpu/drm/nouveau/nv10_gpio.c + delete mode 100644 drivers/gpu/drm/nouveau/nv17_gpio.c + create mode 100644 drivers/gpu/drm/nouveau/nv30_fb.c + create mode 100644 drivers/gpu/drm/nouveau/nvc0_fb.c + create mode 100644 drivers/gpu/drm/nouveau/nvc0_fifo.c + create mode 100644 drivers/gpu/drm/nouveau/nvc0_graph.c + create mode 100644 drivers/gpu/drm/nouveau/nvc0_instmem.c diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 9b2a541..1eaa315 100644 @@ -111,7 +1255,7 @@ index 9b2a541..1eaa315 100644 } diff --git a/drivers/gpu/drm/i2c/ch7006_drv.c b/drivers/gpu/drm/i2c/ch7006_drv.c -index 81681a0..833b35f 100644 +index 8c760c7..08792a7 100644 --- a/drivers/gpu/drm/i2c/ch7006_drv.c +++ b/drivers/gpu/drm/i2c/ch7006_drv.c @@ -33,7 +33,7 @@ static void ch7006_encoder_set_config(struct drm_encoder *encoder, @@ -155,7 +1299,7 @@ index 81681a0..833b35f 100644 static int ch7006_encoder_init(struct i2c_client *client, struct drm_device *dev, struct drm_encoder_slave *encoder) -@@ -487,6 +503,8 @@ static struct drm_i2c_encoder_driver ch7006_driver = { +@@ -488,6 +504,8 @@ static struct drm_i2c_encoder_driver ch7006_driver = { .i2c_driver = { .probe = ch7006_probe, .remove = ch7006_remove, @@ -165,7 +1309,7 @@ index 81681a0..833b35f 100644 .driver = { .name = "ch7006", diff --git a/drivers/gpu/drm/i2c/ch7006_priv.h b/drivers/gpu/drm/i2c/ch7006_priv.h -index b06d3d9..1c6d2e3 100644 +index 9487123..17667b7 100644 --- a/drivers/gpu/drm/i2c/ch7006_priv.h +++ b/drivers/gpu/drm/i2c/ch7006_priv.h @@ -77,7 +77,7 @@ struct ch7006_state { @@ -177,21 +1321,51 @@ index b06d3d9..1c6d2e3 100644 struct ch7006_mode *mode; struct ch7006_state state; +diff --git a/drivers/gpu/drm/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig +index 6b8967a..15ca435 100644 +--- a/drivers/gpu/drm/nouveau/Kconfig ++++ b/drivers/gpu/drm/nouveau/Kconfig +@@ -10,6 +10,7 @@ config DRM_NOUVEAU + select FB + select FRAMEBUFFER_CONSOLE if !EMBEDDED + select FB_BACKLIGHT if DRM_NOUVEAU_BACKLIGHT ++ select ACPI_VIDEO if ACPI + help + Choose this option for open-source nVidia support. + diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile -index acd31ed..4a1db73 100644 +index acd31ed..d6cfbf2 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile -@@ -9,7 +9,7 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \ +@@ -9,20 +9,20 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.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 nouveau_grctx.o \ -+ nouveau_dp.o \ ++ nouveau_dp.o nouveau_ramht.o \ nv04_timer.o \ nv04_mc.o nv40_mc.o nv50_mc.o \ - nv04_fb.o nv10_fb.o nv40_fb.o nv50_fb.o \ +- nv04_fb.o nv10_fb.o nv40_fb.o nv50_fb.o \ +- nv04_fifo.o nv10_fifo.o nv40_fifo.o nv50_fifo.o \ ++ nv04_fb.o nv10_fb.o nv30_fb.o nv40_fb.o nv50_fb.o nvc0_fb.o \ ++ nv04_fifo.o nv10_fifo.o nv40_fifo.o nv50_fifo.o nvc0_fifo.o \ + nv04_graph.o nv10_graph.o nv20_graph.o \ +- nv40_graph.o nv50_graph.o \ ++ nv40_graph.o nv50_graph.o nvc0_graph.o \ + nv40_grctx.o nv50_grctx.o \ +- nv04_instmem.o nv50_instmem.o \ ++ nv04_instmem.o nv50_instmem.o nvc0_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 nv50_gpio.o \ ++ nv10_gpio.o nv50_gpio.o \ + nv50_calc.o + + nouveau-$(CONFIG_DRM_NOUVEAU_DEBUG) += nouveau_debugfs.o diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c -index d4bcca8..c17a055 100644 +index d4bcca8..1191526 100644 --- a/drivers/gpu/drm/nouveau/nouveau_acpi.c +++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c @@ -3,6 +3,7 @@ @@ -254,11 +1428,11 @@ index d4bcca8..c17a055 100644 + if (ret < 0) + return ret; + -+ nv_connector->edid = edid; ++ nv_connector->edid = kmemdup(edid, EDID_LENGTH, GFP_KERNEL); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c -index e492919..aae29cc 100644 +index e492919..72905c9 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -28,6 +28,8 @@ @@ -340,7 +1514,7 @@ index e492919..aae29cc 100644 } switch (cond) { -@@ -1218,12 +1220,16 @@ init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) +@@ -1218,14 +1220,18 @@ init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) int ret; auxch = nouveau_i2c_find(dev, bios->display.output->i2c_index); @@ -359,8 +1533,11 @@ index e492919..aae29cc 100644 + return 3; + } - if (cond & 1) +- if (cond & 1) ++ if (!(cond & 1)) iexec->execute = false; + } + break; @@ -1392,7 +1398,7 @@ init_io_restrict_pll2(struct nvbios *bios, uint16_t offset, NV_ERROR(bios->dev, "0x%04X: Config 0x%02X exceeds maximal bound 0x%02X\n", @@ -543,7 +1720,104 @@ index e492919..aae29cc 100644 for (i = 0; i < count; i++) { uint8_t tmdsaddr = bios->data[offset + 3 + i * 2]; -@@ -2039,6 +2069,323 @@ init_zm_index_io(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) +@@ -1898,6 +1928,31 @@ init_condition_time(struct nvbios *bios, uint16_t offset, + } + + static int ++init_ltime(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) ++{ ++ /* ++ * INIT_LTIME opcode: 0x57 ('V') ++ * ++ * offset (8 bit): opcode ++ * offset + 1 (16 bit): time ++ * ++ * Sleep for "time" miliseconds. ++ */ ++ ++ unsigned time = ROM16(bios->data[offset + 1]); ++ ++ if (!iexec->execute) ++ return 3; ++ ++ BIOSLOG(bios, "0x%04X: Sleeping for 0x%04X miliseconds\n", ++ offset, time); ++ ++ msleep(time); ++ ++ return 3; ++} ++ ++static int + init_zm_reg_sequence(struct nvbios *bios, uint16_t offset, + struct init_exec *iexec) + { +@@ -1965,6 +2020,64 @@ init_sub_direct(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) + } + + static int ++init_i2c_if(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) ++{ ++ /* ++ * INIT_I2C_IF opcode: 0x5E ('^') ++ * ++ * offset (8 bit): opcode ++ * offset + 1 (8 bit): DCB I2C table entry index ++ * offset + 2 (8 bit): I2C slave address ++ * offset + 3 (8 bit): I2C register ++ * offset + 4 (8 bit): mask ++ * offset + 5 (8 bit): data ++ * ++ * Read the register given by "I2C register" on the device addressed ++ * by "I2C slave address" on the I2C bus given by "DCB I2C table ++ * entry index". Compare the result AND "mask" to "data". ++ * If they're not equal, skip subsequent opcodes until condition is ++ * inverted (INIT_NOT), or we hit INIT_RESUME ++ */ ++ ++ uint8_t i2c_index = bios->data[offset + 1]; ++ uint8_t i2c_address = bios->data[offset + 2] >> 1; ++ uint8_t reg = bios->data[offset + 3]; ++ uint8_t mask = bios->data[offset + 4]; ++ uint8_t data = bios->data[offset + 5]; ++ struct nouveau_i2c_chan *chan; ++ union i2c_smbus_data val; ++ int ret; ++ ++ /* no execute check by design */ ++ ++ BIOSLOG(bios, "0x%04X: DCBI2CIndex: 0x%02X, I2CAddress: 0x%02X\n", ++ offset, i2c_index, i2c_address); ++ ++ chan = init_i2c_device_find(bios->dev, i2c_index); ++ if (!chan) ++ return -ENODEV; ++ ++ ret = i2c_smbus_xfer(&chan->adapter, i2c_address, 0, ++ I2C_SMBUS_READ, reg, ++ I2C_SMBUS_BYTE_DATA, &val); ++ if (ret < 0) { ++ BIOSLOG(bios, "0x%04X: I2CReg: 0x%02X, Value: [no device], " ++ "Mask: 0x%02X, Data: 0x%02X\n", ++ offset, reg, mask, data); ++ iexec->execute = 0; ++ return 6; ++ } ++ ++ BIOSLOG(bios, "0x%04X: I2CReg: 0x%02X, Value: 0x%02X, " ++ "Mask: 0x%02X, Data: 0x%02X\n", ++ offset, reg, val.byte, mask, data); ++ ++ iexec->execute = ((val.byte & mask) == data); ++ ++ return 6; ++} ++ ++static int + init_copy_nv_reg(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) + { + /* +@@ -2039,6 +2152,325 @@ init_zm_index_io(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) return 5; } @@ -561,9 +1835,10 @@ index e492919..aae29cc 100644 + uint32_t val = 0; + + if (off < pci_resource_len(dev->pdev, 1)) { -+ uint32_t __iomem *p = io_mapping_map_atomic_wc(fb, off); ++ uint8_t __iomem *p = ++ io_mapping_map_atomic_wc(fb, off & PAGE_MASK); + -+ val = ioread32(p); ++ val = ioread32(p + (off & ~PAGE_MASK)); + + io_mapping_unmap_atomic(p); + } @@ -576,9 +1851,10 @@ index e492919..aae29cc 100644 + uint32_t off, uint32_t val) +{ + if (off < pci_resource_len(dev->pdev, 1)) { -+ uint32_t __iomem *p = io_mapping_map_atomic_wc(fb, off); ++ uint8_t __iomem *p = ++ io_mapping_map_atomic_wc(fb, off & PAGE_MASK); + -+ iowrite32(val, p); ++ iowrite32(val, p + (off & ~PAGE_MASK)); + wmb(); + + io_mapping_unmap_atomic(p); @@ -643,7 +1919,7 @@ index e492919..aae29cc 100644 + NV04_PFB_BOOT_0_RAM_AMOUNT, + NV04_PFB_BOOT_0_RAM_AMOUNT_4MB); + -+ } else if (peek_fb(dev, fb, 0) == patt) { ++ } else if (peek_fb(dev, fb, 0) != patt) { + if (read_back_fb(dev, fb, 0x800000, patt)) + bios_md32(bios, NV04_PFB_BOOT_0, + NV04_PFB_BOOT_0_RAM_AMOUNT, @@ -867,7 +2143,7 @@ index e492919..aae29cc 100644 static int init_compute_mem(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) { -@@ -2047,64 +2394,57 @@ init_compute_mem(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) +@@ -2047,64 +2479,57 @@ init_compute_mem(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) * * offset (8 bit): opcode * @@ -970,7 +2246,7 @@ index e492919..aae29cc 100644 return 1; } -@@ -2131,7 +2471,8 @@ init_reset(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) +@@ -2131,7 +2556,8 @@ init_reset(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) /* no iexec->execute check by design */ pci_nv_19 = bios_rd32(bios, NV_PBUS_PCI_NV_19); @@ -980,7 +2256,7 @@ index e492919..aae29cc 100644 bios_wr32(bios, reg, value1); udelay(10); -@@ -2167,7 +2508,7 @@ init_configure_mem(struct nvbios *bios, uint16_t offset, +@@ -2167,7 +2593,7 @@ init_configure_mem(struct nvbios *bios, uint16_t offset, uint32_t reg, data; if (bios->major_version > 2) @@ -989,7 +2265,7 @@ index e492919..aae29cc 100644 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); -@@ -2180,14 +2521,14 @@ init_configure_mem(struct nvbios *bios, uint16_t offset, +@@ -2180,14 +2606,14 @@ init_configure_mem(struct nvbios *bios, uint16_t offset, reg = ROM32(bios->data[seqtbloffs += 4])) { switch (reg) { @@ -1010,7 +2286,7 @@ index e492919..aae29cc 100644 break; default: data = ROM32(bios->data[meminitdata]); -@@ -2222,7 +2563,7 @@ init_configure_clk(struct nvbios *bios, uint16_t offset, +@@ -2222,7 +2648,7 @@ init_configure_clk(struct nvbios *bios, uint16_t offset, int clock; if (bios->major_version > 2) @@ -1019,8 +2295,12 @@ index e492919..aae29cc 100644 clock = ROM16(bios->data[meminitoffs + 4]) * 10; setPLL(bios, NV_PRAMDAC_NVPLL_COEFF, clock); -@@ -2255,7 +2596,7 @@ init_configure_preinit(struct nvbios *bios, uint16_t offset, - uint8_t cr3c = ((straps << 2) & 0xf0) | (straps & (1 << 6)); +@@ -2252,10 +2678,10 @@ init_configure_preinit(struct nvbios *bios, uint16_t offset, + /* 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)); ++ uint8_t cr3c = ((straps << 2) & 0xf0) | (straps & 0x40) >> 6; if (bios->major_version > 2) - return -ENODEV; @@ -1028,7 +2308,7 @@ index e492919..aae29cc 100644 bios_idxprt_wr(bios, NV_CIO_CRX__COLOR, NV_CIO_CRE_SCRATCH4__INDEX, cr3c); -@@ -2389,7 +2730,7 @@ init_ram_condition(struct nvbios *bios, uint16_t offset, +@@ -2389,7 +2815,7 @@ init_ram_condition(struct nvbios *bios, uint16_t offset, * offset + 1 (8 bit): mask * offset + 2 (8 bit): cmpval * @@ -1037,7 +2317,7 @@ index e492919..aae29cc 100644 * If condition not met skip subsequent opcodes until condition is * inverted (INIT_NOT), or we hit INIT_RESUME */ -@@ -2401,7 +2742,7 @@ init_ram_condition(struct nvbios *bios, uint16_t offset, +@@ -2401,7 +2827,7 @@ init_ram_condition(struct nvbios *bios, uint16_t offset, if (!iexec->execute) return 3; @@ -1046,16 +2326,32 @@ index e492919..aae29cc 100644 BIOSLOG(bios, "0x%04X: Checking if 0x%08X equals 0x%08X\n", offset, data, cmpval); -@@ -2800,7 +3141,7 @@ init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) +@@ -2795,12 +3221,13 @@ init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) + */ - if (dev_priv->card_type != NV_50) { + struct drm_nouveau_private *dev_priv = bios->dev->dev_private; ++ struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; + const uint32_t nv50_gpio_ctl[2] = { 0xe100, 0xe28c }; + int i; + +- if (dev_priv->card_type != NV_50) { ++ if (dev_priv->card_type < NV_50) { NV_ERROR(bios->dev, "INIT_GPIO on unsupported chipset\n"); - return -ENODEV; + return 1; } if (!iexec->execute) -@@ -2872,10 +3213,7 @@ init_ram_restrict_zm_reg_group(struct nvbios *bios, uint16_t offset, +@@ -2815,7 +3242,7 @@ init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) + BIOSLOG(bios, "0x%04X: set gpio 0x%02x, state %d\n", + offset, gpio->tag, gpio->state_default); + if (bios->execute) +- nv50_gpio_set(bios->dev, gpio->tag, gpio->state_default); ++ pgpio->set(bios->dev, gpio->tag, gpio->state_default); + + /* The NVIDIA binary driver doesn't appear to actually do + * any of this, my VBIOS does however. +@@ -2872,10 +3299,7 @@ init_ram_restrict_zm_reg_group(struct nvbios *bios, uint16_t offset, uint8_t index; int i; @@ -1067,7 +2363,7 @@ index e492919..aae29cc 100644 if (!blocklen) { NV_ERROR(bios->dev, "0x%04X: Zero block length - has the M table " -@@ -2883,6 +3221,9 @@ init_ram_restrict_zm_reg_group(struct nvbios *bios, uint16_t offset, +@@ -2883,6 +3307,9 @@ init_ram_restrict_zm_reg_group(struct nvbios *bios, uint16_t offset, return -EINVAL; } @@ -1077,7 +2373,7 @@ index e492919..aae29cc 100644 strap_ramcfg = (bios_rd32(bios, NV_PEXTDEV_BOOT_0) >> 2) & 0xf; index = bios->data[bios->ram_restrict_tbl_ptr + strap_ramcfg]; -@@ -3064,14 +3405,14 @@ init_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) +@@ -3064,14 +3491,14 @@ init_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) if (!bios->display.output) { NV_ERROR(dev, "INIT_AUXCH: no active output\n"); @@ -1094,7 +2390,7 @@ index e492919..aae29cc 100644 } if (!iexec->execute) -@@ -3084,7 +3425,7 @@ init_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) +@@ -3084,7 +3511,7 @@ init_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) ret = nouveau_dp_auxch(auxch, 9, addr, &data, 1); if (ret) { NV_ERROR(dev, "INIT_AUXCH: rd auxch fail %d\n", ret); @@ -1103,7 +2399,7 @@ index e492919..aae29cc 100644 } data &= bios->data[offset + 0]; -@@ -3093,7 +3434,7 @@ init_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) +@@ -3093,7 +3520,7 @@ init_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) ret = nouveau_dp_auxch(auxch, 8, addr, &data, 1); if (ret) { NV_ERROR(dev, "INIT_AUXCH: wr auxch fail %d\n", ret); @@ -1112,7 +2408,7 @@ index e492919..aae29cc 100644 } } -@@ -3123,14 +3464,14 @@ init_zm_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) +@@ -3123,14 +3550,14 @@ init_zm_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) if (!bios->display.output) { NV_ERROR(dev, "INIT_ZM_AUXCH: no active output\n"); @@ -1129,7 +2425,7 @@ index e492919..aae29cc 100644 } if (!iexec->execute) -@@ -3141,7 +3482,7 @@ init_zm_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) +@@ -3141,13 +3568,76 @@ init_zm_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) 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); @@ -1138,7 +2434,234 @@ index e492919..aae29cc 100644 } } -@@ -5151,10 +5492,14 @@ static int parse_bmp_structure(struct drm_device *dev, struct nvbios *bios, unsi + return len; + } + ++static int ++init_i2c_long_if(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) ++{ ++ /* ++ * INIT_I2C_LONG_IF opcode: 0x9A ('') ++ * ++ * offset (8 bit): opcode ++ * offset + 1 (8 bit): DCB I2C table entry index ++ * offset + 2 (8 bit): I2C slave address ++ * offset + 3 (16 bit): I2C register ++ * offset + 5 (8 bit): mask ++ * offset + 6 (8 bit): data ++ * ++ * Read the register given by "I2C register" on the device addressed ++ * by "I2C slave address" on the I2C bus given by "DCB I2C table ++ * entry index". Compare the result AND "mask" to "data". ++ * If they're not equal, skip subsequent opcodes until condition is ++ * inverted (INIT_NOT), or we hit INIT_RESUME ++ */ ++ ++ uint8_t i2c_index = bios->data[offset + 1]; ++ uint8_t i2c_address = bios->data[offset + 2] >> 1; ++ uint8_t reglo = bios->data[offset + 3]; ++ uint8_t reghi = bios->data[offset + 4]; ++ uint8_t mask = bios->data[offset + 5]; ++ uint8_t data = bios->data[offset + 6]; ++ struct nouveau_i2c_chan *chan; ++ uint8_t buf0[2] = { reghi, reglo }; ++ uint8_t buf1[1]; ++ struct i2c_msg msg[2] = { ++ { i2c_address, 0, 1, buf0 }, ++ { i2c_address, I2C_M_RD, 1, buf1 }, ++ }; ++ int ret; ++ ++ /* no execute check by design */ ++ ++ BIOSLOG(bios, "0x%04X: DCBI2CIndex: 0x%02X, I2CAddress: 0x%02X\n", ++ offset, i2c_index, i2c_address); ++ ++ chan = init_i2c_device_find(bios->dev, i2c_index); ++ if (!chan) ++ return -ENODEV; ++ ++ ++ ret = i2c_transfer(&chan->adapter, msg, 2); ++ if (ret < 0) { ++ BIOSLOG(bios, "0x%04X: I2CReg: 0x%02X:0x%02X, Value: [no device], " ++ "Mask: 0x%02X, Data: 0x%02X\n", ++ offset, reghi, reglo, mask, data); ++ iexec->execute = 0; ++ return 7; ++ } ++ ++ BIOSLOG(bios, "0x%04X: I2CReg: 0x%02X:0x%02X, Value: 0x%02X, " ++ "Mask: 0x%02X, Data: 0x%02X\n", ++ offset, reghi, reglo, buf1[0], mask, data); ++ ++ iexec->execute = ((buf1[0] & mask) == data); ++ ++ return 7; ++} ++ + 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 */ +@@ -3174,9 +3664,11 @@ static struct init_tbl_entry itbl_entry[] = { + { "INIT_ZM_CR" , 0x53, init_zm_cr }, + { "INIT_ZM_CR_GROUP" , 0x54, init_zm_cr_group }, + { "INIT_CONDITION_TIME" , 0x56, init_condition_time }, ++ { "INIT_LTIME" , 0x57, init_ltime }, + { "INIT_ZM_REG_SEQUENCE" , 0x58, init_zm_reg_sequence }, + /* INIT_INDIRECT_REG (0x5A, 7, 0, 0) removed due to no example of use */ + { "INIT_SUB_DIRECT" , 0x5B, init_sub_direct }, ++ { "INIT_I2C_IF" , 0x5E, init_i2c_if }, + { "INIT_COPY_NV_REG" , 0x5F, init_copy_nv_reg }, + { "INIT_ZM_INDEX_IO" , 0x62, init_zm_index_io }, + { "INIT_COMPUTE_MEM" , 0x63, init_compute_mem }, +@@ -3210,6 +3702,7 @@ static struct init_tbl_entry itbl_entry[] = { + { "INIT_97" , 0x97, init_97 }, + { "INIT_AUXCH" , 0x98, init_auxch }, + { "INIT_ZM_AUXCH" , 0x99, init_zm_auxch }, ++ { "INIT_I2C_LONG_IF" , 0x9A, init_i2c_long_if }, + { NULL , 0 , NULL } + }; + +@@ -3376,27 +3869,10 @@ static int call_lvds_manufacturer_script(struct drm_device *dev, struct dcb_entr + } + #ifdef __powerpc__ + /* Powerbook specific quirks */ +- if ((dev->pci_device & 0xffff) == 0x0179 || +- (dev->pci_device & 0xffff) == 0x0189 || +- (dev->pci_device & 0xffff) == 0x0329) { +- if (script == LVDS_RESET) { +- nv_write_tmds(dev, dcbent->or, 0, 0x02, 0x72); +- +- } else 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); +- +- } else 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); +- } +- } ++ if (script == LVDS_RESET && ++ (dev->pci_device == 0x0179 || dev->pci_device == 0x0189 || ++ dev->pci_device == 0x0329)) ++ nv_write_tmds(dev, dcbent->or, 0, 0x02, 0x72); + #endif + + return 0; +@@ -3888,11 +4364,8 @@ int nouveau_bios_parse_lvds_table(struct drm_device *dev, int pxclk, bool *dl, b + * + * For the moment, a quirk will do :) + */ +- if ((dev->pdev->device == 0x01d7) && +- (dev->pdev->subsystem_vendor == 0x1028) && +- (dev->pdev->subsystem_device == 0x01c2)) { ++ if (nv_match_device(dev, 0x01d7, 0x1028, 0x01c2)) + bios->fp.duallink_transition_clk = 80000; +- } + + /* set dual_link flag for EDID case */ + if (pxclk && (chip_version < 0x25 || chip_version > 0x28)) +@@ -4068,7 +4541,7 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent, + bios->display.script_table_ptr, + table[2], table[3], table[0] >= 0x21); + if (!otable) { +- NV_ERROR(dev, "Couldn't find matching output script table\n"); ++ NV_DEBUG_KMS(dev, "failed to match any output table\n"); + return 1; + } + +@@ -4094,7 +4567,7 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent, + return 1; + } + +- NV_TRACE(dev, "0x%04X: parsing output script 0\n", script); ++ NV_DEBUG_KMS(dev, "0x%04X: parsing output script 0\n", script); + nouveau_bios_run_init_table(dev, script, dcbent); + } else + if (pxclk == -1) { +@@ -4104,7 +4577,7 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent, + return 1; + } + +- NV_TRACE(dev, "0x%04X: parsing output script 1\n", script); ++ NV_DEBUG_KMS(dev, "0x%04X: parsing output script 1\n", script); + nouveau_bios_run_init_table(dev, script, dcbent); + } else + if (pxclk == -2) { +@@ -4117,7 +4590,7 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent, + return 1; + } + +- NV_TRACE(dev, "0x%04X: parsing output script 2\n", script); ++ NV_DEBUG_KMS(dev, "0x%04X: parsing output script 2\n", script); + nouveau_bios_run_init_table(dev, script, dcbent); + } else + if (pxclk > 0) { +@@ -4125,11 +4598,11 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent, + if (script) + script = clkcmptable(bios, script, pxclk); + if (!script) { +- NV_ERROR(dev, "clock script 0 not found\n"); ++ NV_DEBUG_KMS(dev, "clock script 0 not found\n"); + return 1; + } + +- NV_TRACE(dev, "0x%04X: parsing clock script 0\n", script); ++ NV_DEBUG_KMS(dev, "0x%04X: parsing clock script 0\n", script); + nouveau_bios_run_init_table(dev, script, dcbent); + } else + if (pxclk < 0) { +@@ -4141,7 +4614,7 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent, + return 1; + } + +- NV_TRACE(dev, "0x%04X: parsing clock script 1\n", script); ++ NV_DEBUG_KMS(dev, "0x%04X: parsing clock script 1\n", script); + nouveau_bios_run_init_table(dev, script, dcbent); + } + +@@ -4484,7 +4957,7 @@ int get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims + pll_lim->min_p = record[12]; + pll_lim->max_p = record[13]; + /* where did this go to?? */ +- if (limit_match == 0x00614100 || limit_match == 0x00614900) ++ if ((entry[0] & 0xf0) == 0x80) + pll_lim->refclk = 27000; + else + pll_lim->refclk = 100000; +@@ -4864,19 +5337,17 @@ static int parse_bit_tmds_tbl_entry(struct drm_device *dev, struct nvbios *bios, + } + + tmdstableptr = ROM16(bios->data[bitentry->offset]); +- +- if (tmdstableptr == 0x0) { ++ if (!tmdstableptr) { + NV_ERROR(dev, "Pointer to TMDS table invalid\n"); + return -EINVAL; + } + ++ NV_INFO(dev, "TMDS table version %d.%d\n", ++ bios->data[tmdstableptr] >> 4, bios->data[tmdstableptr] & 0xf); ++ + /* 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); ++ if (bios->data[tmdstableptr] != 0x11) + return -ENOSYS; +- } + + /* + * These two scripts are odd: they don't seem to get run even when +@@ -5151,10 +5622,14 @@ static int parse_bmp_structure(struct drm_device *dev, struct nvbios *bios, unsi 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]; @@ -1157,7 +2680,48 @@ index e492919..aae29cc 100644 if (bmplength > 74) { bios->fmaxvco = ROM32(bmp[67]); -@@ -5589,9 +5934,12 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb, +@@ -5312,6 +5787,20 @@ parse_dcb_gpio_table(struct nvbios *bios) + gpio->line = tvdac_gpio[1] >> 4; + gpio->invert = tvdac_gpio[0] & 2; + } ++ } else { ++ /* ++ * No systematic way to store GPIO info on pre-v2.2 ++ * DCBs, try to match the PCI device IDs. ++ */ ++ ++ /* Apple iMac G4 NV18 */ ++ if (nv_match_device(dev, 0x0189, 0x10de, 0x0010)) { ++ struct dcb_gpio_entry *gpio = new_gpio_entry(bios); ++ ++ gpio->tag = DCB_GPIO_TVDAC0; ++ gpio->line = 4; ++ } ++ + } + + if (!gpio_table_ptr) +@@ -5387,9 +5876,7 @@ apply_dcb_connector_quirks(struct nvbios *bios, int idx) + struct drm_device *dev = bios->dev; + + /* Gigabyte NX85T */ +- if ((dev->pdev->device == 0x0421) && +- (dev->pdev->subsystem_vendor == 0x1458) && +- (dev->pdev->subsystem_device == 0x344c)) { ++ if (nv_match_device(dev, 0x0421, 0x1458, 0x344c)) { + if (cte->type == DCB_CONNECTOR_HDMI_1) + cte->type = DCB_CONNECTOR_DVI_I; + } +@@ -5506,7 +5993,7 @@ static void fabricate_vga_output(struct dcb_table *dcb, int i2c, int heads) + entry->i2c_index = i2c; + entry->heads = heads; + entry->location = DCB_LOC_ON_CHIP; +- /* "or" mostly unused in early gen crt modesetting, 0 is fine */ ++ entry->or = 1; + } + + static void fabricate_dvi_i_output(struct dcb_table *dcb, bool twoHeads) +@@ -5589,9 +6076,12 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb, if (conf & 0x4 || conf & 0x8) entry->lvdsconf.use_power_scripts = true; } else { @@ -1171,7 +2735,54 @@ index e492919..aae29cc 100644 } if (conf & mask) { /* -@@ -5706,13 +6054,6 @@ parse_dcb15_entry(struct drm_device *dev, struct dcb_table *dcb, +@@ -5631,9 +6121,15 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb, + } + break; + case OUTPUT_TMDS: +- entry->tmdsconf.sor.link = (conf & 0x00000030) >> 4; ++ if (dcb->version >= 0x40) ++ entry->tmdsconf.sor.link = (conf & 0x00000030) >> 4; ++ else if (dcb->version >= 0x30) ++ entry->tmdsconf.slave_addr = (conf & 0x00000700) >> 8; ++ else if (dcb->version >= 0x22) ++ entry->tmdsconf.slave_addr = (conf & 0x00000070) >> 4; ++ + break; +- case 0xe: ++ case OUTPUT_EOL: + /* weird g80 mobile type that "nv" treats as a terminator */ + dcb->entries--; + return false; +@@ -5670,22 +6166,14 @@ parse_dcb15_entry(struct drm_device *dev, struct dcb_table *dcb, + entry->type = OUTPUT_TV; + break; + case 2: +- case 3: +- entry->type = OUTPUT_LVDS; +- break; + case 4: +- switch ((conn & 0x000000f0) >> 4) { +- case 0: +- entry->type = OUTPUT_TMDS; +- break; +- case 1: ++ if (conn & 0x10) + entry->type = OUTPUT_LVDS; +- break; +- default: +- NV_ERROR(dev, "Unknown DCB subtype 4/%d\n", +- (conn & 0x000000f0) >> 4); +- return false; +- } ++ else ++ entry->type = OUTPUT_TMDS; ++ break; ++ case 3: ++ entry->type = OUTPUT_LVDS; + break; + default: + NV_ERROR(dev, "Unknown DCB type %d\n", conn & 0x0000000f); +@@ -5706,13 +6194,6 @@ parse_dcb15_entry(struct drm_device *dev, struct dcb_table *dcb, case OUTPUT_TV: entry->tvconf.has_component_output = false; break; @@ -1185,7 +2796,7 @@ index e492919..aae29cc 100644 case OUTPUT_LVDS: if ((conn & 0x00003f00) != 0x10) entry->lvdsconf.use_straps_for_mode = true; -@@ -5793,6 +6134,31 @@ void merge_like_dcb_entries(struct drm_device *dev, struct dcb_table *dcb) +@@ -5793,6 +6274,29 @@ void merge_like_dcb_entries(struct drm_device *dev, struct dcb_table *dcb) dcb->entries = newentries; } @@ -1204,9 +2815,7 @@ index e492919..aae29cc 100644 + * nasty problems until this is sorted (assuming it's not a + * VBIOS bug). + */ -+ if ((dev->pdev->device == 0x040d) && -+ (dev->pdev->subsystem_vendor == 0x1028) && -+ (dev->pdev->subsystem_device == 0x019b)) { ++ if (nv_match_device(dev, 0x040d, 0x1028, 0x019b)) { + if (*conn == 0x02026312 && *conf == 0x00000020) + return false; + } @@ -1217,7 +2826,27 @@ index e492919..aae29cc 100644 static int parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads) { -@@ -5926,6 +6292,9 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads) +@@ -5903,6 +6407,19 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads) + dcb->i2c_table = &bios->data[i2ctabptr]; + if (dcb->version >= 0x30) + dcb->i2c_default_indices = dcb->i2c_table[4]; ++ ++ /* ++ * Parse the "management" I2C bus, used for hardware ++ * monitoring and some external TMDS transmitters. ++ */ ++ if (dcb->version >= 0x22) { ++ int idx = (dcb->version >= 0x40 ? ++ dcb->i2c_default_indices & 0xf : ++ 2); ++ ++ read_dcb_i2c_entry(dev, dcb->version, dcb->i2c_table, ++ idx, &dcb->i2c[idx]); ++ } + } + + if (entries > DCB_MAX_NUM_ENTRIES) +@@ -5926,6 +6443,9 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads) if ((connection & 0x0000000f) == 0x0000000f) continue; @@ -1227,20 +2856,28 @@ index e492919..aae29cc 100644 NV_TRACEWARN(dev, "Raw DCB entry %d: %08x %08x\n", dcb->entries, connection, config); -@@ -6182,8 +6551,10 @@ nouveau_run_vbios_init(struct drm_device *dev) +@@ -6181,9 +6701,8 @@ nouveau_run_vbios_init(struct drm_device *dev) + struct nvbios *bios = &dev_priv->vbios; int i, ret = 0; - NVLockVgaCrtcs(dev, false); +- NVLockVgaCrtcs(dev, false); - if (nv_two_heads(dev)) - NVSetOwner(dev, bios->state.crtchead); -+ if (nv_two_heads(dev)) { -+ bios->state.crtchead = 0; -+ NVSetOwner(dev, 0); -+ } ++ /* Reset the BIOS head to 0. */ ++ bios->state.crtchead = 0; if (bios->major_version < 5) /* BMP only */ load_nv17_hw_sequencer_ucode(dev, bios); -@@ -6238,7 +6609,6 @@ static bool +@@ -6216,8 +6735,6 @@ nouveau_run_vbios_init(struct drm_device *dev) + } + } + +- NVLockVgaCrtcs(dev, true); +- + return ret; + } + +@@ -6238,7 +6755,6 @@ static bool nouveau_bios_posted(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; @@ -1248,24 +2885,22 @@ index e492919..aae29cc 100644 unsigned htotal; if (dev_priv->chipset >= NV_50) { -@@ -6248,13 +6618,14 @@ nouveau_bios_posted(struct drm_device *dev) +@@ -6248,13 +6764,12 @@ nouveau_bios_posted(struct drm_device *dev) return true; } - was_locked = NVLockVgaCrtcs(dev, false); -+ NVLockVgaCrtcs(dev, false); htotal = NVReadVgaCrtc(dev, 0, 0x06); htotal |= (NVReadVgaCrtc(dev, 0, 0x07) & 0x01) << 8; htotal |= (NVReadVgaCrtc(dev, 0, 0x07) & 0x20) << 4; htotal |= (NVReadVgaCrtc(dev, 0, 0x25) & 0x01) << 10; htotal |= (NVReadVgaCrtc(dev, 0, 0x41) & 0x01) << 11; - NVLockVgaCrtcs(dev, was_locked); -+ NVLockVgaCrtcs(dev, true); + return (htotal != 0); } -@@ -6263,8 +6634,6 @@ nouveau_bios_init(struct drm_device *dev) +@@ -6263,8 +6778,6 @@ nouveau_bios_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nvbios *bios = &dev_priv->vbios; @@ -1274,7 +2909,7 @@ index e492919..aae29cc 100644 int ret; if (!NVInitVBIOS(dev)) -@@ -6284,40 +6653,29 @@ nouveau_bios_init(struct drm_device *dev) +@@ -6284,40 +6797,29 @@ nouveau_bios_init(struct drm_device *dev) if (!bios->major_version) /* we don't run version 0 bios */ return 0; @@ -1298,16 +2933,17 @@ index e492919..aae29cc 100644 + "running VBIOS init tables.\n"); bios->execute = true; } - -- bios_wr32(bios, NV_PEXTDEV_BOOT_0, saved_nv_pextdev_boot_0); - +- bios_wr32(bios, NV_PEXTDEV_BOOT_0, saved_nv_pextdev_boot_0); ++ if (nouveau_force_post) ++ bios->execute = true; + ret = nouveau_run_vbios_init(dev); if (ret) return ret; /* feature_byte on BMP is poor, but init always sets CR4B */ - was_locked = NVLockVgaCrtcs(dev, false); -+ NVLockVgaCrtcs(dev, false); if (bios->major_version < 5) bios->is_mobile = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_4B) & 0x40; @@ -1315,12 +2951,11 @@ index e492919..aae29cc 100644 if (bios->is_mobile || bios->major_version >= 5) ret = parse_fp_mode_table(dev, bios); - NVLockVgaCrtcs(dev, was_locked); -+ NVLockVgaCrtcs(dev, true); /* allow subsequent scripts to execute */ bios->execute = true; diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h -index adf4ec2..024458a 100644 +index adf4ec2..c1de2f3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.h +++ b/drivers/gpu/drm/nouveau/nouveau_bios.h @@ -81,6 +81,7 @@ struct dcb_connector_table_entry { @@ -1331,7 +2966,15 @@ index adf4ec2..024458a 100644 }; struct dcb_connector_table { -@@ -117,6 +118,7 @@ struct dcb_entry { +@@ -94,6 +95,7 @@ enum dcb_type { + OUTPUT_TMDS = 2, + OUTPUT_LVDS = 3, + OUTPUT_DP = 6, ++ OUTPUT_EOL = 14, /* DCB 4.0+, appears to be end-of-list */ + OUTPUT_ANY = -1 + }; + +@@ -117,6 +119,7 @@ struct dcb_entry { struct { struct sor_conf sor; bool use_straps_for_mode; @@ -1339,7 +2982,15 @@ index adf4ec2..024458a 100644 bool use_power_scripts; } lvdsconf; struct { -@@ -249,8 +251,6 @@ struct nvbios { +@@ -129,6 +132,7 @@ struct dcb_entry { + } dpconf; + struct { + struct sor_conf sor; ++ int slave_addr; + } tmdsconf; + }; + bool i2c_upper_default; +@@ -249,8 +253,6 @@ struct nvbios { struct { int crtchead; @@ -1349,10 +3000,69 @@ index adf4ec2..024458a 100644 struct { diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c -index 6f3c195..d8c341d 100644 +index 51746d9..a4011f5 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c -@@ -461,9 +461,9 @@ nouveau_bo_move_accel_cleanup(struct nouveau_channel *chan, +@@ -58,17 +58,12 @@ nouveau_bo_del_ttm(struct ttm_buffer_object *bo) + struct drm_device *dev = dev_priv->dev; + 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); + + if (nvbo->tile) + nv10_mem_expire_tiling(dev, nvbo->tile, NULL); + +- spin_lock(&dev_priv->ttm.bo_list_lock); +- list_del(&nvbo->head); +- spin_unlock(&dev_priv->ttm.bo_list_lock); + kfree(nvbo); + } + +@@ -167,8 +162,6 @@ nouveau_bo_new(struct drm_device *dev, struct nouveau_channel *chan, + nouveau_bo_fixup_align(dev, tile_mode, tile_flags, &align, &size); + align >>= PAGE_SHIFT; + +- nvbo->placement.fpfn = 0; +- nvbo->placement.lpfn = mappable ? dev_priv->fb_mappable_pages : 0; + nouveau_bo_placement_set(nvbo, flags, 0); + + nvbo->channel = chan; +@@ -181,9 +174,6 @@ nouveau_bo_new(struct drm_device *dev, struct nouveau_channel *chan, + } + nvbo->channel = NULL; + +- 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; + } +@@ -311,7 +301,8 @@ nouveau_bo_map(struct nouveau_bo *nvbo) + void + nouveau_bo_unmap(struct nouveau_bo *nvbo) + { +- ttm_bo_kunmap(&nvbo->kmap); ++ if (nvbo) ++ ttm_bo_kunmap(&nvbo->kmap); + } + + u16 +@@ -410,7 +401,10 @@ nouveau_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, + man->available_caching = TTM_PL_FLAG_UNCACHED | + TTM_PL_FLAG_WC; + man->default_caching = TTM_PL_FLAG_WC; +- man->gpu_offset = dev_priv->vm_vram_base; ++ if (dev_priv->card_type == NV_50) ++ man->gpu_offset = 0x40000000; ++ else ++ man->gpu_offset = 0; + break; + case TTM_PL_TT: + switch (dev_priv->gart_info.type) { +@@ -476,18 +470,20 @@ nouveau_bo_move_accel_cleanup(struct nouveau_channel *chan, return ret; ret = ttm_bo_move_accel_cleanup(&nvbo->bo, fence, NULL, @@ -1365,18 +3075,304 @@ index 6f3c195..d8c341d 100644 nouveau_fence_unref((void *)&fence); return ret; } -@@ -711,8 +711,7 @@ nouveau_bo_move(struct ttm_buffer_object *bo, bool evict, bool intr, + + static inline uint32_t +-nouveau_bo_mem_ctxdma(struct nouveau_bo *nvbo, struct nouveau_channel *chan, +- struct ttm_mem_reg *mem) ++nouveau_bo_mem_ctxdma(struct ttm_buffer_object *bo, ++ struct nouveau_channel *chan, struct ttm_mem_reg *mem) + { +- if (chan == nouveau_bdev(nvbo->bo.bdev)->channel) { ++ struct nouveau_bo *nvbo = nouveau_bo(bo); ++ ++ if (nvbo->no_vm) { + if (mem->mem_type == TTM_PL_TT) + return NvDmaGART; + return NvDmaVRAM; +@@ -499,86 +495,181 @@ nouveau_bo_mem_ctxdma(struct nouveau_bo *nvbo, struct nouveau_channel *chan, + } + + static int +-nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr, +- bool no_wait_reserve, bool no_wait_gpu, +- struct ttm_mem_reg *new_mem) ++nv50_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, ++ 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 ttm_mem_reg *old_mem = &bo->mem; +- struct nouveau_channel *chan; +- uint64_t src_offset, dst_offset; +- uint32_t page_count; ++ struct nouveau_bo *nvbo = nouveau_bo(bo); ++ u64 length = (new_mem->num_pages << PAGE_SHIFT); ++ u64 src_offset, dst_offset; + int ret; + +- chan = nvbo->channel; +- if (!chan || nvbo->tile_flags || nvbo->no_vm) +- chan = dev_priv->channel; +- + 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 ++ if (!nvbo->no_vm) { ++ if (old_mem->mem_type == TTM_PL_VRAM) + src_offset += dev_priv->vm_vram_base; +- +- if (new_mem->mem_type == TTM_PL_TT) +- dst_offset += dev_priv->vm_gart_base; + else ++ src_offset += dev_priv->vm_gart_base; ++ ++ if (new_mem->mem_type == TTM_PL_VRAM) + dst_offset += dev_priv->vm_vram_base; ++ else ++ dst_offset += dev_priv->vm_gart_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); ++ BEGIN_RING(chan, NvSubM2MF, 0x0184, 2); ++ OUT_RING (chan, nouveau_bo_mem_ctxdma(bo, chan, old_mem)); ++ OUT_RING (chan, nouveau_bo_mem_ctxdma(bo, chan, new_mem)); ++ ++ while (length) { ++ u32 amount, stride, height; ++ ++ amount = min(length, (u64)(4 * 1024 * 1024)); ++ stride = 16 * 4; ++ height = amount / stride; ++ ++ if (new_mem->mem_type == TTM_PL_VRAM && nvbo->tile_flags) { ++ ret = RING_SPACE(chan, 8); ++ if (ret) ++ return ret; ++ ++ BEGIN_RING(chan, NvSubM2MF, 0x0200, 7); ++ OUT_RING (chan, 0); ++ OUT_RING (chan, 0); ++ OUT_RING (chan, stride); ++ OUT_RING (chan, height); ++ OUT_RING (chan, 1); ++ OUT_RING (chan, 0); ++ OUT_RING (chan, 0); ++ } else { ++ ret = RING_SPACE(chan, 2); ++ if (ret) ++ return ret; ++ ++ BEGIN_RING(chan, NvSubM2MF, 0x0200, 1); ++ OUT_RING (chan, 1); ++ } ++ if (old_mem->mem_type == TTM_PL_VRAM && nvbo->tile_flags) { ++ ret = RING_SPACE(chan, 8); ++ if (ret) ++ return ret; ++ ++ BEGIN_RING(chan, NvSubM2MF, 0x021c, 7); ++ OUT_RING (chan, 0); ++ OUT_RING (chan, 0); ++ OUT_RING (chan, stride); ++ OUT_RING (chan, height); ++ OUT_RING (chan, 1); ++ OUT_RING (chan, 0); ++ OUT_RING (chan, 0); ++ } else { ++ ret = RING_SPACE(chan, 2); ++ if (ret) ++ return ret; ++ ++ BEGIN_RING(chan, NvSubM2MF, 0x021c, 1); ++ OUT_RING (chan, 1); ++ } ++ ++ ret = RING_SPACE(chan, 14); + if (ret) + return ret; +- BEGIN_RING(chan, NvSubM2MF, 0x0200, 1); +- OUT_RING(chan, 1); +- BEGIN_RING(chan, NvSubM2MF, 0x021c, 1); +- OUT_RING(chan, 1); ++ ++ BEGIN_RING(chan, NvSubM2MF, 0x0238, 2); ++ OUT_RING (chan, upper_32_bits(src_offset)); ++ OUT_RING (chan, upper_32_bits(dst_offset)); ++ BEGIN_RING(chan, NvSubM2MF, 0x030c, 8); ++ OUT_RING (chan, lower_32_bits(src_offset)); ++ OUT_RING (chan, lower_32_bits(dst_offset)); ++ OUT_RING (chan, stride); ++ OUT_RING (chan, stride); ++ OUT_RING (chan, stride); ++ OUT_RING (chan, height); ++ OUT_RING (chan, 0x00000101); ++ OUT_RING (chan, 0x00000000); ++ BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_NOP, 1); ++ OUT_RING (chan, 0); ++ ++ length -= amount; ++ src_offset += amount; ++ dst_offset += amount; + } + ++ return 0; ++} ++ ++static int ++nv04_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo, ++ struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem) ++{ ++ u32 src_offset = old_mem->mm_node->start << PAGE_SHIFT; ++ u32 dst_offset = new_mem->mm_node->start << PAGE_SHIFT; ++ u32 page_count = new_mem->num_pages; ++ int ret; ++ ++ 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(bo, chan, old_mem)); ++ OUT_RING (chan, nouveau_bo_mem_ctxdma(bo, chan, new_mem)); ++ + 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); ++ OUT_RING (chan, src_offset); ++ OUT_RING (chan, 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, 0x00000101); ++ OUT_RING (chan, 0x00000000); + BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_NOP, 1); +- OUT_RING(chan, 0); ++ OUT_RING (chan, 0); + + page_count -= line_count; + src_offset += (PAGE_SIZE * line_count); + dst_offset += (PAGE_SIZE * line_count); + } + ++ return 0; ++} ++ ++static int ++nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr, ++ bool no_wait_reserve, bool no_wait_gpu, ++ struct ttm_mem_reg *new_mem) ++{ ++ struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev); ++ struct nouveau_bo *nvbo = nouveau_bo(bo); ++ struct nouveau_channel *chan; ++ int ret; ++ ++ chan = nvbo->channel; ++ if (!chan || nvbo->no_vm) ++ chan = dev_priv->channel; ++ ++ if (dev_priv->card_type < NV_50) ++ ret = nv04_bo_move_m2mf(chan, bo, &bo->mem, new_mem); ++ else ++ ret = nv50_bo_move_m2mf(chan, bo, &bo->mem, new_mem); ++ if (ret) ++ return ret; ++ + return nouveau_bo_move_accel_cleanup(chan, nvbo, evict, no_wait_reserve, no_wait_gpu, new_mem); + } + +@@ -725,13 +816,6 @@ nouveau_bo_move(struct ttm_buffer_object *bo, bool evict, bool intr, + if (ret) return ret; - /* Software copy if the card isn't up and running yet. */ +- /* Software copy if the card isn't up and running yet. */ - if (dev_priv->init_state != NOUVEAU_CARD_INIT_DONE || - !dev_priv->channel) { -+ if (!dev_priv->channel) { - ret = ttm_bo_move_memcpy(bo, evict, no_wait_reserve, no_wait_gpu, new_mem); +- ret = ttm_bo_move_memcpy(bo, evict, no_wait_reserve, no_wait_gpu, new_mem); +- goto out; +- } +- + /* Fake bo copy. */ + if (old_mem->mem_type == TTM_PL_SYSTEM && !bo->ttm) { + BUG_ON(bo->mem.mm_node != NULL); +@@ -740,6 +824,12 @@ nouveau_bo_move(struct ttm_buffer_object *bo, bool evict, bool intr, goto out; } + ++ /* Software copy if the card isn't up and running yet. */ ++ if (!dev_priv->channel) { ++ ret = ttm_bo_move_memcpy(bo, evict, no_wait_reserve, no_wait_gpu, new_mem); ++ goto out; ++ } ++ + /* Hardware assisted copy. */ + if (new_mem->mem_type == TTM_PL_SYSTEM) + ret = nouveau_bo_move_flipd(bo, evict, intr, no_wait_reserve, no_wait_gpu, new_mem); +@@ -815,7 +905,26 @@ nouveau_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) + static int + nouveau_ttm_fault_reserve_notify(struct ttm_buffer_object *bo) + { +- return 0; ++ struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev); ++ struct nouveau_bo *nvbo = nouveau_bo(bo); ++ ++ /* as long as the bo isn't in vram, and isn't tiled, we've got ++ * nothing to do here. ++ */ ++ if (bo->mem.mem_type != TTM_PL_VRAM) { ++ if (dev_priv->card_type < NV_50 || !nvbo->tile_flags) ++ return 0; ++ } ++ ++ /* make sure bo is in mappable vram */ ++ if (bo->mem.mm_node->start + bo->mem.num_pages < dev_priv->fb_mappable_pages) ++ return 0; ++ ++ ++ nvbo->placement.fpfn = 0; ++ nvbo->placement.lpfn = dev_priv->fb_mappable_pages; ++ nouveau_bo_placement_set(nvbo, TTM_PL_VRAM, 0); ++ return ttm_bo_validate(bo, &nvbo->placement, false, true, false); + } + + struct ttm_bo_driver nouveau_bo_driver = { diff --git a/drivers/gpu/drm/nouveau/nouveau_calc.c b/drivers/gpu/drm/nouveau/nouveau_calc.c -index 88f9bc0..ca85da7 100644 +index 88f9bc0..23d9896 100644 --- a/drivers/gpu/drm/nouveau/nouveau_calc.c +++ b/drivers/gpu/drm/nouveau/nouveau_calc.c @@ -200,7 +200,7 @@ nv04_update_arb(struct drm_device *dev, int VClk, int bpp, @@ -1397,11 +3393,54 @@ index 88f9bc0..ca85da7 100644 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); +@@ -234,7 +234,7 @@ nv04_update_arb(struct drm_device *dev, int VClk, int bpp, + } + + static void +-nv30_update_arb(int *burst, int *lwm) ++nv20_update_arb(int *burst, int *lwm) + { + unsigned int fifo_size, burst_size, graphics_lwm; + +@@ -251,14 +251,14 @@ 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) ++ if (dev_priv->card_type < NV_20) + 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); ++ nv20_update_arb(burst, lwm); + } + + static int diff --git a/drivers/gpu/drm/nouveau/nouveau_channel.c b/drivers/gpu/drm/nouveau/nouveau_channel.c -index 1fc57ef..e952c3b 100644 +index 1fc57ef..53c2a6f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_channel.c +++ b/drivers/gpu/drm/nouveau/nouveau_channel.c -@@ -257,9 +257,7 @@ nouveau_channel_free(struct nouveau_channel *chan) +@@ -69,14 +69,8 @@ nouveau_channel_pushbuf_ctxdma_init(struct nouveau_channel *chan) + chan->pushbuf_base = pb->bo.mem.mm_node->start << PAGE_SHIFT; + } + +- 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; +- } +- ++ nouveau_gpuobj_ref(pushbuf, &chan->pushbuf); ++ nouveau_gpuobj_ref(NULL, &pushbuf); + return 0; + } + +@@ -257,9 +251,7 @@ nouveau_channel_free(struct nouveau_channel *chan) nouveau_debugfs_channel_fini(chan); /* Give outstanding push buffers a chance to complete */ @@ -1411,7 +3450,18 @@ index 1fc57ef..e952c3b 100644 if (chan->fence.sequence != chan->fence.sequence_ack) { struct nouveau_fence *fence = NULL; -@@ -368,8 +366,6 @@ nouveau_ioctl_fifo_alloc(struct drm_device *dev, void *data, +@@ -309,8 +301,9 @@ nouveau_channel_free(struct nouveau_channel *chan) + spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); + + /* Release the channel's resources */ +- nouveau_gpuobj_ref_del(dev, &chan->pushbuf); ++ nouveau_gpuobj_ref(NULL, &chan->pushbuf); + if (chan->pushbuf_bo) { ++ nouveau_bo_unmap(chan->pushbuf_bo); + nouveau_bo_unpin(chan->pushbuf_bo); + nouveau_bo_ref(NULL, &chan->pushbuf_bo); + } +@@ -368,8 +361,6 @@ nouveau_ioctl_fifo_alloc(struct drm_device *dev, void *data, struct nouveau_channel *chan; int ret; @@ -1420,7 +3470,7 @@ index 1fc57ef..e952c3b 100644 if (dev_priv->engine.graph.accel_blocked) return -ENODEV; -@@ -418,7 +414,6 @@ nouveau_ioctl_fifo_free(struct drm_device *dev, void *data, +@@ -418,7 +409,6 @@ nouveau_ioctl_fifo_free(struct drm_device *dev, void *data, struct drm_nouveau_channel_free *cfree = data; struct nouveau_channel *chan; @@ -1429,10 +3479,46 @@ index 1fc57ef..e952c3b 100644 nouveau_channel_free(chan); diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c -index 149ed22..734e926 100644 +index 149ed22..46584c3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c -@@ -102,63 +102,15 @@ nouveau_connector_destroy(struct drm_connector *drm_connector) +@@ -37,12 +37,6 @@ + #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) + { +@@ -82,6 +76,22 @@ nouveau_encoder_connector_get(struct nouveau_encoder *encoder) + return NULL; + } + ++/*TODO: This could use improvement, and learn to handle the fixed ++ * BIOS tables etc. It's fine currently, for its only user. ++ */ ++int ++nouveau_connector_bpp(struct drm_connector *connector) ++{ ++ struct nouveau_connector *nv_connector = nouveau_connector(connector); ++ ++ if (nv_connector->edid && nv_connector->edid->revision >= 4) { ++ u8 bpc = ((nv_connector->edid->input & 0x70) >> 3) + 4; ++ if (bpc > 4) ++ return bpc; ++ } ++ ++ return 18; ++} + + static void + nouveau_connector_destroy(struct drm_connector *drm_connector) +@@ -102,60 +112,12 @@ nouveau_connector_destroy(struct drm_connector *drm_connector) kfree(drm_connector); } @@ -1493,32 +3579,61 @@ index 149ed22..734e926 100644 + int i; for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { -- struct nouveau_i2c_chan *i2c = NULL; -+ struct nouveau_i2c_chan *i2c; - struct nouveau_encoder *nv_encoder; - struct drm_mode_object *obj; - int id; -@@ -171,17 +123,9 @@ nouveau_connector_ddc_detect(struct drm_connector *connector, - if (!obj) - continue; - nv_encoder = nouveau_encoder(obj_to_encoder(obj)); -+ i2c = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index); + struct nouveau_i2c_chan *i2c = NULL; +@@ -174,14 +136,8 @@ nouveau_connector_ddc_detect(struct drm_connector *connector, -- if (nv_encoder->dcb->i2c_index < 0xf) -- i2c = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index); + 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) { + if (i2c && nouveau_probe_i2c_addr(i2c, 0x50)) { *pnv_encoder = nv_encoder; return i2c; } -@@ -234,21 +178,7 @@ nouveau_connector_detect(struct drm_connector *connector) +@@ -190,6 +146,36 @@ nouveau_connector_ddc_detect(struct drm_connector *connector, + return NULL; + } + ++static struct nouveau_encoder * ++nouveau_connector_of_detect(struct drm_connector *connector) ++{ ++#ifdef __powerpc__ ++ struct drm_device *dev = connector->dev; ++ struct nouveau_connector *nv_connector = nouveau_connector(connector); ++ struct nouveau_encoder *nv_encoder; ++ struct device_node *cn, *dn = pci_device_to_OF_node(dev->pdev); ++ ++ if (!dn || ++ !((nv_encoder = find_encoder_by_type(connector, OUTPUT_TMDS)) || ++ (nv_encoder = find_encoder_by_type(connector, OUTPUT_ANALOG)))) ++ return NULL; ++ ++ for_each_child_of_node(dn, cn) { ++ const char *name = of_get_property(cn, "name", NULL); ++ const void *edid = of_get_property(cn, "EDID", NULL); ++ int idx = name ? name[strlen(name) - 1] - 'A' : 0; ++ ++ if (nv_encoder->dcb->i2c_index == idx && edid) { ++ nv_connector->edid = ++ kmemdup(edid, EDID_LENGTH, GFP_KERNEL); ++ of_node_put(cn); ++ return nv_encoder; ++ } ++ } ++#endif ++ return NULL; ++} ++ + static void + nouveau_connector_set_encoder(struct drm_connector *connector, + struct nouveau_encoder *nv_encoder) +@@ -234,21 +220,7 @@ nouveau_connector_detect(struct drm_connector *connector) struct nouveau_connector *nv_connector = nouveau_connector(connector); struct nouveau_encoder *nv_encoder = NULL; struct nouveau_i2c_chan *i2c; @@ -1541,7 +3656,7 @@ index 149ed22..734e926 100644 /* Cleanup the previous EDID block. */ if (nv_connector->edid) { -@@ -259,9 +189,7 @@ nouveau_connector_detect(struct drm_connector *connector) +@@ -259,9 +231,7 @@ nouveau_connector_detect(struct drm_connector *connector) i2c = nouveau_connector_ddc_detect(connector, &nv_encoder); if (i2c) { @@ -1551,7 +3666,20 @@ index 149ed22..734e926 100644 drm_mode_connector_update_edid_property(connector, nv_connector->edid); if (!nv_connector->edid) { -@@ -321,6 +249,85 @@ detect_analog: +@@ -301,6 +271,12 @@ nouveau_connector_detect(struct drm_connector *connector) + return connector_status_connected; + } + ++ nv_encoder = nouveau_connector_of_detect(connector); ++ if (nv_encoder) { ++ nouveau_connector_set_encoder(connector, nv_encoder); ++ return connector_status_connected; ++ } ++ + detect_analog: + nv_encoder = find_encoder_by_type(connector, OUTPUT_ANALOG); + if (!nv_encoder && !nouveau_tv_disable) +@@ -321,6 +297,85 @@ detect_analog: return connector_status_disconnected; } @@ -1637,7 +3765,26 @@ index 149ed22..734e926 100644 static void nouveau_connector_force(struct drm_connector *connector) { -@@ -441,7 +448,8 @@ nouveau_connector_native_mode(struct drm_connector *connector) +@@ -353,6 +408,7 @@ nouveau_connector_set_property(struct drm_connector *connector, + { + struct nouveau_connector *nv_connector = nouveau_connector(connector); + struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; ++ struct drm_encoder *encoder = to_drm_encoder(nv_encoder); + struct drm_device *dev = connector->dev; + int ret; + +@@ -425,8 +481,8 @@ nouveau_connector_set_property(struct drm_connector *connector, + } + + 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 get_slave_funcs(encoder)->set_property( ++ encoder, connector, property, value); + + return -EINVAL; + } +@@ -441,7 +497,8 @@ nouveau_connector_native_mode(struct drm_connector *connector) int high_w = 0, high_h = 0, high_v = 0; list_for_each_entry(mode, &nv_connector->base.probed_modes, head) { @@ -1647,13 +3794,14 @@ index 149ed22..734e926 100644 continue; /* Use preferred mode if there is one.. */ -@@ -534,21 +542,27 @@ static int +@@ -534,21 +591,30 @@ static int nouveau_connector_get_modes(struct drm_connector *connector) { struct drm_device *dev = connector->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_connector *nv_connector = nouveau_connector(connector); struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; ++ struct drm_encoder *encoder = to_drm_encoder(nv_encoder); int ret = 0; - /* If we're not LVDS, destroy the previous native mode, the attached @@ -1673,15 +3821,21 @@ index 149ed22..734e926 100644 + if (nv_encoder->dcb->type == OUTPUT_LVDS && + (nv_encoder->dcb->lvdsconf.use_straps_for_mode || + dev_priv->vbios.fp_no_ddc) && nouveau_bios_fp_mode(dev, NULL)) { -+ nv_connector->native_mode = drm_mode_create(dev); -+ nouveau_bios_fp_mode(dev, nv_connector->native_mode); ++ struct drm_display_mode mode; ++ ++ nouveau_bios_fp_mode(dev, &mode); ++ nv_connector->native_mode = drm_mode_duplicate(dev, &mode); + } /* 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 -@@ -569,7 +583,8 @@ nouveau_connector_get_modes(struct drm_connector *connector) - ret = get_slave_funcs(nv_encoder)-> - get_modes(to_drm_encoder(nv_encoder), connector); +@@ -566,10 +632,10 @@ nouveau_connector_get_modes(struct drm_connector *connector) + } + + if (nv_encoder->dcb->type == OUTPUT_TV) +- ret = get_slave_funcs(nv_encoder)-> +- get_modes(to_drm_encoder(nv_encoder), connector); ++ ret = get_slave_funcs(encoder)->get_modes(encoder, connector); - if (nv_encoder->dcb->type == OUTPUT_LVDS) + if (nv_connector->dcb->type == DCB_CONNECTOR_LVDS || @@ -1689,7 +3843,33 @@ index 149ed22..734e926 100644 ret += nouveau_connector_scaler_modes_add(connector); return ret; -@@ -643,6 +658,44 @@ nouveau_connector_best_encoder(struct drm_connector *connector) +@@ -582,6 +648,7 @@ nouveau_connector_mode_valid(struct drm_connector *connector, + 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; ++ struct drm_encoder *encoder = to_drm_encoder(nv_encoder); + unsigned min_clock = 25000, max_clock = min_clock; + unsigned clock = mode->clock; + +@@ -608,15 +675,14 @@ nouveau_connector_mode_valid(struct drm_connector *connector, + max_clock = 350000; + break; + case OUTPUT_TV: +- return get_slave_funcs(nv_encoder)-> +- mode_valid(to_drm_encoder(nv_encoder), mode); ++ return get_slave_funcs(encoder)->mode_valid(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; ++ clock = clock * nouveau_connector_bpp(connector) / 8; + break; + default: + BUG_ON(1); +@@ -643,6 +709,44 @@ nouveau_connector_best_encoder(struct drm_connector *connector) return NULL; } @@ -1734,7 +3914,7 @@ index 149ed22..734e926 100644 static const struct drm_connector_helper_funcs nouveau_connector_helper_funcs = { .get_modes = nouveau_connector_get_modes, -@@ -662,148 +715,74 @@ nouveau_connector_funcs = { +@@ -662,148 +766,74 @@ nouveau_connector_funcs = { .force = nouveau_connector_force }; @@ -1909,7 +4089,7 @@ index 149ed22..734e926 100644 nv_connector->dcb = dcb; connector = &nv_connector->base; -@@ -811,27 +790,21 @@ nouveau_connector_create(struct drm_device *dev, +@@ -811,27 +841,21 @@ nouveau_connector_create(struct drm_device *dev, connector->interlace_allowed = false; connector->doublescan_allowed = false; @@ -1948,7 +4128,7 @@ index 149ed22..734e926 100644 } /* Init DVI-I specific properties */ -@@ -841,12 +814,8 @@ nouveau_connector_create(struct drm_device *dev, +@@ -841,12 +865,8 @@ nouveau_connector_create(struct drm_device *dev, drm_connector_attach_property(connector, dev->mode_config.dvi_i_select_subconnector_property, 0); } @@ -1961,7 +4141,7 @@ index 149ed22..734e926 100644 if (dev_priv->card_type >= NV_50) { drm_connector_attach_property(connector, dev->mode_config.scaling_mode_property, -@@ -858,17 +827,6 @@ nouveau_connector_create(struct drm_device *dev, +@@ -858,17 +878,6 @@ nouveau_connector_create(struct drm_device *dev, case DCB_CONNECTOR_TV_3: nv_connector->scaling_mode = DRM_MODE_SCALE_NONE; break; @@ -1979,7 +4159,7 @@ index 149ed22..734e926 100644 default: nv_connector->scaling_mode = DRM_MODE_SCALE_FULLSCREEN; -@@ -882,15 +840,15 @@ nouveau_connector_create(struct drm_device *dev, +@@ -882,15 +891,15 @@ nouveau_connector_create(struct drm_device *dev, break; } @@ -2004,10 +4184,10 @@ index 149ed22..734e926 100644 - return 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h -index 4ef38ab..0d2e668 100644 +index 4ef38ab..c21ed6b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.h +++ b/drivers/gpu/drm/nouveau/nouveau_connector.h -@@ -49,7 +49,10 @@ static inline struct nouveau_connector *nouveau_connector( +@@ -49,7 +49,13 @@ static inline struct nouveau_connector *nouveau_connector( return container_of(con, struct nouveau_connector, base); } @@ -2018,37 +4198,133 @@ index 4ef38ab..0d2e668 100644 + +void +nouveau_connector_set_polling(struct drm_connector *); ++ ++int ++nouveau_connector_bpp(struct drm_connector *); #endif /* __NOUVEAU_CONNECTOR_H__ */ +diff --git a/drivers/gpu/drm/nouveau/nouveau_debugfs.c b/drivers/gpu/drm/nouveau/nouveau_debugfs.c +index 7933de4..8e15923 100644 +--- a/drivers/gpu/drm/nouveau/nouveau_debugfs.c ++++ b/drivers/gpu/drm/nouveau/nouveau_debugfs.c +@@ -157,7 +157,23 @@ nouveau_debugfs_vbios_image(struct seq_file *m, void *data) + return 0; + } + ++static int ++nouveau_debugfs_evict_vram(struct seq_file *m, void *data) ++{ ++ struct drm_info_node *node = (struct drm_info_node *) m->private; ++ struct drm_nouveau_private *dev_priv = node->minor->dev->dev_private; ++ int ret; ++ ++ ret = ttm_bo_evict_mm(&dev_priv->ttm.bdev, TTM_PL_VRAM); ++ if (ret) ++ seq_printf(m, "failed: %d", ret); ++ else ++ seq_printf(m, "succeeded\n"); ++ return 0; ++} ++ + static struct drm_info_list nouveau_debugfs_list[] = { ++ { "evict_vram", nouveau_debugfs_evict_vram, 0, NULL }, + { "chipset", nouveau_debugfs_chipset_info, 0, NULL }, + { "memory", nouveau_debugfs_memory_info, 0, NULL }, + { "vbios.rom", nouveau_debugfs_vbios_image, 0, NULL }, diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.c b/drivers/gpu/drm/nouveau/nouveau_dma.c -index 65c441a..2e3c6ca 100644 +index 65c441a..eb24e2b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dma.c +++ b/drivers/gpu/drm/nouveau/nouveau_dma.c -@@ -92,11 +92,9 @@ nouveau_dma_init(struct nouveau_channel *chan) +@@ -28,6 +28,7 @@ + #include "drm.h" + #include "nouveau_drv.h" + #include "nouveau_dma.h" ++#include "nouveau_ramht.h" + + void + nouveau_dma_pre_init(struct nouveau_channel *chan) +@@ -58,26 +59,27 @@ 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; +- struct nouveau_gpuobj *nvsw = NULL; ++ struct nouveau_gpuobj *obj = 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); ++ 0x0039 : 0x5039, &obj); + if (ret) return ret; - /* Map M2MF notifier object - fbcon. */ +- ret = nouveau_gpuobj_ref_add(dev, chan, NvM2MF, m2mf, NULL); ++ ret = nouveau_ramht_insert(chan, NvM2MF, obj); ++ nouveau_gpuobj_ref(NULL, &obj); + if (ret) + return ret; + + /* Create an NV_SW object for various sync purposes */ +- ret = nouveau_gpuobj_sw_new(chan, NV_SW, &nvsw); ++ ret = nouveau_gpuobj_sw_new(chan, NV_SW, &obj); + if (ret) + return ret; + +- ret = nouveau_gpuobj_ref_add(dev, chan, NvSw, nvsw, NULL); ++ ret = nouveau_ramht_insert(chan, NvSw, obj); ++ nouveau_gpuobj_ref(NULL, &obj); + if (ret) + return ret; + +@@ -91,13 +93,6 @@ nouveau_dma_init(struct nouveau_channel *chan) + 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; - } -+ ret = nouveau_bo_map(chan->notifier_bo); -+ if (ret) -+ return ret; - +- /* Insert NOPS for NOUVEAU_DMA_SKIPS */ ret = RING_SPACE(chan, NOUVEAU_DMA_SKIPS); + if (ret) +@@ -219,7 +214,7 @@ nv50_dma_push_wait(struct nouveau_channel *chan, int count) + + chan->dma.ib_free = get - chan->dma.ib_put; + if (chan->dma.ib_free <= 0) +- chan->dma.ib_free += chan->dma.ib_max + 1; ++ chan->dma.ib_free += chan->dma.ib_max; + } + + return 0; diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c -index deeb21c..184bc95 100644 +index deeb21c..4562f30 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c -@@ -271,12 +271,26 @@ nouveau_dp_link_train(struct drm_encoder *encoder) +@@ -23,8 +23,10 @@ + */ + + #include "drmP.h" ++ + #include "nouveau_drv.h" + #include "nouveau_i2c.h" ++#include "nouveau_connector.h" + #include "nouveau_encoder.h" + + static int +@@ -270,13 +272,39 @@ bool + nouveau_dp_link_train(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - uint8_t config[4]; - uint8_t status[3]; ++ struct nouveau_connector *nv_connector; + struct bit_displayport_encoder_table *dpe; + int dpe_headerlen; + uint8_t config[4], status[3]; @@ -2057,12 +4333,21 @@ index deeb21c..184bc95 100644 NV_DEBUG_KMS(dev, "link training!!\n"); + ++ nv_connector = nouveau_encoder_connector_get(nv_encoder); ++ if (!nv_connector) ++ return false; ++ + 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 false; + } + ++ /* disable hotplug detect, this flips around on some panels during ++ * link training. ++ */ ++ pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, false); ++ + if (dpe->script0) { + NV_DEBUG_KMS(dev, "SOR-%d: running DP script 0\n", nv_encoder->or); + nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script0), @@ -2072,7 +4357,17 @@ index deeb21c..184bc95 100644 train: cr_done = eq_done = false; -@@ -403,6 +417,12 @@ stop: +@@ -289,7 +317,8 @@ train: + return false; + + config[0] = nv_encoder->dp.link_nr; +- if (nv_encoder->dp.dpcd_version >= 0x11) ++ if (nv_encoder->dp.dpcd_version >= 0x11 && ++ nv_encoder->dp.enhanced_frame) + config[0] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; + + ret = nouveau_dp_lane_count_set(encoder, config[0]); +@@ -403,6 +432,15 @@ stop: } } @@ -2081,15 +4376,142 @@ index deeb21c..184bc95 100644 + nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script1), + nv_encoder->dcb); + } ++ ++ /* re-enable hotplug detect */ ++ pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, true); + return eq_done; } +@@ -431,10 +469,12 @@ nouveau_dp_detect(struct drm_encoder *encoder) + !nv_encoder->dcb->dpconf.link_bw) + nv_encoder->dp.link_bw = DP_LINK_BW_1_62; + +- nv_encoder->dp.link_nr = dpcd[2] & 0xf; ++ nv_encoder->dp.link_nr = dpcd[2] & DP_MAX_LANE_COUNT_MASK; + if (nv_encoder->dp.link_nr > nv_encoder->dcb->dpconf.link_nr) + nv_encoder->dp.link_nr = nv_encoder->dcb->dpconf.link_nr; + ++ nv_encoder->dp.enhanced_frame = (dpcd[2] & DP_ENHANCED_FRAME_CAP); ++ + return true; + } + +@@ -487,7 +527,8 @@ nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr, + 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)) { ++ if (!nv_wait(dev, NV50_AUXCH_CTRL(index), ++ 0x00010000, 0x00000000)) { + NV_ERROR(dev, "expected bit 16 == 0, got 0x%08x\n", + nv_rd32(dev, NV50_AUXCH_CTRL(index))); + ret = -EBUSY; +@@ -535,47 +576,64 @@ out: + 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) ++static int ++nouveau_dp_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) + { +- struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; +- struct nouveau_i2c_chan *auxch = (struct nouveau_i2c_chan *)adapter; ++ struct nouveau_i2c_chan *auxch = (struct nouveau_i2c_chan *)adap; + 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; +- } ++ struct i2c_msg *msg = msgs; ++ int ret, mcnt = num; + +- if (!(mode & MODE_I2C_STOP)) +- cmd |= AUX_I2C_MOT; ++ while (mcnt--) { ++ u8 remaining = msg->len; ++ u8 *ptr = msg->buf; + +- if (mode & MODE_I2C_START) +- return 1; ++ while (remaining) { ++ u8 cnt = (remaining > 16) ? 16 : remaining; ++ u8 cmd; + +- 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; ++ if (msg->flags & I2C_M_RD) ++ cmd = AUX_I2C_READ; ++ else ++ cmd = AUX_I2C_WRITE; ++ ++ if (mcnt || remaining > 16) ++ cmd |= AUX_I2C_MOT; ++ ++ ret = nouveau_dp_auxch(auxch, cmd, msg->addr, ptr, cnt); ++ if (ret < 0) ++ return ret; ++ ++ switch (ret & NV50_AUXCH_STAT_REPLY_I2C) { ++ case NV50_AUXCH_STAT_REPLY_I2C_ACK: ++ break; ++ case NV50_AUXCH_STAT_REPLY_I2C_NACK: ++ return -EREMOTEIO; ++ case NV50_AUXCH_STAT_REPLY_I2C_DEFER: ++ udelay(100); ++ continue; ++ default: ++ NV_ERROR(dev, "bad auxch reply: 0x%08x\n", ret); ++ return -EREMOTEIO; ++ } ++ ++ ptr += cnt; ++ remaining -= cnt; + } ++ ++ msg++; + } ++ ++ return num; ++} ++ ++static u32 ++nouveau_dp_i2c_func(struct i2c_adapter *adap) ++{ ++ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; + } + ++const struct i2c_algorithm nouveau_dp_i2c_algo = { ++ .master_xfer = nouveau_dp_i2c_xfer, ++ .functionality = nouveau_dp_i2c_func ++}; diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c -index 2737704..203d0b6 100644 +index 2737704..ee2442f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.c +++ b/drivers/gpu/drm/nouveau/nouveau_drv.c -@@ -35,10 +35,6 @@ +@@ -35,13 +35,9 @@ #include "drm_pciids.h" @@ -2097,9 +4519,15 @@ index 2737704..203d0b6 100644 -int nouveau_ctxfw = 0; -module_param_named(ctxfw, nouveau_ctxfw, int, 0400); - - MODULE_PARM_DESC(noagp, "Disable AGP"); - int nouveau_noagp; - module_param_named(noagp, nouveau_noagp, int, 0400); +-MODULE_PARM_DESC(noagp, "Disable AGP"); +-int nouveau_noagp; +-module_param_named(noagp, nouveau_noagp, int, 0400); ++MODULE_PARM_DESC(agpmode, "AGP mode (0 to disable AGP)"); ++int nouveau_agpmode = -1; ++module_param_named(agpmode, nouveau_agpmode, int, 0400); + + MODULE_PARM_DESC(modeset, "Enable kernel modesetting"); + static int nouveau_modeset = -1; /* kms */ @@ -56,7 +52,7 @@ int nouveau_vram_pushbuf; module_param_named(vram_pushbuf, nouveau_vram_pushbuf, int, 0400); @@ -2109,7 +4537,18 @@ index 2737704..203d0b6 100644 module_param_named(vram_notify, nouveau_vram_notify, int, 0400); MODULE_PARM_DESC(duallink, "Allow dual-link TMDS (>=GeForce 8)"); -@@ -155,9 +151,6 @@ nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state) +@@ -83,6 +79,10 @@ MODULE_PARM_DESC(nofbaccel, "Disable fbcon acceleration"); + int nouveau_nofbaccel = 0; + module_param_named(nofbaccel, nouveau_nofbaccel, int, 0400); + ++MODULE_PARM_DESC(force_post, "Force POST"); ++int nouveau_force_post = 0; ++module_param_named(force_post, nouveau_force_post, int, 0400); ++ + MODULE_PARM_DESC(override_conntype, "Ignore DCB connector type"); + int nouveau_override_conntype = 0; + module_param_named(override_conntype, nouveau_override_conntype, int, 0400); +@@ -155,9 +155,6 @@ nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state) struct drm_crtc *crtc; int ret, i; @@ -2119,7 +4558,7 @@ index 2737704..203d0b6 100644 if (pm_state.event == PM_EVENT_PRETHAW) return 0; -@@ -257,9 +250,6 @@ nouveau_pci_resume(struct pci_dev *pdev) +@@ -257,9 +254,6 @@ nouveau_pci_resume(struct pci_dev *pdev) struct drm_crtc *crtc; int ret, i; @@ -2129,7 +4568,21 @@ index 2737704..203d0b6 100644 nouveau_fbcon_save_disable_accel(dev); NV_INFO(dev, "We're back, enabling device...\n"); -@@ -323,7 +313,6 @@ nouveau_pci_resume(struct pci_dev *pdev) +@@ -269,6 +263,13 @@ nouveau_pci_resume(struct pci_dev *pdev) + return -1; + pci_set_master(dev->pdev); + ++ /* Make sure the AGP controller is in a consistent state */ ++ if (dev_priv->gart_info.type == NOUVEAU_GART_AGP) ++ nouveau_mem_reset_agp(dev); ++ ++ /* Make the CRTCs accessible */ ++ engine->display.early_init(dev); ++ + NV_INFO(dev, "POSTing device...\n"); + ret = nouveau_run_vbios_init(dev); + if (ret) +@@ -323,7 +324,6 @@ nouveau_pci_resume(struct pci_dev *pdev) list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); @@ -2137,20 +4590,20 @@ index 2737704..203d0b6 100644 ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM); if (!ret) -@@ -332,10 +321,9 @@ nouveau_pci_resume(struct pci_dev *pdev) +@@ -332,11 +332,7 @@ nouveau_pci_resume(struct pci_dev *pdev) NV_ERROR(dev, "Could not pin/map cursor.\n"); } - if (dev_priv->card_type < NV_50) { -+ if (dev_priv->card_type < NV_50) - nv04_display_restore(dev); +- nv04_display_restore(dev); - NVLockVgaCrtcs(dev, false); - } else -+ else - nv50_display_init(dev); +- nv50_display_init(dev); ++ engine->display.init(dev); list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { -@@ -371,7 +359,8 @@ nouveau_pci_resume(struct pci_dev *pdev) + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); +@@ -371,7 +367,8 @@ nouveau_pci_resume(struct pci_dev *pdev) static struct drm_driver driver = { .driver_features = DRIVER_USE_AGP | DRIVER_PCI_DMA | DRIVER_SG | @@ -2160,7 +4613,7 @@ index 2737704..203d0b6 100644 .load = nouveau_load, .firstopen = nouveau_firstopen, .lastclose = nouveau_lastclose, -@@ -438,16 +427,18 @@ static int __init nouveau_init(void) +@@ -438,16 +435,18 @@ static int __init nouveau_init(void) nouveau_modeset = 1; } @@ -2184,7 +4637,7 @@ index 2737704..203d0b6 100644 nouveau_unregister_dsm_handler(); } diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h -index c697191..20ca5b8 100644 +index 8be2f59..be53e92 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -123,14 +123,6 @@ nvbo_kmap_obj_iovirtual(struct nouveau_bo *nvbo) @@ -2202,16 +4655,55 @@ index c697191..20ca5b8 100644 enum nouveau_flags { NV_NFORCE = 0x10000000, NV_NFORCE2 = 0x20000000 -@@ -149,7 +141,7 @@ struct nouveau_gpuobj { +@@ -141,22 +133,24 @@ enum nouveau_flags { + #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 drm_device *dev; ++ struct kref refcount; struct list_head list; - struct nouveau_channel *im_channel; +- struct nouveau_channel *im_channel; - struct mem_block *im_pramin; + struct drm_mm_node *im_pramin; struct nouveau_bo *im_backing; - uint32_t im_backing_start; +- uint32_t im_backing_start; uint32_t *im_backing_suspend; -@@ -196,7 +188,7 @@ struct nouveau_channel { + int im_bound; + + uint32_t flags; +- int refcount; ++ ++ u32 size; ++ u32 pinst; ++ u32 cinst; ++ u64 vinst; + + uint32_t engine; + uint32_t class; +@@ -165,16 +159,6 @@ 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; +@@ -196,37 +180,36 @@ struct nouveau_channel { struct list_head pending; uint32_t sequence; uint32_t sequence_ack; @@ -2220,7 +4712,12 @@ index c697191..20ca5b8 100644 } fence; /* DMA push buffer */ -@@ -206,7 +198,7 @@ struct nouveau_channel { +- struct nouveau_gpuobj_ref *pushbuf; +- struct nouveau_bo *pushbuf_bo; +- uint32_t pushbuf_base; ++ struct nouveau_gpuobj *pushbuf; ++ struct nouveau_bo *pushbuf_bo; ++ uint32_t pushbuf_base; /* Notifier memory */ struct nouveau_bo *notifier_bo; @@ -2228,17 +4725,37 @@ index c697191..20ca5b8 100644 + struct drm_mm notifier_heap; /* PFIFO context */ - struct nouveau_gpuobj_ref *ramfc; -@@ -224,7 +216,7 @@ struct nouveau_channel { +- struct nouveau_gpuobj_ref *ramfc; +- struct nouveau_gpuobj_ref *cache; ++ struct nouveau_gpuobj *ramfc; ++ struct nouveau_gpuobj *cache; + + /* PGRAPH context */ + /* XXX may be merge 2 pointers as private data ??? */ +- struct nouveau_gpuobj_ref *ramin_grctx; ++ struct nouveau_gpuobj *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]; ++ struct nouveau_gpuobj *vm_pd; ++ struct nouveau_gpuobj *vm_gart_pt; ++ struct nouveau_gpuobj *vm_vram_pt[NV50_VM_VRAM_NR]; /* Objects */ - struct nouveau_gpuobj_ref *ramin; /* Private instmem */ +- struct nouveau_gpuobj_ref *ramin; /* Private instmem */ - struct mem_block *ramin_heap; /* Private PRAMIN heap */ -+ struct drm_mm ramin_heap; /* Private PRAMIN heap */ - struct nouveau_gpuobj_ref *ramht; /* Hash table */ - struct list_head ramht_refs; /* Objects referenced by RAMHT */ +- struct nouveau_gpuobj_ref *ramht; /* Hash table */ +- struct list_head ramht_refs; /* Objects referenced by RAMHT */ ++ struct nouveau_gpuobj *ramin; /* Private instmem */ ++ struct drm_mm ramin_heap; /* Private PRAMIN heap */ ++ struct nouveau_ramht *ramht; /* Hash table */ -@@ -277,8 +269,7 @@ struct nouveau_instmem_engine { + /* GPU object info for stuff used in-kernel (mm_enabled) */ + uint32_t m2mf_ntfy; +@@ -277,8 +260,7 @@ struct nouveau_instmem_engine { void (*clear)(struct drm_device *, struct nouveau_gpuobj *); int (*bind)(struct drm_device *, struct nouveau_gpuobj *); int (*unbind)(struct drm_device *, struct nouveau_gpuobj *); @@ -2248,7 +4765,7 @@ index c697191..20ca5b8 100644 }; struct nouveau_mc_engine { -@@ -303,10 +294,11 @@ struct nouveau_fb_engine { +@@ -303,17 +285,17 @@ struct nouveau_fb_engine { }; struct nouveau_fifo_engine { @@ -2256,13 +4773,20 @@ index c697191..20ca5b8 100644 - int channels; -+ struct nouveau_gpuobj_ref *playlist[2]; ++ struct nouveau_gpuobj *playlist[2]; + int cur_playlist; + int (*init)(struct drm_device *); void (*takedown)(struct drm_device *); -@@ -339,10 +331,11 @@ struct nouveau_pgraph_object_class { + void (*disable)(struct drm_device *); + void (*enable)(struct drm_device *); + bool (*reassign)(struct drm_device *, bool enable); +- bool (*cache_flush)(struct drm_device *dev); + bool (*cache_pull)(struct drm_device *dev, bool enable); + + int (*channel_id)(struct drm_device *); +@@ -339,10 +321,11 @@ struct nouveau_pgraph_object_class { struct nouveau_pgraph_engine { struct nouveau_pgraph_object_class *grclass; bool accel_blocked; @@ -2271,12 +4795,60 @@ index c697191..20ca5b8 100644 int grctx_size; + /* NV2x/NV3x context table (0x400780) */ -+ struct nouveau_gpuobj_ref *ctx_table; ++ struct nouveau_gpuobj *ctx_table; + int (*init)(struct drm_device *); void (*takedown)(struct drm_device *); -@@ -500,11 +493,6 @@ enum nouveau_card_type { +@@ -358,6 +341,24 @@ struct nouveau_pgraph_engine { + uint32_t size, uint32_t pitch); + }; + ++struct nouveau_display_engine { ++ int (*early_init)(struct drm_device *); ++ void (*late_takedown)(struct drm_device *); ++ int (*create)(struct drm_device *); ++ int (*init)(struct drm_device *); ++ void (*destroy)(struct drm_device *); ++}; ++ ++struct nouveau_gpio_engine { ++ int (*init)(struct drm_device *); ++ void (*takedown)(struct drm_device *); ++ ++ int (*get)(struct drm_device *, enum dcb_gpio_tag); ++ int (*set)(struct drm_device *, enum dcb_gpio_tag, int state); ++ ++ void (*irq_enable)(struct drm_device *, enum dcb_gpio_tag, bool on); ++}; ++ + struct nouveau_engine { + struct nouveau_instmem_engine instmem; + struct nouveau_mc_engine mc; +@@ -365,6 +366,8 @@ struct nouveau_engine { + struct nouveau_fb_engine fb; + struct nouveau_pgraph_engine graph; + struct nouveau_fifo_engine fifo; ++ struct nouveau_display_engine display; ++ struct nouveau_gpio_engine gpio; + }; + + struct nouveau_pll_vals { +@@ -397,7 +400,7 @@ enum nv04_fp_display_regs { + + struct nv04_crtc_reg { + unsigned char MiscOutReg; /* */ +- uint8_t CRTC[0x9f]; ++ uint8_t CRTC[0xa0]; + uint8_t CR58[0x10]; + uint8_t Sequencer[5]; + uint8_t Graphics[9]; +@@ -496,15 +499,11 @@ enum nouveau_card_type { + NV_30 = 0x30, + NV_40 = 0x40, + NV_50 = 0x50, ++ NV_C0 = 0xc0, + }; struct drm_nouveau_private { struct drm_device *dev; @@ -2288,7 +4860,28 @@ index c697191..20ca5b8 100644 /* the card type, takes NV_* as values */ enum nouveau_card_type card_type; -@@ -533,8 +521,6 @@ struct drm_nouveau_private { +@@ -513,8 +512,14 @@ struct drm_nouveau_private { + int flags; + + void __iomem *mmio; ++ ++ spinlock_t ramin_lock; + void __iomem *ramin; +- uint32_t ramin_size; ++ u32 ramin_size; ++ u32 ramin_base; ++ bool ramin_available; ++ struct drm_mm ramin_heap; ++ struct list_head gpuobj_list; + + struct nouveau_bo *vga_ram; + +@@ -528,13 +533,9 @@ struct drm_nouveau_private { + 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; @@ -2297,7 +4890,44 @@ index c697191..20ca5b8 100644 int fifo_alloc_count; struct nouveau_channel *fifos[NOUVEAU_MAX_CHANNEL_NR]; -@@ -595,11 +581,7 @@ struct drm_nouveau_private { +@@ -545,15 +546,11 @@ struct drm_nouveau_private { + spinlock_t context_switch_lock; + + /* RAMIN configuration, RAMFC, RAMHT and RAMRO offsets */ +- struct nouveau_gpuobj *ramht; ++ struct nouveau_ramht *ramht; ++ struct nouveau_gpuobj *ramfc; ++ struct nouveau_gpuobj *ramro; ++ + 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; + + struct { + enum { +@@ -571,14 +568,12 @@ struct drm_nouveau_private { + } gart_info; + + /* nv10-nv40 tiling regions */ +- struct { +- struct nouveau_tile_reg reg[NOUVEAU_MAX_TILE_NR]; +- spinlock_t lock; +- } tile; ++ struct nouveau_tile_reg tile[NOUVEAU_MAX_TILE_NR]; + + /* VRAM/fb configuration */ + uint64_t vram_size; + uint64_t vram_sys_base; ++ u32 vram_rblock_size; + + uint64_t fb_phys; + uint64_t fb_available_size; +@@ -595,14 +590,6 @@ struct drm_nouveau_private { struct nouveau_gpuobj *vm_vram_pt[NV50_VM_VRAM_NR]; int vm_vram_pt_nr; @@ -2306,11 +4936,13 @@ index c697191..20ca5b8 100644 - /* context table pointed to be NV_PGRAPH_CHANNEL_CTX_TABLE (0x400780) */ - uint32_t ctx_table_size; - struct nouveau_gpuobj_ref *ctx_table; -+ struct drm_mm ramin_heap; +- +- struct list_head gpuobj_list; +- + struct nvbios vbios; - struct list_head gpuobj_list; - -@@ -618,6 +600,11 @@ struct drm_nouveau_private { + struct nv04_mode_state mode_reg; +@@ -618,6 +605,11 @@ struct drm_nouveau_private { struct backlight_device *backlight; struct nouveau_channel *evo; @@ -2322,7 +4954,7 @@ index c697191..20ca5b8 100644 struct { struct dentry *channel_root; -@@ -652,14 +639,6 @@ nouveau_bo_ref(struct nouveau_bo *ref, struct nouveau_bo **pnvbo) +@@ -652,14 +644,6 @@ nouveau_bo_ref(struct nouveau_bo *ref, struct nouveau_bo **pnvbo) return 0; } @@ -2337,7 +4969,16 @@ index c697191..20ca5b8 100644 #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))) { \ -@@ -682,7 +661,6 @@ extern int nouveau_tv_disable; +@@ -671,7 +655,7 @@ nouveau_bo_ref(struct nouveau_bo *ref, struct nouveau_bo **pnvbo) + } while (0) + + /* nouveau_drv.c */ +-extern int nouveau_noagp; ++extern int nouveau_agpmode; + extern int nouveau_duallink; + extern int nouveau_uscript_lvds; + extern int nouveau_uscript_tmds; +@@ -682,10 +666,10 @@ extern int nouveau_tv_disable; extern char *nouveau_tv_norm; extern int nouveau_reg_debug; extern char *nouveau_vbios; @@ -2345,7 +4986,11 @@ index c697191..20ca5b8 100644 extern int nouveau_ignorelid; extern int nouveau_nofbaccel; extern int nouveau_noaccel; -@@ -707,15 +685,7 @@ extern bool nouveau_wait_for_idle(struct drm_device *); ++extern int nouveau_force_post; + extern int nouveau_override_conntype; + + extern int nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state); +@@ -707,17 +691,12 @@ extern bool nouveau_wait_for_idle(struct drm_device *); extern int nouveau_card_init(struct drm_device *); /* nouveau_mem.c */ @@ -2356,12 +5001,57 @@ index c697191..20ca5b8 100644 - struct drm_file *, int tail); -extern void nouveau_mem_takedown(struct mem_block **heap); -extern void nouveau_mem_free_block(struct mem_block *); - extern int nouveau_mem_detect(struct drm_device *dev); +-extern int nouveau_mem_detect(struct drm_device *dev); -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(struct drm_device *); ++extern int nouveau_mem_vram_init(struct drm_device *); ++extern void nouveau_mem_vram_fini(struct drm_device *); ++extern int nouveau_mem_gart_init(struct drm_device *); ++extern void nouveau_mem_gart_fini(struct drm_device *); extern int nouveau_mem_init_agp(struct drm_device *); ++extern int nouveau_mem_reset_agp(struct drm_device *); extern void nouveau_mem_close(struct drm_device *); -@@ -857,11 +827,13 @@ void nouveau_register_dsm_handler(void); + extern struct nouveau_tile_reg *nv10_mem_set_tiling(struct drm_device *dev, + uint32_t addr, +@@ -759,7 +738,6 @@ extern void nouveau_channel_free(struct nouveau_channel *); + 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); +@@ -769,24 +747,11 @@ 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 void nouveau_gpuobj_ref(struct nouveau_gpuobj *, ++ struct nouveau_gpuobj **); ++extern int nouveau_gpuobj_new_fake(struct drm_device *, u32 pinst, u64 vinst, ++ u32 size, u32 flags, ++ struct nouveau_gpuobj **); + extern int nouveau_gpuobj_dma_new(struct nouveau_channel *, int class, + uint64_t offset, uint64_t size, int access, + int target, struct nouveau_gpuobj **); +@@ -857,11 +822,13 @@ void nouveau_register_dsm_handler(void); void nouveau_unregister_dsm_handler(void); int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len); bool nouveau_acpi_rom_supported(struct pci_dev *pdev); @@ -2371,11 +5061,63 @@ index c697191..20ca5b8 100644 static inline void nouveau_unregister_dsm_handler(void) {} static inline bool nouveau_acpi_rom_supported(struct pci_dev *pdev) { return false; } static inline int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len) { return -EINVAL; } -+static inline int nouveau_acpi_edid(struct drm_device *, struct drm_connector *) { return -EINVAL; } ++static inline int nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector) { return -EINVAL; } #endif /* nouveau_backlight.c */ -@@ -1035,12 +1007,6 @@ extern int nv50_graph_unload_context(struct drm_device *); +@@ -924,22 +891,29 @@ extern void nv10_fb_takedown(struct drm_device *); + extern void nv10_fb_set_region_tiling(struct drm_device *, int, uint32_t, + uint32_t, uint32_t); + ++/* nv30_fb.c */ ++extern int nv30_fb_init(struct drm_device *); ++extern void nv30_fb_takedown(struct drm_device *); ++ + /* nv40_fb.c */ + extern int nv40_fb_init(struct drm_device *); + extern void nv40_fb_takedown(struct drm_device *); + extern void nv40_fb_set_region_tiling(struct drm_device *, int, uint32_t, + uint32_t, uint32_t); +- + /* nv50_fb.c */ + extern int nv50_fb_init(struct drm_device *); + extern void nv50_fb_takedown(struct drm_device *); ++extern void nv50_fb_vm_trap(struct drm_device *, int display, const char *); ++ ++/* nvc0_fb.c */ ++extern int nvc0_fb_init(struct drm_device *); ++extern void nvc0_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 bool nv04_fifo_cache_flush(struct drm_device *); + extern bool nv04_fifo_cache_pull(struct drm_device *, bool); + extern int nv04_fifo_channel_id(struct drm_device *); + extern int nv04_fifo_create_context(struct nouveau_channel *); +@@ -971,6 +945,19 @@ 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 *); + ++/* nvc0_fifo.c */ ++extern int nvc0_fifo_init(struct drm_device *); ++extern void nvc0_fifo_takedown(struct drm_device *); ++extern void nvc0_fifo_disable(struct drm_device *); ++extern void nvc0_fifo_enable(struct drm_device *); ++extern bool nvc0_fifo_reassign(struct drm_device *, bool); ++extern bool nvc0_fifo_cache_pull(struct drm_device *, bool); ++extern int nvc0_fifo_channel_id(struct drm_device *); ++extern int nvc0_fifo_create_context(struct nouveau_channel *); ++extern void nvc0_fifo_destroy_context(struct nouveau_channel *); ++extern int nvc0_fifo_load_context(struct nouveau_channel *); ++extern int nvc0_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 *); +@@ -1035,11 +1022,15 @@ extern int nv50_graph_unload_context(struct drm_device *); extern void nv50_graph_context_switch(struct drm_device *); extern int nv50_grctx_init(struct nouveau_grctx *); @@ -2384,11 +5126,19 @@ index c697191..20ca5b8 100644 -extern void nouveau_grctx_vals_load(struct drm_device *, - struct nouveau_gpuobj *); -extern void nouveau_grctx_fini(struct drm_device *); -- ++/* nvc0_graph.c */ ++extern int nvc0_graph_init(struct drm_device *); ++extern void nvc0_graph_takedown(struct drm_device *); ++extern void nvc0_graph_fifo_access(struct drm_device *, bool); ++extern struct nouveau_channel *nvc0_graph_channel(struct drm_device *); ++extern int nvc0_graph_create_context(struct nouveau_channel *); ++extern void nvc0_graph_destroy_context(struct nouveau_channel *); ++extern int nvc0_graph_load_context(struct nouveau_channel *); ++extern int nvc0_graph_unload_context(struct drm_device *); + /* nv04_instmem.c */ extern int nv04_instmem_init(struct drm_device *); - extern void nv04_instmem_takedown(struct drm_device *); -@@ -1051,8 +1017,7 @@ extern int nv04_instmem_populate(struct drm_device *, struct nouveau_gpuobj *, +@@ -1051,8 +1042,7 @@ extern int nv04_instmem_populate(struct drm_device *, struct nouveau_gpuobj *, 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 *); @@ -2398,7 +5148,7 @@ index c697191..20ca5b8 100644 /* nv50_instmem.c */ extern int nv50_instmem_init(struct drm_device *); -@@ -1064,8 +1029,9 @@ extern int nv50_instmem_populate(struct drm_device *, struct nouveau_gpuobj *, +@@ -1064,8 +1054,21 @@ extern int nv50_instmem_populate(struct drm_device *, struct nouveau_gpuobj *, 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 *); @@ -2407,10 +5157,22 @@ index c697191..20ca5b8 100644 +extern void nv50_instmem_flush(struct drm_device *); +extern void nv84_instmem_flush(struct drm_device *); +extern void nv50_vm_flush(struct drm_device *, int engine); ++ ++/* nvc0_instmem.c */ ++extern int nvc0_instmem_init(struct drm_device *); ++extern void nvc0_instmem_takedown(struct drm_device *); ++extern int nvc0_instmem_suspend(struct drm_device *); ++extern void nvc0_instmem_resume(struct drm_device *); ++extern int nvc0_instmem_populate(struct drm_device *, struct nouveau_gpuobj *, ++ uint32_t *size); ++extern void nvc0_instmem_clear(struct drm_device *, struct nouveau_gpuobj *); ++extern int nvc0_instmem_bind(struct drm_device *, struct nouveau_gpuobj *); ++extern int nvc0_instmem_unbind(struct drm_device *, struct nouveau_gpuobj *); ++extern void nvc0_instmem_flush(struct drm_device *); /* nv04_mc.c */ extern int nv04_mc_init(struct drm_device *); -@@ -1088,13 +1054,14 @@ extern long nouveau_compat_ioctl(struct file *file, unsigned int cmd, +@@ -1088,13 +1091,14 @@ extern long nouveau_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg); /* nv04_dac.c */ @@ -2427,7 +5189,7 @@ index c697191..20ca5b8 100644 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); -@@ -1103,10 +1070,10 @@ extern void nv04_dfp_update_fp_control(struct drm_encoder *encoder, int mode); +@@ -1103,15 +1107,17 @@ 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); @@ -2439,8 +5201,16 @@ index c697191..20ca5b8 100644 +extern int nv17_tv_create(struct drm_connector *, struct dcb_entry *); /* nv04_display.c */ ++extern int nv04_display_early_init(struct drm_device *); ++extern void nv04_display_late_takedown(struct drm_device *); extern int nv04_display_create(struct drm_device *); -@@ -1147,7 +1114,6 @@ extern int nouveau_fence_wait(void *obj, void *arg, bool lazy, bool intr); ++extern int nv04_display_init(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); +@@ -1148,7 +1154,6 @@ 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); @@ -2448,8 +5218,86 @@ index c697191..20ca5b8 100644 /* nouveau_gem.c */ extern int nouveau_gem_new(struct drm_device *, struct nouveau_channel *, +@@ -1168,13 +1173,15 @@ extern int nouveau_gem_ioctl_cpu_fini(struct drm_device *, void *, + 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); ++/* nv10_gpio.c */ ++int nv10_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag); ++int nv10_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state); + + /* nv50_gpio.c */ ++int nv50_gpio_init(struct drm_device *dev); + int nv50_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag); + int nv50_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state); ++void nv50_gpio_irq_enable(struct drm_device *, enum dcb_gpio_tag, bool on); + + /* nv50_calc. */ + int nv50_calc_pll(struct drm_device *, struct pll_lims *, int clk, +@@ -1221,6 +1228,13 @@ static inline void nv_wr32(struct drm_device *dev, unsigned reg, u32 val) + iowrite32_native(val, dev_priv->mmio + reg); + } + ++static inline u32 nv_mask(struct drm_device *dev, u32 reg, u32 mask, u32 val) ++{ ++ u32 tmp = nv_rd32(dev, reg); ++ nv_wr32(dev, reg, (tmp & ~mask) | val); ++ return tmp; ++} ++ + static inline u8 nv_rd08(struct drm_device *dev, unsigned reg) + { + struct drm_nouveau_private *dev_priv = dev->dev_private; +@@ -1233,7 +1247,7 @@ static inline void nv_wr08(struct drm_device *dev, unsigned reg, u8 val) + iowrite8(val, dev_priv->mmio + reg); + } + +-#define nv_wait(reg, mask, val) \ ++#define nv_wait(dev, reg, mask, val) \ + nouveau_wait_until(dev, 2000000000ULL, (reg), (mask), (val)) + + /* PRAMIN access */ +@@ -1250,17 +1264,8 @@ static inline void nv_wi32(struct drm_device *dev, unsigned offset, u32 val) + } + + /* 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); +-} ++extern u32 nv_ro32(struct nouveau_gpuobj *, u32 offset); ++extern void nv_wo32(struct nouveau_gpuobj *, u32 offset, u32 val); + + /* + * Logging +@@ -1347,6 +1352,15 @@ nv_two_reg_pll(struct drm_device *dev) + return false; + } + ++static inline bool ++nv_match_device(struct drm_device *dev, unsigned device, ++ unsigned sub_vendor, unsigned sub_device) ++{ ++ return dev->pdev->device == device && ++ dev->pdev->subsystem_vendor == sub_vendor && ++ dev->pdev->subsystem_device == sub_device; ++} ++ + #define NV_SW 0x0000506e + #define NV_SW_DMA_SEMAPHORE 0x00000060 + #define NV_SW_SEMAPHORE_OFFSET 0x00000064 diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h -index e1df820..a1a0d48 100644 +index e1df820..ae69b61 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -38,13 +38,15 @@ struct nouveau_encoder { @@ -2470,8 +5318,24 @@ index e1df820..a1a0d48 100644 union { struct { int mc_unknown; -@@ -71,8 +73,8 @@ static inline struct drm_encoder *to_drm_encoder(struct nouveau_encoder *enc) +@@ -53,6 +55,7 @@ struct nouveau_encoder { + int dpcd_version; + int link_nr; + int link_bw; ++ bool enhanced_frame; + } dp; + }; + }; +@@ -69,10 +72,16 @@ static inline struct drm_encoder *to_drm_encoder(struct nouveau_encoder *enc) + return &enc->base.base; + } ++static inline struct drm_encoder_slave_funcs * ++get_slave_funcs(struct drm_encoder *enc) ++{ ++ return to_encoder_slave(enc)->slave_funcs; ++} ++ struct nouveau_connector * nouveau_encoder_connector_get(struct nouveau_encoder *encoder); -int nv50_sor_create(struct drm_device *dev, struct dcb_entry *entry); @@ -2482,10 +5346,19 @@ index e1df820..a1a0d48 100644 struct bit_displayport_encoder_table { uint32_t match; diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c -index 257ea13..2fb2444 100644 +index 257ea13..11f13fc 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c -@@ -333,7 +333,7 @@ nouveau_fbcon_output_poll_changed(struct drm_device *dev) +@@ -280,6 +280,8 @@ nouveau_fbcon_create(struct nouveau_fbdev *nfbdev, + + if (dev_priv->channel && !nouveau_nofbaccel) { + switch (dev_priv->card_type) { ++ case NV_C0: ++ break; + case NV_50: + nv50_fbcon_accel_init(info); + info->fbops = &nv50_fbcon_ops; +@@ -333,7 +335,7 @@ nouveau_fbcon_output_poll_changed(struct drm_device *dev) drm_fb_helper_hotplug_event(&dev_priv->nfbdev->helper); } @@ -2495,7 +5368,7 @@ index 257ea13..2fb2444 100644 { struct nouveau_framebuffer *nouveau_fb = &nfbdev->nouveau_fb; diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c -index faddf53..813d853 100644 +index faddf53..6b208ff 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.c +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c @@ -67,12 +67,13 @@ nouveau_fence_update(struct nouveau_channel *chan) @@ -2566,6 +5439,24 @@ index faddf53..813d853 100644 return fence->signalled; } +@@ -190,8 +186,6 @@ 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; +@@ -201,6 +195,8 @@ nouveau_fence_wait(void *sync_obj, void *sync_arg, bool lazy, bool intr) + break; + } + ++ __set_current_state(intr ? TASK_INTERRUPTIBLE ++ : TASK_UNINTERRUPTIBLE); + if (lazy) + schedule_timeout(1); + @@ -221,27 +217,12 @@ nouveau_fence_flush(void *sync_obj, void *sync_arg) return 0; } @@ -2596,7 +5487,7 @@ index faddf53..813d853 100644 } diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c -index 69c76cf..547f2c2 100644 +index 6937d53..613f878 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -137,8 +137,6 @@ nouveau_gem_ioctl_new(struct drm_device *dev, void *data, @@ -2608,7 +5499,7 @@ index 69c76cf..547f2c2 100644 if (unlikely(dev_priv->ttm.bdev.dev_mapping == NULL)) dev_priv->ttm.bdev.dev_mapping = dev_priv->dev->dev_mapping; -@@ -577,10 +575,9 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data, +@@ -578,10 +576,9 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data, struct drm_nouveau_gem_pushbuf_bo *bo; struct nouveau_channel *chan; struct validate_op op; @@ -2620,7 +5511,25 @@ index 69c76cf..547f2c2 100644 NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(req->channel, file_priv, chan); req->vram_available = dev_priv->fb_aper_free; -@@ -760,8 +757,6 @@ nouveau_gem_ioctl_cpu_prep(struct drm_device *dev, void *data, +@@ -666,7 +663,7 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data, + push[i].length); + } + } else +- if (dev_priv->card_type >= NV_20) { ++ if (dev_priv->chipset >= 0x25) { + ret = RING_SPACE(chan, req->nr_push * 2); + if (ret) { + NV_ERROR(dev, "cal_space: %d\n", ret); +@@ -741,7 +738,7 @@ out_next: + req->suffix0 = 0x00000000; + req->suffix1 = 0x00000000; + } else +- if (dev_priv->card_type >= NV_20) { ++ if (dev_priv->chipset >= 0x25) { + req->suffix0 = 0x00020000; + req->suffix1 = 0x00000000; + } else { +@@ -776,8 +773,6 @@ nouveau_gem_ioctl_cpu_prep(struct drm_device *dev, void *data, bool no_wait = !!(req->flags & NOUVEAU_GEM_CPU_PREP_NOWAIT); int ret = -EINVAL; @@ -2629,7 +5538,7 @@ index 69c76cf..547f2c2 100644 gem = drm_gem_object_lookup(dev, file_priv, req->handle); if (!gem) return ret; -@@ -800,8 +795,6 @@ nouveau_gem_ioctl_cpu_fini(struct drm_device *dev, void *data, +@@ -816,8 +811,6 @@ nouveau_gem_ioctl_cpu_fini(struct drm_device *dev, void *data, struct nouveau_bo *nvbo; int ret = -EINVAL; @@ -2638,7 +5547,7 @@ index 69c76cf..547f2c2 100644 gem = drm_gem_object_lookup(dev, file_priv, req->handle); if (!gem) return ret; -@@ -827,8 +820,6 @@ nouveau_gem_ioctl_info(struct drm_device *dev, void *data, +@@ -843,8 +836,6 @@ nouveau_gem_ioctl_info(struct drm_device *dev, void *data, struct drm_gem_object *gem; int ret; @@ -2813,23 +5722,162 @@ index f731c5f..0000000 - nv_wo32(dev, ctx, le32_to_cpu(cv->data[i].offset), - le32_to_cpu(cv->data[i].value)); -} +diff --git a/drivers/gpu/drm/nouveau/nouveau_grctx.h b/drivers/gpu/drm/nouveau/nouveau_grctx.h +index 5d39c4c..4a8ad13 100644 +--- a/drivers/gpu/drm/nouveau/nouveau_grctx.h ++++ b/drivers/gpu/drm/nouveau/nouveau_grctx.h +@@ -126,7 +126,7 @@ gr_def(struct nouveau_grctx *ctx, uint32_t reg, uint32_t val) + reg = (reg - 0x00400000) / 4; + reg = (reg - ctx->ctxprog_reg) + ctx->ctxvals_base; + +- nv_wo32(ctx->dev, ctx->data, reg, val); ++ nv_wo32(ctx->data, reg * 4, val); + } + #endif + +diff --git a/drivers/gpu/drm/nouveau/nouveau_hw.c b/drivers/gpu/drm/nouveau/nouveau_hw.c +index 7855b35..cb13134 100644 +--- a/drivers/gpu/drm/nouveau/nouveau_hw.c ++++ b/drivers/gpu/drm/nouveau/nouveau_hw.c +@@ -305,7 +305,7 @@ setPLL_double_lowregs(struct drm_device *dev, uint32_t NMNMreg, + 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))) | ++ uint32_t Pval = (oldPval & (mpll ? ~(0x77 << 16) : ~(7 << 16))) | + 0xc << 28 | pv->log2P << 16; + uint32_t saved4600 = 0; + /* some cards have different maskc040s */ +@@ -865,8 +865,13 @@ nv_save_state_ext(struct drm_device *dev, int head, + 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) ++ ++ if (dev_priv->card_type >= NV_20) + rd_cio_state(dev, head, regp, NV_CIO_CRE_47); ++ ++ if (dev_priv->card_type >= NV_30) ++ rd_cio_state(dev, head, regp, 0x9f); ++ + 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); +@@ -971,9 +976,13 @@ nv_load_state_ext(struct drm_device *dev, int head, + 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) ++ ++ if (dev_priv->card_type >= NV_20) + wr_cio_state(dev, head, regp, NV_CIO_CRE_47); + ++ if (dev_priv->card_type >= NV_30) ++ wr_cio_state(dev, head, regp, 0x9f); ++ + 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); diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c -index 316a3c7..97ba89e 100644 +index 316a3c7..8461485 100644 --- a/drivers/gpu/drm/nouveau/nouveau_i2c.c +++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c -@@ -278,3 +278,37 @@ nouveau_i2c_find(struct drm_device *dev, int index) +@@ -163,7 +163,7 @@ nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index) + if (entry->chan) + return -EEXIST; + +- if (dev_priv->card_type == NV_50 && entry->read >= NV50_I2C_PORTS) { ++ if (dev_priv->card_type >= NV_50 && entry->read >= NV50_I2C_PORTS) { + NV_ERROR(dev, "unknown i2c port %d\n", entry->read); + return -EINVAL; + } +@@ -174,26 +174,26 @@ nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index) + + 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->bit.setsda = nv04_i2c_setsda; ++ i2c->bit.setscl = nv04_i2c_setscl; ++ i2c->bit.getsda = nv04_i2c_getsda; ++ i2c->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->bit.setsda = nv4e_i2c_setsda; ++ i2c->bit.setscl = nv4e_i2c_setscl; ++ i2c->bit.getsda = nv4e_i2c_getsda; ++ i2c->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->bit.setsda = nv50_i2c_setsda; ++ i2c->bit.setscl = nv50_i2c_setscl; ++ i2c->bit.getsda = nv50_i2c_getsda; ++ i2c->bit.getscl = nv50_i2c_getscl; + i2c->rd = nv50_i2c_port[entry->read]; + i2c->wr = i2c->rd; + break; +@@ -216,17 +216,14 @@ nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index) + 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; ++ i2c->adapter.algo_data = &i2c->bit; ++ i2c->bit.udelay = 40; ++ i2c->bit.timeout = usecs_to_jiffies(5000); ++ i2c->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); ++ i2c->adapter.algo = &nouveau_dp_i2c_algo; ++ ret = i2c_add_adapter(&i2c->adapter); + } + + if (ret) { +@@ -278,3 +275,45 @@ nouveau_i2c_find(struct drm_device *dev, int index) return i2c->chan; } +bool +nouveau_probe_i2c_addr(struct nouveau_i2c_chan *i2c, int addr) +{ -+ struct i2c_msg msg = { -+ .addr = addr, -+ .len = 0, ++ uint8_t buf[] = { 0 }; ++ struct i2c_msg msgs[] = { ++ { ++ .addr = addr, ++ .flags = 0, ++ .len = 1, ++ .buf = buf, ++ }, ++ { ++ .addr = addr, ++ .flags = I2C_M_RD, ++ .len = 1, ++ .buf = buf, ++ } + }; + -+ return i2c_transfer(&i2c->adapter, &msg, 1) == 1; ++ return i2c_transfer(&i2c->adapter, msgs, 2) == 2; +} + +int @@ -2837,29 +5885,38 @@ index 316a3c7..97ba89e 100644 + struct i2c_board_info *info, int index) +{ + struct nouveau_i2c_chan *i2c = nouveau_i2c_find(dev, index); -+ int was_locked, i; ++ int i; + -+ was_locked = NVLockVgaCrtcs(dev, false); + NV_DEBUG(dev, "Probing %ss on I2C bus: %d\n", what, index); + + for (i = 0; info[i].addr; i++) { + if (nouveau_probe_i2c_addr(i2c, info[i].addr)) { + NV_INFO(dev, "Detected %s: %s\n", what, info[i].type); -+ goto out; ++ return i; + } + } + + NV_DEBUG(dev, "No devices found.\n"); -+out: -+ NVLockVgaCrtcs(dev, was_locked); + -+ return info[i].addr ? i : -ENODEV; ++ return -ENODEV; +} diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.h b/drivers/gpu/drm/nouveau/nouveau_i2c.h -index c8eaf7a..6dd2f87 100644 +index c8eaf7a..f71cb32 100644 --- a/drivers/gpu/drm/nouveau/nouveau_i2c.h +++ b/drivers/gpu/drm/nouveau/nouveau_i2c.h -@@ -45,6 +45,9 @@ struct nouveau_i2c_chan { +@@ -33,10 +33,7 @@ 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; ++ struct i2c_algo_bit_data bit; + unsigned rd; + unsigned wr; + unsigned data; +@@ -45,8 +42,10 @@ struct nouveau_i2c_chan { 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); @@ -2867,10 +5924,288 @@ index c8eaf7a..6dd2f87 100644 +int nouveau_i2c_identify(struct drm_device *dev, const char *what, + struct i2c_board_info *info, int index); - int nouveau_dp_i2c_aux_ch(struct i2c_adapter *, int mode, uint8_t write_byte, - uint8_t *read_byte); +-int nouveau_dp_i2c_aux_ch(struct i2c_adapter *, int mode, uint8_t write_byte, +- uint8_t *read_byte); ++extern const struct i2c_algorithm nouveau_dp_i2c_algo; + + #endif /* __NOUVEAU_I2C_H__ */ +diff --git a/drivers/gpu/drm/nouveau/nouveau_irq.c b/drivers/gpu/drm/nouveau/nouveau_irq.c +index 53360f1..6fd51a5 100644 +--- a/drivers/gpu/drm/nouveau/nouveau_irq.c ++++ b/drivers/gpu/drm/nouveau/nouveau_irq.c +@@ -35,6 +35,7 @@ + #include "nouveau_drm.h" + #include "nouveau_drv.h" + #include "nouveau_reg.h" ++#include "nouveau_ramht.h" + #include + + /* needed for hotplug irq */ +@@ -49,7 +50,7 @@ nouveau_irq_preinstall(struct drm_device *dev) + /* Master disable */ + nv_wr32(dev, NV03_PMC_INTR_EN_0, 0); + +- if (dev_priv->card_type == NV_50) { ++ if (dev_priv->card_type >= NV_50) { + INIT_WORK(&dev_priv->irq_work, nv50_display_irq_handler_bh); + INIT_WORK(&dev_priv->hpd_work, nv50_display_irq_hotplug_bh); + INIT_LIST_HEAD(&dev_priv->vbl_waiting); +@@ -106,15 +107,16 @@ nouveau_fifo_swmthd(struct nouveau_channel *chan, uint32_t addr, uint32_t data) + const int mthd = addr & 0x1ffc; + + if (mthd == 0x0000) { +- struct nouveau_gpuobj_ref *ref = NULL; ++ struct nouveau_gpuobj *gpuobj; + +- if (nouveau_gpuobj_ref_find(chan, data, &ref)) ++ gpuobj = nouveau_ramht_find(chan, data); ++ if (!gpuobj) + return false; + +- if (ref->gpuobj->engine != NVOBJ_ENGINE_SW) ++ if (gpuobj->engine != NVOBJ_ENGINE_SW) + return false; + +- chan->sw_subchannel[subc] = ref->gpuobj->class; ++ chan->sw_subchannel[subc] = gpuobj->class; + nv_wr32(dev, NV04_PFIFO_CACHE1_ENGINE, nv_rd32(dev, + NV04_PFIFO_CACHE1_ENGINE) & ~(0xf << subc * 4)); + return true; +@@ -200,16 +202,45 @@ nouveau_fifo_irq_handler(struct drm_device *dev) + } + + if (status & NV_PFIFO_INTR_DMA_PUSHER) { +- NV_INFO(dev, "PFIFO_DMA_PUSHER - Ch %d\n", chid); ++ u32 get = nv_rd32(dev, 0x003244); ++ u32 put = nv_rd32(dev, 0x003240); ++ u32 push = nv_rd32(dev, 0x003220); ++ u32 state = nv_rd32(dev, 0x003228); ++ ++ if (dev_priv->card_type == NV_50) { ++ u32 ho_get = nv_rd32(dev, 0x003328); ++ u32 ho_put = nv_rd32(dev, 0x003320); ++ u32 ib_get = nv_rd32(dev, 0x003334); ++ u32 ib_put = nv_rd32(dev, 0x003330); ++ ++ NV_INFO(dev, "PFIFO_DMA_PUSHER - Ch %d Get 0x%02x%08x " ++ "Put 0x%02x%08x IbGet 0x%08x IbPut 0x%08x " ++ "State 0x%08x Push 0x%08x\n", ++ chid, ho_get, get, ho_put, put, ib_get, ib_put, ++ state, push); ++ ++ /* METHOD_COUNT, in DMA_STATE on earlier chipsets */ ++ nv_wr32(dev, 0x003364, 0x00000000); ++ if (get != put || ho_get != ho_put) { ++ nv_wr32(dev, 0x003244, put); ++ nv_wr32(dev, 0x003328, ho_put); ++ } else ++ if (ib_get != ib_put) { ++ nv_wr32(dev, 0x003334, ib_put); ++ } ++ } else { ++ NV_INFO(dev, "PFIFO_DMA_PUSHER - Ch %d Get 0x%08x " ++ "Put 0x%08x State 0x%08x Push 0x%08x\n", ++ chid, get, put, state, push); + +- status &= ~NV_PFIFO_INTR_DMA_PUSHER; +- nv_wr32(dev, NV03_PFIFO_INTR_0, +- NV_PFIFO_INTR_DMA_PUSHER); ++ if (get != put) ++ nv_wr32(dev, 0x003244, put); ++ } + +- 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); ++ nv_wr32(dev, 0x003228, 0x00000000); ++ nv_wr32(dev, 0x003220, 0x00000001); ++ nv_wr32(dev, 0x002100, NV_PFIFO_INTR_DMA_PUSHER); ++ status &= ~NV_PFIFO_INTR_DMA_PUSHER; + } + + if (status & NV_PFIFO_INTR_SEMAPHORE) { +@@ -226,6 +257,14 @@ nouveau_fifo_irq_handler(struct drm_device *dev) + nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, 1); + } + ++ if (dev_priv->card_type == NV_50) { ++ if (status & 0x00000010) { ++ nv50_fb_vm_trap(dev, 1, "PFIFO_BAR_FAULT"); ++ status &= ~0x00000010; ++ nv_wr32(dev, 0x002100, 0x00000010); ++ } ++ } ++ + if (status) { + NV_INFO(dev, "PFIFO_INTR 0x%08x - Ch %d\n", + status, chid); +@@ -357,7 +396,7 @@ nouveau_graph_chid_from_grctx(struct drm_device *dev) + if (!chan || !chan->ramin_grctx) + continue; + +- if (inst == chan->ramin_grctx->instance) ++ if (inst == chan->ramin_grctx->pinst) + break; + } + } else { +@@ -369,7 +408,7 @@ nouveau_graph_chid_from_grctx(struct drm_device *dev) + if (!chan || !chan->ramin) + continue; + +- if (inst == chan->ramin->instance) ++ if (inst == chan->ramin->vinst) + break; + } + } +@@ -586,11 +625,11 @@ nouveau_pgraph_irq_handler(struct drm_device *dev) + } + + 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); ++ ++ nouveau_pgraph_intr_context_switch(dev); + } + + if (status) { +@@ -605,40 +644,6 @@ nouveau_pgraph_irq_handler(struct drm_device *dev) + nv_wr32(dev, NV03_PMC_INTR_0, NV_PMC_INTR_0_PGRAPH_PENDING); + } + +-static void +-nv50_pfb_vm_trap(struct drm_device *dev, int display, const char *name) +-{ +- struct drm_nouveau_private *dev_priv = dev->dev_private; +- uint32_t trap[6]; +- int i, ch; +- uint32_t idx = nv_rd32(dev, 0x100c90); +- if (idx & 0x80000000) { +- idx &= 0xffffff; +- if (display) { +- for (i = 0; i < 6; i++) { +- nv_wr32(dev, 0x100c90, idx | i << 24); +- trap[i] = nv_rd32(dev, 0x100c94); +- } +- for (ch = 0; ch < dev_priv->engine.fifo.channels; ch++) { +- struct nouveau_channel *chan = dev_priv->fifos[ch]; +- +- if (!chan || !chan->ramin) +- continue; +- +- if (trap[1] == chan->ramin->instance >> 12) +- break; +- } +- NV_INFO(dev, "%s - VM: Trapped %s at %02x%04x%04x status %08x %08x channel %d\n", +- name, (trap[5]&0x100?"read":"write"), +- trap[5]&0xff, trap[4]&0xffff, +- trap[3]&0xffff, trap[0], trap[2], ch); +- } +- nv_wr32(dev, 0x100c90, idx | 0x80000000); +- } else if (display) { +- NV_INFO(dev, "%s - no VM fault?\n", name); +- } +-} +- + static struct nouveau_enum_names nv50_mp_exec_error_names[] = + { + { 3, "STACK_UNDERFLOW" }, +@@ -711,7 +716,7 @@ nv50_pgraph_tp_trap(struct drm_device *dev, int type, uint32_t ustatus_old, + tps++; + switch (type) { + case 6: /* texture error... unknown for now */ +- nv50_pfb_vm_trap(dev, display, name); ++ nv50_fb_vm_trap(dev, display, name); + if (display) { + NV_ERROR(dev, "magic set %d:\n", i); + for (r = ustatus_addr + 4; r <= ustatus_addr + 0x10; r += 4) +@@ -734,7 +739,7 @@ nv50_pgraph_tp_trap(struct drm_device *dev, int type, uint32_t ustatus_old, + uint32_t e1c = nv_rd32(dev, ustatus_addr + 0x14); + uint32_t e20 = nv_rd32(dev, ustatus_addr + 0x18); + uint32_t e24 = nv_rd32(dev, ustatus_addr + 0x1c); +- nv50_pfb_vm_trap(dev, display, name); ++ nv50_fb_vm_trap(dev, display, name); + /* 2d engine destination */ + if (ustatus & 0x00000010) { + if (display) { +@@ -817,7 +822,7 @@ nv50_pgraph_trap_handler(struct drm_device *dev) + + /* Known to be triggered by screwed up NOTIFY and COND... */ + if (ustatus & 0x00000001) { +- nv50_pfb_vm_trap(dev, display, "PGRAPH_TRAP_DISPATCH_FAULT"); ++ nv50_fb_vm_trap(dev, display, "PGRAPH_TRAP_DISPATCH_FAULT"); + nv_wr32(dev, 0x400500, 0); + if (nv_rd32(dev, 0x400808) & 0x80000000) { + if (display) { +@@ -842,7 +847,7 @@ nv50_pgraph_trap_handler(struct drm_device *dev) + ustatus &= ~0x00000001; + } + if (ustatus & 0x00000002) { +- nv50_pfb_vm_trap(dev, display, "PGRAPH_TRAP_DISPATCH_QUERY"); ++ nv50_fb_vm_trap(dev, display, "PGRAPH_TRAP_DISPATCH_QUERY"); + nv_wr32(dev, 0x400500, 0); + if (nv_rd32(dev, 0x40084c) & 0x80000000) { + if (display) { +@@ -884,15 +889,15 @@ nv50_pgraph_trap_handler(struct drm_device *dev) + NV_INFO(dev, "PGRAPH_TRAP_M2MF - no ustatus?\n"); + } + if (ustatus & 0x00000001) { +- nv50_pfb_vm_trap(dev, display, "PGRAPH_TRAP_M2MF_NOTIFY"); ++ nv50_fb_vm_trap(dev, display, "PGRAPH_TRAP_M2MF_NOTIFY"); + ustatus &= ~0x00000001; + } + if (ustatus & 0x00000002) { +- nv50_pfb_vm_trap(dev, display, "PGRAPH_TRAP_M2MF_IN"); ++ nv50_fb_vm_trap(dev, display, "PGRAPH_TRAP_M2MF_IN"); + ustatus &= ~0x00000002; + } + if (ustatus & 0x00000004) { +- nv50_pfb_vm_trap(dev, display, "PGRAPH_TRAP_M2MF_OUT"); ++ nv50_fb_vm_trap(dev, display, "PGRAPH_TRAP_M2MF_OUT"); + ustatus &= ~0x00000004; + } + NV_INFO (dev, "PGRAPH_TRAP_M2MF - %08x %08x %08x %08x\n", +@@ -917,7 +922,7 @@ nv50_pgraph_trap_handler(struct drm_device *dev) + NV_INFO(dev, "PGRAPH_TRAP_VFETCH - no ustatus?\n"); + } + if (ustatus & 0x00000001) { +- nv50_pfb_vm_trap(dev, display, "PGRAPH_TRAP_VFETCH_FAULT"); ++ nv50_fb_vm_trap(dev, display, "PGRAPH_TRAP_VFETCH_FAULT"); + NV_INFO (dev, "PGRAPH_TRAP_VFETCH_FAULT - %08x %08x %08x %08x\n", + nv_rd32(dev, 0x400c00), + nv_rd32(dev, 0x400c08), +@@ -939,7 +944,7 @@ nv50_pgraph_trap_handler(struct drm_device *dev) + NV_INFO(dev, "PGRAPH_TRAP_STRMOUT - no ustatus?\n"); + } + if (ustatus & 0x00000001) { +- nv50_pfb_vm_trap(dev, display, "PGRAPH_TRAP_STRMOUT_FAULT"); ++ nv50_fb_vm_trap(dev, display, "PGRAPH_TRAP_STRMOUT_FAULT"); + NV_INFO (dev, "PGRAPH_TRAP_STRMOUT_FAULT - %08x %08x %08x %08x\n", + nv_rd32(dev, 0x401804), + nv_rd32(dev, 0x401808), +@@ -964,7 +969,7 @@ nv50_pgraph_trap_handler(struct drm_device *dev) + NV_INFO(dev, "PGRAPH_TRAP_CCACHE - no ustatus?\n"); + } + if (ustatus & 0x00000001) { +- nv50_pfb_vm_trap(dev, display, "PGRAPH_TRAP_CCACHE_FAULT"); ++ nv50_fb_vm_trap(dev, display, "PGRAPH_TRAP_CCACHE_FAULT"); + NV_INFO (dev, "PGRAPH_TRAP_CCACHE_FAULT - %08x %08x %08x %08x %08x %08x %08x\n", + nv_rd32(dev, 0x405800), + nv_rd32(dev, 0x405804), +@@ -986,7 +991,7 @@ nv50_pgraph_trap_handler(struct drm_device *dev) + * remaining, so try to handle it anyway. Perhaps related to that + * unknown DMA slot on tesla? */ + if (status & 0x20) { +- nv50_pfb_vm_trap(dev, display, "PGRAPH_TRAP_UNKC04"); ++ nv50_fb_vm_trap(dev, display, "PGRAPH_TRAP_UNKC04"); + ustatus = nv_rd32(dev, 0x402000) & 0x7fffffff; + if (display) + NV_INFO(dev, "PGRAPH_TRAP_UNKC04 - Unhandled ustatus 0x%08x\n", ustatus); diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c -index c1fd42b..adf5ac4 100644 +index c1fd42b..4f0ae39 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -35,162 +35,6 @@ @@ -3036,7 +6371,74 @@ index c1fd42b..adf5ac4 100644 /* * NV10-NV40 tiling helpers */ -@@ -299,7 +143,6 @@ nv50_mem_vm_bind_linear(struct drm_device *dev, uint64_t virt, uint32_t size, +@@ -203,18 +47,14 @@ nv10_mem_set_region_tiling(struct drm_device *dev, int i, uint32_t addr, + struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; + struct nouveau_fb_engine *pfb = &dev_priv->engine.fb; + struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; +- struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i]; ++ struct nouveau_tile_reg *tile = &dev_priv->tile[i]; + + tile->addr = addr; + tile->size = size; + tile->used = !!pitch; + nouveau_fence_unref((void **)&tile->fence); + +- if (!pfifo->cache_flush(dev)) +- return; +- + pfifo->reassign(dev, false); +- pfifo->cache_flush(dev); + pfifo->cache_pull(dev, false); + + nouveau_wait_for_idle(dev); +@@ -232,34 +72,36 @@ nv10_mem_set_tiling(struct drm_device *dev, uint32_t addr, uint32_t size, + { + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_fb_engine *pfb = &dev_priv->engine.fb; +- struct nouveau_tile_reg *tile = dev_priv->tile.reg, *found = NULL; +- int i; ++ struct nouveau_tile_reg *found = NULL; ++ unsigned long i, flags; + +- spin_lock(&dev_priv->tile.lock); ++ spin_lock_irqsave(&dev_priv->context_switch_lock, flags); + + for (i = 0; i < pfb->num_tiles; i++) { +- if (tile[i].used) ++ struct nouveau_tile_reg *tile = &dev_priv->tile[i]; ++ ++ if (tile->used) + /* Tile region in use. */ + continue; + +- if (tile[i].fence && +- !nouveau_fence_signalled(tile[i].fence, NULL)) ++ if (tile->fence && ++ !nouveau_fence_signalled(tile->fence, NULL)) + /* Pending tile region. */ + continue; + +- if (max(tile[i].addr, addr) < +- min(tile[i].addr + tile[i].size, addr + size)) ++ if (max(tile->addr, addr) < ++ min(tile->addr + tile->size, addr + size)) + /* Kill an intersecting tile region. */ + nv10_mem_set_region_tiling(dev, i, 0, 0, 0); + + if (pitch && !found) { + /* Free tile region. */ + nv10_mem_set_region_tiling(dev, i, addr, size, pitch); +- found = &tile[i]; ++ found = tile; + } + } + +- spin_unlock(&dev_priv->tile.lock); ++ spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); + + return found; + } +@@ -299,7 +141,6 @@ nv50_mem_vm_bind_linear(struct drm_device *dev, uint64_t virt, uint32_t size, phys |= 0x30; } @@ -3044,7 +6446,15 @@ index c1fd42b..adf5ac4 100644 while (size) { unsigned offset_h = upper_32_bits(phys); unsigned offset_l = lower_32_bits(phys); -@@ -331,36 +174,12 @@ nv50_mem_vm_bind_linear(struct drm_device *dev, uint64_t virt, uint32_t size, +@@ -326,41 +167,18 @@ nv50_mem_vm_bind_linear(struct drm_device *dev, uint64_t virt, uint32_t size, + virt += (end - pte); + + while (pte < end) { +- nv_wo32(dev, pgt, pte++, offset_l); +- nv_wo32(dev, pgt, pte++, offset_h); ++ nv_wo32(pgt, (pte * 4) + 0, offset_l); ++ nv_wo32(pgt, (pte * 4) + 4, offset_h); ++ pte += 2; } } } @@ -3086,7 +6496,7 @@ index c1fd42b..adf5ac4 100644 return 0; } -@@ -374,7 +193,6 @@ nv50_mem_vm_unbind(struct drm_device *dev, uint64_t virt, uint32_t size) +@@ -374,7 +192,6 @@ nv50_mem_vm_unbind(struct drm_device *dev, uint64_t virt, uint32_t size) virt -= dev_priv->vm_vram_base; pages = (size >> 16) << 1; @@ -3094,10 +6504,13 @@ index c1fd42b..adf5ac4 100644 while (pages) { pgt = dev_priv->vm_vram_pt[virt >> 29]; pte = (virt & 0x1ffe0000ULL) >> 15; -@@ -388,57 +206,19 @@ nv50_mem_vm_unbind(struct drm_device *dev, uint64_t virt, uint32_t size) - while (pte < end) - nv_wo32(dev, pgt, pte++, 0); - } +@@ -385,60 +202,24 @@ nv50_mem_vm_unbind(struct drm_device *dev, uint64_t virt, uint32_t size) + pages -= (end - pte); + virt += (end - pte) << 15; + +- while (pte < end) +- nv_wo32(dev, pgt, pte++, 0); +- } - dev_priv->engine.instmem.finish_access(dev); - - nv_wr32(dev, 0x100c80, 0x00050001); @@ -3119,7 +6532,11 @@ index c1fd42b..adf5ac4 100644 - NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n"); - NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80)); - return; -- } ++ while (pte < end) { ++ nv_wo32(pgt, (pte * 4), 0); ++ pte++; ++ } + } + dev_priv->engine.instmem.flush(dev); - nv_wr32(dev, 0x100c80, 0x00060001); @@ -3155,31 +6572,43 @@ index c1fd42b..adf5ac4 100644 - -void nouveau_mem_close(struct drm_device *dev) +void -+nouveau_mem_close(struct drm_device *dev) ++nouveau_mem_vram_fini(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; -@@ -449,8 +229,7 @@ void nouveau_mem_close(struct drm_device *dev) +@@ -449,8 +230,20 @@ void nouveau_mem_close(struct drm_device *dev) nouveau_ttm_global_release(dev_priv); - if (drm_core_has_AGP(dev) && dev->agp && - drm_core_check_feature(dev, DRIVER_MODESET)) { ++ if (dev_priv->fb_mtrr >= 0) { ++ drm_mtrr_del(dev_priv->fb_mtrr, ++ pci_resource_start(dev->pdev, 1), ++ pci_resource_len(dev->pdev, 1), DRM_MTRR_WC); ++ dev_priv->fb_mtrr = -1; ++ } ++} ++ ++void ++nouveau_mem_gart_fini(struct drm_device *dev) ++{ ++ nouveau_sgdma_takedown(dev); ++ + if (drm_core_has_AGP(dev) && dev->agp) { struct drm_agp_mem *entry, *tempe; /* Remove AGP resources, but leave dev->agp -@@ -470,29 +249,29 @@ void nouveau_mem_close(struct drm_device *dev) +@@ -469,30 +262,24 @@ void nouveau_mem_close(struct drm_device *dev) + dev->agp->acquired = 0; dev->agp->enabled = 0; } - +- - if (dev_priv->fb_mtrr) { -+ if (dev_priv->fb_mtrr >= 0) { - drm_mtrr_del(dev_priv->fb_mtrr, drm_get_resource_start(dev, 1), - drm_get_resource_len(dev, 1), DRM_MTRR_WC); +- 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; -+ dev_priv->fb_mtrr = -1; - } +- } } static uint32_t @@ -3207,33 +6636,336 @@ index c1fd42b..adf5ac4 100644 return 4 * 1024 * 1024; } -@@ -536,12 +315,18 @@ nouveau_mem_detect(struct drm_device *dev) +@@ -525,8 +312,62 @@ nouveau_mem_detect_nforce(struct drm_device *dev) + return 0; + } + +-/* returns the amount of FB ram in bytes */ +-int ++static void ++nv50_vram_preinit(struct drm_device *dev) ++{ ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ int i, parts, colbits, rowbitsa, rowbitsb, banks; ++ u64 rowsize, predicted; ++ u32 r0, r4, rt, ru; ++ ++ r0 = nv_rd32(dev, 0x100200); ++ r4 = nv_rd32(dev, 0x100204); ++ rt = nv_rd32(dev, 0x100250); ++ ru = nv_rd32(dev, 0x001540); ++ NV_DEBUG(dev, "memcfg 0x%08x 0x%08x 0x%08x 0x%08x\n", r0, r4, rt, ru); ++ ++ for (i = 0, parts = 0; i < 8; i++) { ++ if (ru & (0x00010000 << i)) ++ parts++; ++ } ++ ++ colbits = (r4 & 0x0000f000) >> 12; ++ rowbitsa = ((r4 & 0x000f0000) >> 16) + 8; ++ rowbitsb = ((r4 & 0x00f00000) >> 20) + 8; ++ banks = ((r4 & 0x01000000) ? 8 : 4); ++ ++ rowsize = parts * banks * (1 << colbits) * 8; ++ predicted = rowsize << rowbitsa; ++ if (r0 & 0x00000004) ++ predicted += rowsize << rowbitsb; ++ ++ if (predicted != dev_priv->vram_size) { ++ NV_WARN(dev, "memory controller reports %dMiB VRAM\n", ++ (u32)(dev_priv->vram_size >> 20)); ++ NV_WARN(dev, "we calculated %dMiB VRAM\n", ++ (u32)(predicted >> 20)); ++ } ++ ++ dev_priv->vram_rblock_size = rowsize >> 12; ++ if (rt & 1) ++ dev_priv->vram_rblock_size *= 3; ++ ++ NV_DEBUG(dev, "rblock %lld bytes\n", ++ (u64)dev_priv->vram_rblock_size << 12); ++} ++ ++static void ++nvaa_vram_preinit(struct drm_device *dev) ++{ ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ ++ /* To our knowledge, there's no large scale reordering of pages ++ * that occurs on IGP chipsets. ++ */ ++ dev_priv->vram_rblock_size = 1; ++} ++ ++static int + nouveau_mem_detect(struct drm_device *dev) + { + struct drm_nouveau_private *dev_priv = dev->dev_private; +@@ -536,12 +377,31 @@ nouveau_mem_detect(struct drm_device *dev) } else if (dev_priv->flags & (NV_NFORCE | NV_NFORCE2)) { dev_priv->vram_size = nouveau_mem_detect_nforce(dev); +- } else { +- dev_priv->vram_size = nv_rd32(dev, NV04_FIFO_DATA); +- dev_priv->vram_size &= NV10_FIFO_DATA_RAM_AMOUNT_MB_MASK; +- if (dev_priv->chipset == 0xaa || dev_priv->chipset == 0xac) + } else + if (dev_priv->card_type < NV_50) { + dev_priv->vram_size = nv_rd32(dev, NV04_PFB_FIFO_DATA); + dev_priv->vram_size &= NV10_PFB_FIFO_DATA_RAM_AMOUNT_MB_MASK; - } else { -- dev_priv->vram_size = nv_rd32(dev, NV04_FIFO_DATA); -- dev_priv->vram_size &= NV10_FIFO_DATA_RAM_AMOUNT_MB_MASK; -- if (dev_priv->chipset == 0xaa || dev_priv->chipset == 0xac) ++ } else ++ if (dev_priv->card_type < NV_C0) { + dev_priv->vram_size = nv_rd32(dev, NV04_PFB_FIFO_DATA); + dev_priv->vram_size |= (dev_priv->vram_size & 0xff) << 32; + dev_priv->vram_size &= 0xffffffff00ll; -+ if (dev_priv->chipset == 0xaa || dev_priv->chipset == 0xac) { ++ ++ switch (dev_priv->chipset) { ++ case 0xaa: ++ case 0xac: ++ case 0xaf: dev_priv->vram_sys_base = nv_rd32(dev, 0x100e10); dev_priv->vram_sys_base <<= 12; ++ nvaa_vram_preinit(dev); ++ break; ++ default: ++ nv50_vram_preinit(dev); ++ break; + } ++ } else { ++ dev_priv->vram_size = nv_rd32(dev, 0x10f20c) << 20; ++ dev_priv->vram_size *= nv_rd32(dev, 0x121c74); } NV_INFO(dev, "Detected %dMiB VRAM\n", (int)(dev_priv->vram_size >> 20)); +@@ -556,17 +416,63 @@ nouveau_mem_detect(struct drm_device *dev) + } + + #if __OS_HAS_AGP +-static void nouveau_mem_reset_agp(struct drm_device *dev) ++static unsigned long ++get_agp_mode(struct drm_device *dev, unsigned long mode) + { +- uint32_t saved_pci_nv_1, saved_pci_nv_19, pmc_enable; ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ ++ /* ++ * FW seems to be broken on nv18, it makes the card lock up ++ * randomly. ++ */ ++ if (dev_priv->chipset == 0x18) ++ mode &= ~PCI_AGP_COMMAND_FW; ++ ++ /* ++ * AGP mode set in the command line. ++ */ ++ if (nouveau_agpmode > 0) { ++ bool agpv3 = mode & 0x8; ++ int rate = agpv3 ? nouveau_agpmode / 4 : nouveau_agpmode; ++ ++ mode = (mode & ~0x7) | (rate & 0x7); ++ } ++ ++ return mode; ++} ++#endif ++ ++int ++nouveau_mem_reset_agp(struct drm_device *dev) ++{ ++#if __OS_HAS_AGP ++ uint32_t saved_pci_nv_1, pmc_enable; ++ int ret; ++ ++ /* First of all, disable fast writes, otherwise if it's ++ * already enabled in the AGP bridge and we disable the card's ++ * AGP controller we might be locking ourselves out of it. */ ++ if ((nv_rd32(dev, NV04_PBUS_PCI_NV_19) | ++ dev->agp->mode) & PCI_AGP_COMMAND_FW) { ++ struct drm_agp_info info; ++ struct drm_agp_mode mode; ++ ++ ret = drm_agp_info(dev, &info); ++ if (ret) ++ return ret; ++ ++ mode.mode = get_agp_mode(dev, info.mode) & ~PCI_AGP_COMMAND_FW; ++ ret = drm_agp_enable(dev, mode); ++ if (ret) ++ return ret; ++ } + + 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); ++ /* disable AGP */ ++ nv_wr32(dev, NV04_PBUS_PCI_NV_19, 0); + + /* power cycle pgraph, if enabled */ + pmc_enable = nv_rd32(dev, NV03_PMC_ENABLE); +@@ -578,11 +484,12 @@ static void nouveau_mem_reset_agp(struct drm_device *dev) + } + + /* 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); +-} + #endif + ++ return 0; ++} ++ + int + nouveau_mem_init_agp(struct drm_device *dev) + { +@@ -592,11 +499,6 @@ nouveau_mem_init_agp(struct drm_device *dev) + 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) { +@@ -605,6 +507,8 @@ nouveau_mem_init_agp(struct drm_device *dev) + } + } + ++ nouveau_mem_reset_agp(dev); ++ + ret = drm_agp_info(dev, &info); + if (ret) { + NV_ERROR(dev, "Unable to get AGP info: %d\n", ret); +@@ -612,7 +516,7 @@ nouveau_mem_init_agp(struct drm_device *dev) + } + + /* see agp.h for the AGPSTAT_* modes available */ +- mode.mode = info.mode; ++ mode.mode = get_agp_mode(dev, info.mode); + ret = drm_agp_enable(dev, mode); + if (ret) { + NV_ERROR(dev, "Unable to enable AGP: %d\n", ret); +@@ -627,24 +531,27 @@ nouveau_mem_init_agp(struct drm_device *dev) + } + + int +-nouveau_mem_init(struct drm_device *dev) ++nouveau_mem_vram_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; ++ int ret, dma_bits; + + if (dev_priv->card_type >= NV_50 && + pci_dma_supported(dev->pdev, DMA_BIT_MASK(40))) + dma_bits = 40; ++ else ++ dma_bits = 32; + + ret = pci_set_dma_mask(dev->pdev, DMA_BIT_MASK(dma_bits)); +- if (ret) { +- NV_ERROR(dev, "Error setting DMA mask: %d\n", ret); ++ if (ret) + return ret; +- } ++ ++ ret = nouveau_mem_detect(dev); ++ if (ret) ++ return ret; ++ ++ dev_priv->fb_phys = pci_resource_start(dev->pdev, 1); + + ret = nouveau_ttm_global_init(dev_priv); + if (ret) +@@ -659,17 +566,22 @@ nouveau_mem_init(struct drm_device *dev) + return ret; + } + +- INIT_LIST_HEAD(&dev_priv->ttm.bo_list); +- spin_lock_init(&dev_priv->ttm.bo_list_lock); +- spin_lock_init(&dev_priv->tile.lock); +- + dev_priv->fb_available_size = dev_priv->vram_size; + 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; + +- /* remove reserved space at end of vram from available amount */ ++ /* reserve space at end of VRAM for PRAMIN */ ++ if (dev_priv->chipset == 0x40 || dev_priv->chipset == 0x47 || ++ dev_priv->chipset == 0x49 || dev_priv->chipset == 0x4b) ++ dev_priv->ramin_rsvd_vram = (2 * 1024 * 1024); ++ else ++ if (dev_priv->card_type >= NV_40) ++ dev_priv->ramin_rsvd_vram = (1 * 1024 * 1024); ++ else ++ dev_priv->ramin_rsvd_vram = (512 * 1024); ++ + dev_priv->fb_available_size -= dev_priv->ramin_rsvd_vram; + dev_priv->fb_aper_free = dev_priv->fb_available_size; + +@@ -690,9 +602,23 @@ nouveau_mem_init(struct drm_device *dev) + nouveau_bo_ref(NULL, &dev_priv->vga_ram); + } + +- /* GART */ ++ dev_priv->fb_mtrr = drm_mtrr_add(pci_resource_start(dev->pdev, 1), ++ pci_resource_len(dev->pdev, 1), ++ DRM_MTRR_WC); ++ return 0; ++} ++ ++int ++nouveau_mem_gart_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; ++ ++ dev_priv->gart_info.type = NOUVEAU_GART_NONE; ++ + #if !defined(__powerpc__) && !defined(__ia64__) +- if (drm_device_is_agp(dev) && dev->agp) { ++ if (drm_device_is_agp(dev) && dev->agp && nouveau_agpmode) { + ret = nouveau_mem_init_agp(dev); + if (ret) + NV_ERROR(dev, "Error initialising AGP: %d\n", ret); +@@ -718,11 +644,6 @@ nouveau_mem_init(struct drm_device *dev) + 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 -index 9537f3e..3ec181f 100644 +index 9537f3e..22b8618 100644 --- a/drivers/gpu/drm/nouveau/nouveau_notifier.c +++ b/drivers/gpu/drm/nouveau/nouveau_notifier.c -@@ -55,7 +55,7 @@ nouveau_notifier_init_channel(struct nouveau_channel *chan) +@@ -28,6 +28,7 @@ + #include "drmP.h" + #include "drm.h" + #include "nouveau_drv.h" ++#include "nouveau_ramht.h" + + int + nouveau_notifier_init_channel(struct nouveau_channel *chan) +@@ -55,7 +56,7 @@ nouveau_notifier_init_channel(struct nouveau_channel *chan) if (ret) goto out_err; @@ -3242,7 +6974,7 @@ index 9537f3e..3ec181f 100644 if (ret) goto out_err; -@@ -80,7 +80,7 @@ nouveau_notifier_takedown_channel(struct nouveau_channel *chan) +@@ -80,7 +81,7 @@ nouveau_notifier_takedown_channel(struct nouveau_channel *chan) nouveau_bo_unpin(chan->notifier_bo); mutex_unlock(&dev->struct_mutex); drm_gem_object_unreference_unlocked(chan->notifier_bo->gem); @@ -3251,7 +6983,7 @@ index 9537f3e..3ec181f 100644 } static void -@@ -90,7 +90,7 @@ nouveau_notifier_gpuobj_dtor(struct drm_device *dev, +@@ -90,7 +91,7 @@ nouveau_notifier_gpuobj_dtor(struct drm_device *dev, NV_DEBUG(dev, "\n"); if (gpuobj->priv) @@ -3260,7 +6992,7 @@ index 9537f3e..3ec181f 100644 } int -@@ -100,18 +100,13 @@ nouveau_notifier_alloc(struct nouveau_channel *chan, uint32_t handle, +@@ -100,18 +101,13 @@ nouveau_notifier_alloc(struct nouveau_channel *chan, uint32_t handle, struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_gpuobj *nobj = NULL; @@ -3283,7 +7015,7 @@ index 9537f3e..3ec181f 100644 if (!mem) { NV_ERROR(dev, "Channel %d notifier block full\n", chan->id); return -ENOMEM; -@@ -144,17 +139,17 @@ nouveau_notifier_alloc(struct nouveau_channel *chan, uint32_t handle, +@@ -144,18 +140,18 @@ nouveau_notifier_alloc(struct nouveau_channel *chan, uint32_t handle, mem->size, NV_DMA_ACCESS_RW, target, &nobj); if (ret) { @@ -3297,15 +7029,19 @@ index 9537f3e..3ec181f 100644 + nobj->dtor = nouveau_notifier_gpuobj_dtor; + nobj->priv = mem; - ret = nouveau_gpuobj_ref_add(dev, chan, handle, nobj, NULL); +- ret = nouveau_gpuobj_ref_add(dev, chan, handle, nobj, NULL); ++ ret = nouveau_ramht_insert(chan, handle, nobj); ++ nouveau_gpuobj_ref(NULL, &nobj); if (ret) { - nouveau_gpuobj_del(dev, &nobj); +- nouveau_gpuobj_del(dev, &nobj); - nouveau_mem_free_block(mem); +- NV_ERROR(dev, "Error referencing notifier ctxdma: %d\n", ret); + drm_mm_put_block(mem); - NV_ERROR(dev, "Error referencing notifier ctxdma: %d\n", ret); ++ NV_ERROR(dev, "Error adding notifier to ramht: %d\n", ret); return ret; } -@@ -170,7 +165,7 @@ nouveau_notifier_offset(struct nouveau_gpuobj *nobj, uint32_t *poffset) + +@@ -170,7 +166,7 @@ nouveau_notifier_offset(struct nouveau_gpuobj *nobj, uint32_t *poffset) return -EINVAL; if (poffset) { @@ -3314,7 +7050,7 @@ index 9537f3e..3ec181f 100644 if (*poffset >= mem->size) return false; -@@ -189,7 +184,6 @@ nouveau_ioctl_notifier_alloc(struct drm_device *dev, void *data, +@@ -189,7 +185,6 @@ nouveau_ioctl_notifier_alloc(struct drm_device *dev, void *data, struct nouveau_channel *chan; int ret; @@ -3323,71 +7059,187 @@ index 9537f3e..3ec181f 100644 ret = nouveau_notifier_alloc(chan, na->handle, na->size, &na->offset); diff --git a/drivers/gpu/drm/nouveau/nouveau_object.c b/drivers/gpu/drm/nouveau/nouveau_object.c -index e7c100b..4bf6b33 100644 +index e7c100b..115904d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_object.c +++ b/drivers/gpu/drm/nouveau/nouveau_object.c -@@ -132,7 +132,6 @@ nouveau_ramht_insert(struct drm_device *dev, struct nouveau_gpuobj_ref *ref) - } - } +@@ -34,6 +34,7 @@ + #include "drm.h" + #include "nouveau_drv.h" + #include "nouveau_drm.h" ++#include "nouveau_ramht.h" + /* NVidia uses context objects to drive drawing operations. + +@@ -65,141 +66,6 @@ + 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)) { -@@ -143,7 +142,7 @@ nouveau_ramht_insert(struct drm_device *dev, struct nouveau_gpuobj_ref *ref) - nv_wo32(dev, ramht, (co + 4)/4, ctx); - - list_add_tail(&ref->list, &chan->ramht_refs); +- 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); -+ instmem->flush(dev); - return 0; - } - NV_DEBUG(dev, "collision ch%d 0x%08x: h=0x%08x\n", -@@ -153,7 +152,6 @@ nouveau_ramht_insert(struct drm_device *dev, struct nouveau_gpuobj_ref *ref) - if (co >= dev_priv->ramht_size) - co = 0; - } while (co != ho); +- 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; -@@ -173,7 +171,6 @@ nouveau_ramht_remove(struct drm_device *dev, struct nouveau_gpuobj_ref *ref) - return; - } - +- +- 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) && -@@ -186,7 +183,7 @@ nouveau_ramht_remove(struct drm_device *dev, struct nouveau_gpuobj_ref *ref) - nv_wo32(dev, ramht, (co + 4)/4, 0x00000000); - - list_del(&ref->list); +- 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); -+ instmem->flush(dev); - return; - } - -@@ -195,7 +192,6 @@ nouveau_ramht_remove(struct drm_device *dev, struct nouveau_gpuobj_ref *ref) - co = 0; - } while (co != ho); - list_del(&ref->list); +- 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); +-} - NV_ERROR(dev, "RAMHT entry not found. ch=%d, handle=0x%08x\n", - chan->id, ref->handle); -@@ -209,7 +205,7 @@ nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan, + int + nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan, +@@ -209,7 +75,7 @@ nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan, 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; -+ struct drm_mm *pramin = NULL; ++ struct drm_mm_node *ramin = NULL; int ret; NV_DEBUG(dev, "ch%d size=%u align=%d flags=0x%08x\n", -@@ -233,25 +229,12 @@ nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan, - * available. - */ +@@ -222,82 +88,102 @@ nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan, + if (!gpuobj) + return -ENOMEM; + NV_DEBUG(dev, "gpuobj %p\n", gpuobj); ++ gpuobj->dev = dev; + gpuobj->flags = flags; +- gpuobj->im_channel = chan; ++ kref_init(&gpuobj->refcount); ++ gpuobj->size = size; + ++ spin_lock(&dev_priv->ramin_lock); + list_add_tail(&gpuobj->list, &dev_priv->gpuobj_list); ++ spin_unlock(&dev_priv->ramin_lock); + +- /* Choose between global instmem heap, and per-channel private +- * instmem heap. On ramin_heap) { - NV_DEBUG(dev, "private heap\n"); @@ -3396,93 +7248,493 @@ index e7c100b..4bf6b33 100644 - if (dev_priv->card_type < NV_50) { - NV_DEBUG(dev, "global heap fallback\n"); - pramin = dev_priv->ramin_heap; -- } + NV_DEBUG(dev, "channel heap\n"); -+ pramin = &chan->ramin_heap; ++ ++ ramin = drm_mm_search_free(&chan->ramin_heap, size, align, 0); ++ if (ramin) ++ ramin = drm_mm_get_block(ramin, size, align); ++ ++ if (!ramin) { ++ nouveau_gpuobj_ref(NULL, &gpuobj); ++ return -ENOMEM; + } } else { NV_DEBUG(dev, "global heap\n"); - pramin = dev_priv->ramin_heap; - } -- + - if (!pramin) { - NV_ERROR(dev, "No PRAMIN heap!\n"); - return -EINVAL; - } -+ pramin = &dev_priv->ramin_heap; - +- - if (!chan) { ++ /* allocate backing pages, sets vinst */ ret = engine->instmem.populate(dev, gpuobj, &size); if (ret) { - nouveau_gpuobj_del(dev, &gpuobj); -@@ -260,9 +243,10 @@ nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan, - } +- nouveau_gpuobj_del(dev, &gpuobj); ++ nouveau_gpuobj_ref(NULL, &gpuobj); + return ret; + } +- } - /* Allocate a chunk of the PRAMIN aperture */ +- /* Allocate a chunk of the PRAMIN aperture */ - gpuobj->im_pramin = nouveau_mem_alloc_block(pramin, size, - drm_order(align), - (struct drm_file *)-2, 0); -+ gpuobj->im_pramin = drm_mm_search_free(pramin, size, align, 0); -+ if (gpuobj->im_pramin) -+ gpuobj->im_pramin = drm_mm_get_block(gpuobj->im_pramin, size, align); +- if (!gpuobj->im_pramin) { +- nouveau_gpuobj_del(dev, &gpuobj); +- return -ENOMEM; ++ /* try and get aperture space */ ++ do { ++ if (drm_mm_pre_get(&dev_priv->ramin_heap)) ++ return -ENOMEM; + - if (!gpuobj->im_pramin) { - nouveau_gpuobj_del(dev, &gpuobj); - return -ENOMEM; -@@ -279,10 +263,9 @@ nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan, - if (gpuobj->flags & NVOBJ_FLAG_ZERO_ALLOC) { - int i; ++ spin_lock(&dev_priv->ramin_lock); ++ ramin = drm_mm_search_free(&dev_priv->ramin_heap, size, ++ align, 0); ++ if (ramin == NULL) { ++ spin_unlock(&dev_priv->ramin_lock); ++ nouveau_gpuobj_ref(NULL, &gpuobj); ++ return ret; ++ } ++ ++ ramin = drm_mm_get_block_atomic(ramin, size, align); ++ spin_unlock(&dev_priv->ramin_lock); ++ } while (ramin == NULL); ++ ++ /* on nv50 it's ok to fail, we have a fallback path */ ++ if (!ramin && dev_priv->card_type < NV_50) { ++ nouveau_gpuobj_ref(NULL, &gpuobj); ++ return -ENOMEM; ++ } + } + +- if (!chan) { ++ /* if we got a chunk of the aperture, map pages into it */ ++ gpuobj->im_pramin = ramin; ++ if (!chan && gpuobj->im_pramin && dev_priv->ramin_available) { + ret = engine->instmem.bind(dev, gpuobj); + if (ret) { +- nouveau_gpuobj_del(dev, &gpuobj); ++ nouveau_gpuobj_ref(NULL, &gpuobj); + return ret; + } + } + +- if (gpuobj->flags & NVOBJ_FLAG_ZERO_ALLOC) { +- int i; ++ /* calculate the various different addresses for the object */ ++ if (chan) { ++ gpuobj->pinst = chan->ramin->pinst; ++ if (gpuobj->pinst != ~0) ++ gpuobj->pinst += gpuobj->im_pramin->start; - engine->instmem.prepare_access(dev, true); - for (i = 0; i < gpuobj->im_pramin->size; i += 4) - nv_wo32(dev, gpuobj, i/4, 0); +- for (i = 0; i < gpuobj->im_pramin->size; i += 4) +- nv_wo32(dev, gpuobj, i/4, 0); - engine->instmem.finish_access(dev); -+ engine->instmem.flush(dev); ++ if (dev_priv->card_type < NV_50) { ++ gpuobj->cinst = gpuobj->pinst; ++ } else { ++ gpuobj->cinst = gpuobj->im_pramin->start; ++ gpuobj->vinst = gpuobj->im_pramin->start + ++ chan->ramin->vinst; ++ } ++ } else { ++ if (gpuobj->im_pramin) ++ gpuobj->pinst = gpuobj->im_pramin->start; ++ else ++ gpuobj->pinst = ~0; ++ gpuobj->cinst = 0xdeadbeef; } - *gpuobj_ret = gpuobj; -@@ -370,10 +353,9 @@ nouveau_gpuobj_del(struct drm_device *dev, struct nouveau_gpuobj **pgpuobj) - } +- *gpuobj_ret = gpuobj; +- return 0; +-} +- +-int +-nouveau_gpuobj_early_init(struct drm_device *dev) +-{ +- struct drm_nouveau_private *dev_priv = dev->dev_private; ++ if (gpuobj->flags & NVOBJ_FLAG_ZERO_ALLOC) { ++ int i; + +- NV_DEBUG(dev, "\n"); ++ for (i = 0; i < gpuobj->size; i += 4) ++ nv_wo32(gpuobj, i, 0); ++ engine->instmem.flush(dev); ++ } + +- INIT_LIST_HEAD(&dev_priv->gpuobj_list); + ++ *gpuobj_ret = gpuobj; + return 0; + } + +@@ -305,18 +191,12 @@ 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; +- } ++ INIT_LIST_HEAD(&dev_priv->gpuobj_list); ++ spin_lock_init(&dev_priv->ramin_lock); ++ dev_priv->ramin_base = ~0; + + return 0; + } +@@ -328,299 +208,89 @@ nouveau_gpuobj_takedown(struct drm_device *dev) + + NV_DEBUG(dev, "\n"); + +- nouveau_gpuobj_del(dev, &dev_priv->ramht); ++ BUG_ON(!list_empty(&dev_priv->gpuobj_list)); + } + +-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) ++static void ++nouveau_gpuobj_del(struct kref *ref) + { ++ struct nouveau_gpuobj *gpuobj = ++ container_of(ref, struct nouveau_gpuobj, refcount); ++ struct drm_device *dev = gpuobj->dev; + 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; +- } ++ NV_DEBUG(dev, "gpuobj %p\n", gpuobj); 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); +- for (i = 0; i < gpuobj->im_pramin->size; i += 4) +- nv_wo32(dev, gpuobj, i/4, 0); - engine->instmem.finish_access(dev); ++ for (i = 0; i < gpuobj->size; i += 4) ++ nv_wo32(gpuobj, i, 0); + engine->instmem.flush(dev); } if (gpuobj->dtor) -@@ -386,7 +368,7 @@ nouveau_gpuobj_del(struct drm_device *dev, struct nouveau_gpuobj **pgpuobj) - if (gpuobj->flags & NVOBJ_FLAG_FAKE) - kfree(gpuobj->im_pramin); - else + gpuobj->dtor(dev, gpuobj); + +- if (gpuobj->im_backing && !(gpuobj->flags & NVOBJ_FLAG_FAKE)) ++ if (gpuobj->im_backing) + 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); -+ drm_mm_put_block(gpuobj->im_pramin); - } - +- } +- ++ spin_lock(&dev_priv->ramin_lock); ++ if (gpuobj->im_pramin) ++ drm_mm_put_block(gpuobj->im_pramin); list_del(&gpuobj->list); -@@ -589,7 +571,7 @@ nouveau_gpuobj_new_fake(struct drm_device *dev, uint32_t p_offset, - list_add_tail(&gpuobj->list, &dev_priv->gpuobj_list); ++ spin_unlock(&dev_priv->ramin_lock); - if (p_offset != ~0) { +- *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; +- +- /* 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) ++void ++nouveau_gpuobj_ref(struct nouveau_gpuobj *ref, struct nouveau_gpuobj **ptr) + { +- struct nouveau_gpuobj_ref *ref; +- struct list_head *entry, *tmp; ++ if (ref) ++ kref_get(&ref->refcount); + +- list_for_each_safe(entry, tmp, &chan->ramht_refs) { +- ref = list_entry(entry, struct nouveau_gpuobj_ref, list); ++ if (*ptr) ++ kref_put(&(*ptr)->refcount, nouveau_gpuobj_del); + +- if (ref->handle == handle) { +- if (ref_ret) +- *ref_ret = ref; +- return 0; +- } +- } +- +- return -EINVAL; ++ *ptr = ref; + } + + 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) ++nouveau_gpuobj_new_fake(struct drm_device *dev, u32 pinst, u64 vinst, ++ u32 size, u32 flags, struct nouveau_gpuobj **pgpuobj) + { + 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); ++ "pinst=0x%08x vinst=0x%010llx size=0x%08x flags=0x%08x\n", ++ pinst, vinst, 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), -+ gpuobj->im_pramin = kzalloc(sizeof(struct drm_mm_node), - GFP_KERNEL); - if (!gpuobj->im_pramin) { - nouveau_gpuobj_del(dev, &gpuobj); -@@ -605,10 +587,9 @@ nouveau_gpuobj_new_fake(struct drm_device *dev, uint32_t p_offset, - } +- 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; +- } ++ gpuobj->dev = dev; ++ gpuobj->flags = flags; ++ kref_init(&gpuobj->refcount); ++ gpuobj->size = size; ++ gpuobj->pinst = pinst; ++ gpuobj->cinst = 0xdeadbeef; ++ gpuobj->vinst = vinst; 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); +- for (i = 0; i < gpuobj->im_pramin->size; i += 4) +- nv_wo32(dev, gpuobj, i/4, 0); - dev_priv->engine.instmem.finish_access(dev); ++ for (i = 0; i < gpuobj->size; i += 4) ++ nv_wo32(gpuobj, i, 0); + dev_priv->engine.instmem.flush(dev); } - if (pref) { -@@ -696,8 +677,6 @@ nouveau_gpuobj_dma_new(struct nouveau_channel *chan, int class, +- 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; ++ spin_lock(&dev_priv->ramin_lock); ++ list_add_tail(&gpuobj->list, &dev_priv->gpuobj_list); ++ spin_unlock(&dev_priv->ramin_lock); ++ *pgpuobj = gpuobj; + return 0; + } + +@@ -696,8 +366,6 @@ nouveau_gpuobj_dma_new(struct nouveau_channel *chan, int class, return ret; } @@ -3491,8 +7743,43 @@ index e7c100b..4bf6b33 100644 if (dev_priv->card_type < NV_50) { uint32_t frame, adjust, pte_flags = 0; -@@ -734,7 +713,7 @@ nouveau_gpuobj_dma_new(struct nouveau_channel *chan, int class, - nv_wo32(dev, *gpuobj, 5, flags5); +@@ -706,14 +374,12 @@ nouveau_gpuobj_dma_new(struct nouveau_channel *chan, int class, + 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); ++ nv_wo32(*gpuobj, 0, ((1<<12) | (1<<13) | (adjust << 20) | ++ (access << 14) | (target << 16) | ++ class)); ++ nv_wo32(*gpuobj, 4, size - 1); ++ nv_wo32(*gpuobj, 8, frame | pte_flags); ++ nv_wo32(*gpuobj, 12, frame | pte_flags); + } else { + uint64_t limit = offset + size - 1; + uint32_t flags0, flags5; +@@ -726,15 +392,15 @@ nouveau_gpuobj_dma_new(struct nouveau_channel *chan, int class, + 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); ++ nv_wo32(*gpuobj, 0, flags0 | class); ++ nv_wo32(*gpuobj, 4, lower_32_bits(limit)); ++ nv_wo32(*gpuobj, 8, lower_32_bits(offset)); ++ nv_wo32(*gpuobj, 12, ((upper_32_bits(limit) & 0xff) << 24) | ++ (upper_32_bits(offset) & 0xff)); ++ nv_wo32(*gpuobj, 20, flags5); } - instmem->finish_access(dev); @@ -3500,15 +7787,49 @@ index e7c100b..4bf6b33 100644 (*gpuobj)->engine = NVOBJ_ENGINE_SW; (*gpuobj)->class = class; -@@ -849,7 +828,6 @@ nouveau_gpuobj_gr_new(struct nouveau_channel *chan, int class, +@@ -762,7 +428,7 @@ nouveau_gpuobj_gart_dma_new(struct nouveau_channel *chan, + *o_ret = 0; + } else + if (dev_priv->gart_info.type == NOUVEAU_GART_SGDMA) { +- *gpuobj = dev_priv->gart_info.sg_ctxdma; ++ nouveau_gpuobj_ref(dev_priv->gart_info.sg_ctxdma, gpuobj); + if (offset & ~0xffffffffULL) { + NV_ERROR(dev, "obj offset exceeds 32-bits\n"); + return -EINVAL; +@@ -849,32 +515,31 @@ nouveau_gpuobj_gr_new(struct nouveau_channel *chan, int class, 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); -@@ -874,7 +852,7 @@ nouveau_gpuobj_gr_new(struct nouveau_channel *chan, int class, +- nv_wo32(dev, *gpuobj, 0, class); +- nv_wo32(dev, *gpuobj, 5, 0x00010000); ++ nv_wo32(*gpuobj, 0, class); ++ nv_wo32(*gpuobj, 20, 0x00010000); + } else { + switch (class) { + case NV_CLASS_NULL: +- nv_wo32(dev, *gpuobj, 0, 0x00001030); +- nv_wo32(dev, *gpuobj, 1, 0xFFFFFFFF); ++ nv_wo32(*gpuobj, 0, 0x00001030); ++ nv_wo32(*gpuobj, 4, 0xFFFFFFFF); + break; + default: + if (dev_priv->card_type >= NV_40) { +- nv_wo32(dev, *gpuobj, 0, class); ++ nv_wo32(*gpuobj, 0, class); + #ifdef __BIG_ENDIAN +- nv_wo32(dev, *gpuobj, 2, 0x01000000); ++ nv_wo32(*gpuobj, 8, 0x01000000); + #endif + } else { + #ifdef __BIG_ENDIAN +- nv_wo32(dev, *gpuobj, 0, class | 0x00080000); ++ nv_wo32(*gpuobj, 0, class | 0x00080000); + #else +- nv_wo32(dev, *gpuobj, 0, class); ++ nv_wo32(*gpuobj, 0, class); + #endif } } } @@ -3517,7 +7838,31 @@ index e7c100b..4bf6b33 100644 (*gpuobj)->engine = NVOBJ_ENGINE_GR; (*gpuobj)->class = class; -@@ -920,6 +898,7 @@ nouveau_gpuobj_channel_init_pramin(struct nouveau_channel *chan) +@@ -895,10 +560,15 @@ nouveau_gpuobj_sw_new(struct nouveau_channel *chan, int class, + gpuobj = kzalloc(sizeof(*gpuobj), GFP_KERNEL); + if (!gpuobj) + return -ENOMEM; ++ gpuobj->dev = chan->dev; + gpuobj->engine = NVOBJ_ENGINE_SW; + gpuobj->class = class; ++ kref_init(&gpuobj->refcount); ++ gpuobj->cinst = 0x40; + ++ spin_lock(&dev_priv->ramin_lock); + list_add_tail(&gpuobj->list, &dev_priv->gpuobj_list); ++ spin_unlock(&dev_priv->ramin_lock); + *gpuobj_ret = gpuobj; + return 0; + } +@@ -908,7 +578,6 @@ 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; +@@ -920,6 +589,7 @@ nouveau_gpuobj_channel_init_pramin(struct nouveau_channel *chan) base = 0; /* PGRAPH context */ @@ -3525,7 +7870,7 @@ index e7c100b..4bf6b33 100644 if (dev_priv->card_type == NV_50) { /* Various fixed table thingos */ -@@ -930,12 +909,8 @@ nouveau_gpuobj_channel_init_pramin(struct nouveau_channel *chan) +@@ -930,25 +600,18 @@ nouveau_gpuobj_channel_init_pramin(struct nouveau_channel *chan) size += 0x8000; /* RAMFC */ size += 0x1000; @@ -3535,21 +7880,31 @@ index e7c100b..4bf6b33 100644 - 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); +- ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, size, 0x1000, 0, +- &chan->ramin); ++ ret = nouveau_gpuobj_new(dev, NULL, size, 0x1000, 0, &chan->ramin); if (ret) { -@@ -944,8 +919,7 @@ nouveau_gpuobj_channel_init_pramin(struct nouveau_channel *chan) + NV_ERROR(dev, "Error allocating channel PRAMIN: %d\n", ret); + return ret; } - pramin = chan->ramin->gpuobj; +- pramin = chan->ramin->gpuobj; - ret = nouveau_mem_init_heap(&chan->ramin_heap, - pramin->im_pramin->start + base, size); -+ ret = drm_mm_init(&chan->ramin_heap, pramin->im_pramin->start + base, size); ++ ret = drm_mm_init(&chan->ramin_heap, base, size); if (ret) { NV_ERROR(dev, "Error creating PRAMIN heap: %d\n", ret); - nouveau_gpuobj_ref_del(dev, &chan->ramin); -@@ -969,15 +943,11 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan, +- nouveau_gpuobj_ref_del(dev, &chan->ramin); ++ nouveau_gpuobj_ref(NULL, &chan->ramin); + return ret; + } +@@ -965,19 +628,13 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan, + 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 @@ -3569,50 +7924,76 @@ index e7c100b..4bf6b33 100644 } /* NV50 VM -@@ -988,17 +958,13 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan, +@@ -986,65 +643,56 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan, + * locations determined during init. + */ if (dev_priv->card_type >= NV_50) { - uint32_t vm_offset, pde; - -- instmem->prepare_access(dev, true); +- uint32_t vm_offset, pde; - - vm_offset = (dev_priv->chipset & 0xf0) == 0x50 ? 0x1400 : 0x200; - vm_offset += chan->ramin->gpuobj->im_pramin->start; +- instmem->prepare_access(dev, true); ++ u32 pgd_offs = (dev_priv->chipset == 0x50) ? 0x1400 : 0x0200; ++ u64 vm_vinst = chan->ramin->vinst + pgd_offs; ++ u32 vm_pinst = chan->ramin->pinst; ++ u32 pde; - ret = nouveau_gpuobj_new_fake(dev, vm_offset, ~0, 0x4000, - 0, &chan->vm_pd, NULL); +- vm_offset = (dev_priv->chipset & 0xf0) == 0x50 ? 0x1400 : 0x200; +- vm_offset += chan->ramin->gpuobj->im_pramin->start; ++ if (vm_pinst != ~0) ++ vm_pinst += pgd_offs; + +- ret = nouveau_gpuobj_new_fake(dev, vm_offset, ~0, 0x4000, +- 0, &chan->vm_pd, NULL); - if (ret) { - instmem->finish_access(dev); ++ ret = nouveau_gpuobj_new_fake(dev, vm_pinst, vm_vinst, 0x4000, ++ 0, &chan->vm_pd); + if (ret) 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); -@@ -1008,10 +974,8 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan, - ret = nouveau_gpuobj_ref_add(dev, NULL, 0, - dev_priv->gart_info.sg_ctxdma, - &chan->vm_gart_pt); +- nv_wo32(dev, chan->vm_pd, (i+0)/4, 0x00000000); +- nv_wo32(dev, chan->vm_pd, (i+4)/4, 0xdeadcafe); ++ nv_wo32(chan->vm_pd, i + 0, 0x00000000); ++ nv_wo32(chan->vm_pd, i + 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); -+ if (ret) - return ret; +- return ret; - } - nv_wo32(dev, chan->vm_pd, pde++, - chan->vm_gart_pt->instance | 0x03); - nv_wo32(dev, chan->vm_pd, pde++, 0x00000000); -@@ -1021,17 +985,15 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan, - ret = nouveau_gpuobj_ref_add(dev, NULL, 0, - dev_priv->vm_vram_pt[i], - &chan->vm_vram_pt[i]); +- nv_wo32(dev, chan->vm_pd, pde++, +- chan->vm_gart_pt->instance | 0x03); +- nv_wo32(dev, chan->vm_pd, pde++, 0x00000000); ++ nouveau_gpuobj_ref(dev_priv->gart_info.sg_ctxdma, ++ &chan->vm_gart_pt); ++ pde = (dev_priv->vm_gart_base / (512*1024*1024)) * 8; ++ nv_wo32(chan->vm_pd, pde + 0, chan->vm_gart_pt->vinst | 3); ++ nv_wo32(chan->vm_pd, pde + 4, 0x00000000); + +- pde = (dev_priv->vm_vram_base / (512*1024*1024)) * 2; ++ pde = (dev_priv->vm_vram_base / (512*1024*1024)) * 8; + 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); -+ if (ret) - return ret; +- return ret; - } ++ nouveau_gpuobj_ref(dev_priv->vm_vram_pt[i], ++ &chan->vm_vram_pt[i]); - nv_wo32(dev, chan->vm_pd, pde++, - chan->vm_vram_pt[i]->instance | 0x61); - nv_wo32(dev, chan->vm_pd, pde++, 0x00000000); +- nv_wo32(dev, chan->vm_pd, pde++, +- chan->vm_vram_pt[i]->instance | 0x61); +- nv_wo32(dev, chan->vm_pd, pde++, 0x00000000); ++ nv_wo32(chan->vm_pd, pde + 0, ++ chan->vm_vram_pt[i]->vinst | 0x61); ++ nv_wo32(chan->vm_pd, pde + 4, 0x00000000); ++ pde += 8; } - instmem->finish_access(dev); @@ -3620,41 +8001,161 @@ index e7c100b..4bf6b33 100644 } /* RAMHT */ -@@ -1130,8 +1092,8 @@ nouveau_gpuobj_channel_takedown(struct nouveau_channel *chan) - for (i = 0; i < dev_priv->vm_vram_pt_nr; i++) - nouveau_gpuobj_ref_del(dev, &chan->vm_vram_pt[i]); + if (dev_priv->card_type < NV_50) { +- ret = nouveau_gpuobj_ref_add(dev, NULL, 0, dev_priv->ramht, +- &chan->ramht); ++ nouveau_ramht_ref(dev_priv->ramht, &chan->ramht, NULL); ++ } else { ++ struct nouveau_gpuobj *ramht = NULL; ++ ++ ret = nouveau_gpuobj_new(dev, chan, 0x8000, 16, ++ NVOBJ_FLAG_ZERO_ALLOC, &ramht); + if (ret) + return ret; +- } else { +- ret = nouveau_gpuobj_new_ref(dev, chan, chan, 0, +- 0x8000, 16, +- NVOBJ_FLAG_ZERO_ALLOC, +- &chan->ramht); ++ ++ ret = nouveau_ramht_new(dev, ramht, &chan->ramht); ++ nouveau_gpuobj_ref(NULL, &ramht); + if (ret) + return ret; + } +@@ -1061,24 +709,32 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan, + } + } 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); ++ 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); ++ ret = nouveau_ramht_insert(chan, vram_h, vram); ++ nouveau_gpuobj_ref(NULL, &vram); + if (ret) { +- NV_ERROR(dev, "Error referencing VRAM ctxdma: %d\n", ret); ++ NV_ERROR(dev, "Error adding VRAM ctxdma to RAMHT: %d\n", ret); + return ret; + } + + /* TT memory ctxdma */ + if (dev_priv->card_type >= NV_50) { +- tt = vram; ++ ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, ++ 0, dev_priv->vm_end, ++ NV_DMA_ACCESS_RW, ++ NV_DMA_TARGET_AGP, &tt); ++ if (ret) { ++ NV_ERROR(dev, "Error creating VRAM ctxdma: %d\n", ret); ++ return ret; ++ } + } else + if (dev_priv->gart_info.type != NOUVEAU_GART_NONE) { + ret = nouveau_gpuobj_gart_dma_new(chan, 0, +@@ -1094,9 +750,10 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan, + return ret; + } + +- ret = nouveau_gpuobj_ref_add(dev, chan, tt_h, tt, NULL); ++ ret = nouveau_ramht_insert(chan, tt_h, tt); ++ nouveau_gpuobj_ref(NULL, &tt); + if (ret) { +- NV_ERROR(dev, "Error referencing TT ctxdma: %d\n", ret); ++ NV_ERROR(dev, "Error adding TT ctxdma to RAMHT: %d\n", ret); + return ret; + } + +@@ -1108,33 +765,23 @@ 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) ++ if (!chan->ramht) + return; + +- list_for_each_safe(entry, tmp, &chan->ramht_refs) { +- ref = list_entry(entry, struct nouveau_gpuobj_ref, list); ++ nouveau_ramht_ref(NULL, &chan->ramht, chan); + +- 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); ++ nouveau_gpuobj_ref(NULL, &chan->vm_pd); ++ nouveau_gpuobj_ref(NULL, &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); ++ nouveau_gpuobj_ref(NULL, &chan->vm_vram_pt[i]); + + if (chan->ramin_heap.fl_entry.next) + drm_mm_takedown(&chan->ramin_heap); - if (chan->ramin) - nouveau_gpuobj_ref_del(dev, &chan->ramin); ++ nouveau_gpuobj_ref(NULL, &chan->ramin); + } -@@ -1164,10 +1126,8 @@ nouveau_gpuobj_suspend(struct drm_device *dev) + int +@@ -1155,19 +802,17 @@ nouveau_gpuobj_suspend(struct drm_device *dev) + } + + list_for_each_entry(gpuobj, &dev_priv->gpuobj_list, list) { +- if (!gpuobj->im_backing || (gpuobj->flags & NVOBJ_FLAG_FAKE)) ++ if (!gpuobj->im_backing) + continue; + +- gpuobj->im_backing_suspend = vmalloc(gpuobj->im_pramin->size); ++ gpuobj->im_backing_suspend = vmalloc(gpuobj->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); +- 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); ++ for (i = 0; i < gpuobj->size; i += 4) ++ gpuobj->im_backing_suspend[i/4] = nv_ro32(gpuobj, i); } return 0; -@@ -1212,10 +1172,9 @@ nouveau_gpuobj_resume(struct drm_device *dev) +@@ -1212,10 +857,9 @@ nouveau_gpuobj_resume(struct drm_device *dev) 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]); +- 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); ++ for (i = 0; i < gpuobj->size; i += 4) ++ nv_wo32(gpuobj, i, gpuobj->im_backing_suspend[i/4]); + dev_priv->engine.instmem.flush(dev); } nouveau_gpuobj_suspend_cleanup(dev); -@@ -1232,7 +1191,6 @@ int nouveau_ioctl_grobj_alloc(struct drm_device *dev, void *data, +@@ -1232,7 +876,6 @@ int nouveau_ioctl_grobj_alloc(struct drm_device *dev, void *data, struct nouveau_channel *chan; int ret; @@ -3662,16 +8163,463 @@ index e7c100b..4bf6b33 100644 NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(init->channel, file_priv, chan); if (init->handle == ~0) -@@ -1283,7 +1241,6 @@ int nouveau_ioctl_gpuobj_free(struct drm_device *dev, void *data, +@@ -1250,25 +893,24 @@ int nouveau_ioctl_grobj_alloc(struct drm_device *dev, void *data, + return -EPERM; + } + +- if (nouveau_gpuobj_ref_find(chan, init->handle, NULL) == 0) ++ if (nouveau_ramht_find(chan, init->handle)) + 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); ++ ret = nouveau_ramht_insert(chan, init->handle, gr); ++ nouveau_gpuobj_ref(NULL, &gr); + 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; + } + +@@ -1279,17 +921,62 @@ 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_gpuobj *gpuobj; struct nouveau_channel *chan; - int ret; +- 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); +- ret = nouveau_gpuobj_ref_find(chan, objfree->handle, &ref); +- if (ret) +- return ret; +- nouveau_gpuobj_ref_del(dev, &ref); ++ gpuobj = nouveau_ramht_find(chan, objfree->handle); ++ if (!gpuobj) ++ return -ENOENT; + ++ nouveau_ramht_remove(chan, objfree->handle); + return 0; + } ++ ++u32 ++nv_ro32(struct nouveau_gpuobj *gpuobj, u32 offset) ++{ ++ struct drm_nouveau_private *dev_priv = gpuobj->dev->dev_private; ++ struct drm_device *dev = gpuobj->dev; ++ ++ if (gpuobj->pinst == ~0 || !dev_priv->ramin_available) { ++ u64 ptr = gpuobj->vinst + offset; ++ u32 base = ptr >> 16; ++ u32 val; ++ ++ spin_lock(&dev_priv->ramin_lock); ++ if (dev_priv->ramin_base != base) { ++ dev_priv->ramin_base = base; ++ nv_wr32(dev, 0x001700, dev_priv->ramin_base); ++ } ++ val = nv_rd32(dev, 0x700000 + (ptr & 0xffff)); ++ spin_unlock(&dev_priv->ramin_lock); ++ return val; ++ } ++ ++ return nv_ri32(dev, gpuobj->pinst + offset); ++} ++ ++void ++nv_wo32(struct nouveau_gpuobj *gpuobj, u32 offset, u32 val) ++{ ++ struct drm_nouveau_private *dev_priv = gpuobj->dev->dev_private; ++ struct drm_device *dev = gpuobj->dev; ++ ++ if (gpuobj->pinst == ~0 || !dev_priv->ramin_available) { ++ u64 ptr = gpuobj->vinst + offset; ++ u32 base = ptr >> 16; ++ ++ spin_lock(&dev_priv->ramin_lock); ++ if (dev_priv->ramin_base != base) { ++ dev_priv->ramin_base = base; ++ nv_wr32(dev, 0x001700, dev_priv->ramin_base); ++ } ++ nv_wr32(dev, 0x700000 + (ptr & 0xffff), val); ++ spin_unlock(&dev_priv->ramin_lock); ++ return; ++ } ++ ++ nv_wi32(dev, gpuobj->pinst + offset, val); ++} +diff --git a/drivers/gpu/drm/nouveau/nouveau_ramht.c b/drivers/gpu/drm/nouveau/nouveau_ramht.c +new file mode 100644 +index 0000000..7f16697 +--- /dev/null ++++ b/drivers/gpu/drm/nouveau/nouveau_ramht.c +@@ -0,0 +1,289 @@ ++/* ++ * Copyright 2010 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_ramht.h" ++ ++static u32 ++nouveau_ramht_hash_handle(struct nouveau_channel *chan, u32 handle) ++{ ++ struct drm_device *dev = chan->dev; ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ struct nouveau_ramht *ramht = chan->ramht; ++ u32 hash = 0; ++ int i; ++ ++ NV_DEBUG(dev, "ch%d handle=0x%08x\n", chan->id, handle); ++ ++ for (i = 32; i > 0; i -= ramht->bits) { ++ hash ^= (handle & ((1 << ramht->bits) - 1)); ++ handle >>= ramht->bits; ++ } ++ ++ if (dev_priv->card_type < NV_50) ++ hash ^= chan->id << (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, ++ u32 offset) ++{ ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ u32 ctx = nv_ro32(ramht, offset + 4); ++ ++ if (dev_priv->card_type < NV_40) ++ return ((ctx & NV_RAMHT_CONTEXT_VALID) != 0); ++ return (ctx != 0); ++} ++ ++static int ++nouveau_ramht_entry_same_channel(struct nouveau_channel *chan, ++ struct nouveau_gpuobj *ramht, u32 offset) ++{ ++ struct drm_nouveau_private *dev_priv = chan->dev->dev_private; ++ u32 ctx = nv_ro32(ramht, offset + 4); ++ ++ if (dev_priv->card_type >= NV_50) ++ return true; ++ else if (dev_priv->card_type >= NV_40) ++ return chan->id == ++ ((ctx >> NV40_RAMHT_CONTEXT_CHANNEL_SHIFT) & 0x1f); ++ else ++ return chan->id == ++ ((ctx >> NV_RAMHT_CONTEXT_CHANNEL_SHIFT) & 0x1f); ++} ++ ++int ++nouveau_ramht_insert(struct nouveau_channel *chan, u32 handle, ++ 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; ++ struct nouveau_ramht_entry *entry; ++ struct nouveau_gpuobj *ramht = chan->ramht->gpuobj; ++ unsigned long flags; ++ u32 ctx, co, ho; ++ ++ if (nouveau_ramht_find(chan, handle)) ++ return -EEXIST; ++ ++ entry = kmalloc(sizeof(*entry), GFP_KERNEL); ++ if (!entry) ++ return -ENOMEM; ++ entry->channel = chan; ++ entry->gpuobj = NULL; ++ entry->handle = handle; ++ nouveau_gpuobj_ref(gpuobj, &entry->gpuobj); ++ ++ if (dev_priv->card_type < NV_40) { ++ ctx = NV_RAMHT_CONTEXT_VALID | (gpuobj->cinst >> 4) | ++ (chan->id << NV_RAMHT_CONTEXT_CHANNEL_SHIFT) | ++ (gpuobj->engine << NV_RAMHT_CONTEXT_ENGINE_SHIFT); ++ } else ++ if (dev_priv->card_type < NV_50) { ++ ctx = (gpuobj->cinst >> 4) | ++ (chan->id << NV40_RAMHT_CONTEXT_CHANNEL_SHIFT) | ++ (gpuobj->engine << NV40_RAMHT_CONTEXT_ENGINE_SHIFT); ++ } else { ++ if (gpuobj->engine == NVOBJ_ENGINE_DISPLAY) { ++ ctx = (gpuobj->cinst << 10) | 2; ++ } else { ++ ctx = (gpuobj->cinst >> 4) | ++ ((gpuobj->engine << ++ NV40_RAMHT_CONTEXT_ENGINE_SHIFT)); ++ } ++ } ++ ++ spin_lock_irqsave(&chan->ramht->lock, flags); ++ list_add(&entry->head, &chan->ramht->entries); ++ ++ co = ho = nouveau_ramht_hash_handle(chan, 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, handle, ctx); ++ nv_wo32(ramht, co + 0, handle); ++ nv_wo32(ramht, co + 4, ctx); ++ ++ spin_unlock_irqrestore(&chan->ramht->lock, flags); ++ instmem->flush(dev); ++ return 0; ++ } ++ NV_DEBUG(dev, "collision ch%d 0x%08x: h=0x%08x\n", ++ chan->id, co, nv_ro32(ramht, co)); ++ ++ co += 8; ++ if (co >= ramht->size) ++ co = 0; ++ } while (co != ho); ++ ++ NV_ERROR(dev, "RAMHT space exhausted. ch=%d\n", chan->id); ++ list_del(&entry->head); ++ spin_unlock_irqrestore(&chan->ramht->lock, flags); ++ kfree(entry); ++ return -ENOMEM; ++} ++ ++static void ++nouveau_ramht_remove_locked(struct nouveau_channel *chan, u32 handle) ++{ ++ 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 *ramht = chan->ramht->gpuobj; ++ struct nouveau_ramht_entry *entry, *tmp; ++ u32 co, ho; ++ ++ list_for_each_entry_safe(entry, tmp, &chan->ramht->entries, head) { ++ if (entry->channel != chan || entry->handle != handle) ++ continue; ++ ++ nouveau_gpuobj_ref(NULL, &entry->gpuobj); ++ list_del(&entry->head); ++ kfree(entry); ++ break; ++ } ++ ++ co = ho = nouveau_ramht_hash_handle(chan, handle); ++ do { ++ if (nouveau_ramht_entry_valid(dev, ramht, co) && ++ nouveau_ramht_entry_same_channel(chan, ramht, co) && ++ (handle == nv_ro32(ramht, co))) { ++ NV_DEBUG(dev, ++ "remove ch%d 0x%08x: h=0x%08x, c=0x%08x\n", ++ chan->id, co, handle, nv_ro32(ramht, co + 4)); ++ nv_wo32(ramht, co + 0, 0x00000000); ++ nv_wo32(ramht, co + 4, 0x00000000); ++ instmem->flush(dev); ++ return; ++ } ++ ++ co += 8; ++ if (co >= ramht->size) ++ co = 0; ++ } while (co != ho); ++ ++ NV_ERROR(dev, "RAMHT entry not found. ch=%d, handle=0x%08x\n", ++ chan->id, handle); ++} ++ ++void ++nouveau_ramht_remove(struct nouveau_channel *chan, u32 handle) ++{ ++ struct nouveau_ramht *ramht = chan->ramht; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&ramht->lock, flags); ++ nouveau_ramht_remove_locked(chan, handle); ++ spin_unlock_irqrestore(&ramht->lock, flags); ++} ++ ++struct nouveau_gpuobj * ++nouveau_ramht_find(struct nouveau_channel *chan, u32 handle) ++{ ++ struct nouveau_ramht *ramht = chan->ramht; ++ struct nouveau_ramht_entry *entry; ++ struct nouveau_gpuobj *gpuobj = NULL; ++ unsigned long flags; ++ ++ if (unlikely(!chan->ramht)) ++ return NULL; ++ ++ spin_lock_irqsave(&ramht->lock, flags); ++ list_for_each_entry(entry, &chan->ramht->entries, head) { ++ if (entry->channel == chan && entry->handle == handle) { ++ gpuobj = entry->gpuobj; ++ break; ++ } ++ } ++ spin_unlock_irqrestore(&ramht->lock, flags); ++ ++ return gpuobj; ++} ++ ++int ++nouveau_ramht_new(struct drm_device *dev, struct nouveau_gpuobj *gpuobj, ++ struct nouveau_ramht **pramht) ++{ ++ struct nouveau_ramht *ramht; ++ ++ ramht = kzalloc(sizeof(*ramht), GFP_KERNEL); ++ if (!ramht) ++ return -ENOMEM; ++ ++ ramht->dev = dev; ++ kref_init(&ramht->refcount); ++ ramht->bits = drm_order(gpuobj->size / 8); ++ INIT_LIST_HEAD(&ramht->entries); ++ spin_lock_init(&ramht->lock); ++ nouveau_gpuobj_ref(gpuobj, &ramht->gpuobj); ++ ++ *pramht = ramht; ++ return 0; ++} ++ ++static void ++nouveau_ramht_del(struct kref *ref) ++{ ++ struct nouveau_ramht *ramht = ++ container_of(ref, struct nouveau_ramht, refcount); ++ ++ nouveau_gpuobj_ref(NULL, &ramht->gpuobj); ++ kfree(ramht); ++} ++ ++void ++nouveau_ramht_ref(struct nouveau_ramht *ref, struct nouveau_ramht **ptr, ++ struct nouveau_channel *chan) ++{ ++ struct nouveau_ramht_entry *entry, *tmp; ++ struct nouveau_ramht *ramht; ++ unsigned long flags; ++ ++ if (ref) ++ kref_get(&ref->refcount); ++ ++ ramht = *ptr; ++ if (ramht) { ++ spin_lock_irqsave(&ramht->lock, flags); ++ list_for_each_entry_safe(entry, tmp, &ramht->entries, head) { ++ if (entry->channel != chan) ++ continue; ++ ++ nouveau_ramht_remove_locked(chan, entry->handle); ++ } ++ spin_unlock_irqrestore(&ramht->lock, flags); ++ ++ kref_put(&ramht->refcount, nouveau_ramht_del); ++ } ++ *ptr = ref; ++} +diff --git a/drivers/gpu/drm/nouveau/nouveau_ramht.h b/drivers/gpu/drm/nouveau/nouveau_ramht.h +new file mode 100644 +index 0000000..b79cb5e +--- /dev/null ++++ b/drivers/gpu/drm/nouveau/nouveau_ramht.h +@@ -0,0 +1,55 @@ ++/* ++ * Copyright 2010 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 ++ */ ++ ++#ifndef __NOUVEAU_RAMHT_H__ ++#define __NOUVEAU_RAMHT_H__ ++ ++struct nouveau_ramht_entry { ++ struct list_head head; ++ struct nouveau_channel *channel; ++ struct nouveau_gpuobj *gpuobj; ++ u32 handle; ++}; ++ ++struct nouveau_ramht { ++ struct drm_device *dev; ++ struct kref refcount; ++ spinlock_t lock; ++ struct nouveau_gpuobj *gpuobj; ++ struct list_head entries; ++ int bits; ++}; ++ ++extern int nouveau_ramht_new(struct drm_device *, struct nouveau_gpuobj *, ++ struct nouveau_ramht **); ++extern void nouveau_ramht_ref(struct nouveau_ramht *, struct nouveau_ramht **, ++ struct nouveau_channel *unref_channel); ++ ++extern int nouveau_ramht_insert(struct nouveau_channel *, u32 handle, ++ struct nouveau_gpuobj *); ++extern void nouveau_ramht_remove(struct nouveau_channel *, u32 handle); ++extern struct nouveau_gpuobj * ++nouveau_ramht_find(struct nouveau_channel *chan, u32 handle); ++ ++#endif diff --git a/drivers/gpu/drm/nouveau/nouveau_reg.h b/drivers/gpu/drm/nouveau/nouveau_reg.h -index 6ca80a3..9c1056c 100644 +index 6ca80a3..1b42541 100644 --- a/drivers/gpu/drm/nouveau/nouveau_reg.h +++ b/drivers/gpu/drm/nouveau/nouveau_reg.h @@ -1,19 +1,64 @@ @@ -3777,7 +8725,80 @@ index 6ca80a3..9c1056c 100644 #define NV04_PGRAPH_DEBUG_0 0x00400080 #define NV04_PGRAPH_DEBUG_1 0x00400084 #define NV04_PGRAPH_DEBUG_2 0x00400088 -@@ -814,6 +842,7 @@ +@@ -192,28 +220,21 @@ + # 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 NV10_PGRAPH_CTX_SWITCH(i) (0x0040014C + 0x4*(i)) + #define NV04_PGRAPH_CTX_SWITCH1 0x00400160 +-#define NV10_PGRAPH_CTX_CACHE1 0x00400160 ++#define NV10_PGRAPH_CTX_CACHE(i, j) (0x00400160 \ ++ + 0x4*(i) + 0x20*(j)) + #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 +@@ -328,9 +349,12 @@ + #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 NV10_PGRAPH_FFINTFC_FIFO_PTR 0x00400760 + #define NV04_PGRAPH_DVD_COLORFMT 0x00400764 ++#define NV10_PGRAPH_FFINTFC_ST2 0x00400764 + #define NV04_PGRAPH_SCALED_FORMAT 0x00400768 ++#define NV10_PGRAPH_FFINTFC_ST2_DL 0x00400768 ++#define NV10_PGRAPH_FFINTFC_ST2_DH 0x0040076c + #define NV10_PGRAPH_DMA_PITCH 0x00400770 + #define NV10_PGRAPH_DVD_COLORFMT 0x00400774 + #define NV10_PGRAPH_SCALED_FORMAT 0x00400778 +@@ -527,6 +551,8 @@ + #define NV10_PFIFO_CACHE1_DMA_SUBROUTINE 0x0000324C + #define NV03_PFIFO_CACHE1_PULL0 0x00003240 + #define NV04_PFIFO_CACHE1_PULL0 0x00003250 ++# define NV04_PFIFO_CACHE1_PULL0_HASH_FAILED 0x00000010 ++# define NV04_PFIFO_CACHE1_PULL0_HASH_BUSY 0x00001000 + #define NV03_PFIFO_CACHE1_PULL1 0x00003250 + #define NV04_PFIFO_CACHE1_PULL1 0x00003254 + #define NV04_PFIFO_CACHE1_HASH 0x00003258 +@@ -761,15 +787,12 @@ + #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_EXT_MODE_CTRL_P(i) (0x00610b80 + (i) * 0x8) ++#define NV50_PDISPLAY_EXT_MODE_CTRL_C(i) (0x00610b84 + (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) +@@ -814,6 +837,7 @@ #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) @@ -3786,7 +8807,7 @@ index 6ca80a3..9c1056c 100644 #define NV50_SOR_DP_CTRL_LANE_MASK 0x001f0000 #define NV50_SOR_DP_CTRL_LANE_0_ENABLED 0x00010000 diff --git a/drivers/gpu/drm/nouveau/nouveau_sgdma.c b/drivers/gpu/drm/nouveau/nouveau_sgdma.c -index 1d6ee8b..491767f 100644 +index 1d6ee8b..7f028fe 100644 --- a/drivers/gpu/drm/nouveau/nouveau_sgdma.c +++ b/drivers/gpu/drm/nouveau/nouveau_sgdma.c @@ -97,7 +97,6 @@ nouveau_sgdma_bind(struct ttm_backend *be, struct ttm_mem_reg *mem) @@ -3797,7 +8818,24 @@ index 1d6ee8b..491767f 100644 pte = nouveau_sgdma_pte(nvbe->dev, mem->mm_node->start << PAGE_SHIFT); nvbe->pte_start = pte; for (i = 0; i < nvbe->nr_pages; i++) { -@@ -116,24 +115,11 @@ nouveau_sgdma_bind(struct ttm_backend *be, struct ttm_mem_reg *mem) +@@ -106,34 +105,23 @@ nouveau_sgdma_bind(struct ttm_backend *be, struct ttm_mem_reg *mem) + 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); ++ if (dev_priv->card_type < NV_50) { ++ nv_wo32(gpuobj, (pte * 4) + 0, offset_l | 3); ++ pte += 1; ++ } else { ++ nv_wo32(gpuobj, (pte * 4) + 0, offset_l | 0x21); ++ nv_wo32(gpuobj, (pte * 4) + 4, offset_h & 0xff); ++ pte += 2; + } + dma_offset += NV_CTXDMA_PAGE_SIZE; } } @@ -3825,7 +8863,7 @@ index 1d6ee8b..491767f 100644 } nvbe->bound = true; -@@ -154,7 +140,6 @@ nouveau_sgdma_unbind(struct ttm_backend *be) +@@ -154,40 +142,28 @@ nouveau_sgdma_unbind(struct ttm_backend *be) if (!nvbe->bound) return 0; @@ -3833,7 +8871,22 @@ index 1d6ee8b..491767f 100644 pte = nvbe->pte_start; for (i = 0; i < nvbe->nr_pages; i++) { dma_addr_t dma_offset = dev_priv->gart_info.sg_dummy_bus; -@@ -170,24 +155,11 @@ nouveau_sgdma_unbind(struct ttm_backend *be) + + 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); ++ if (dev_priv->card_type < NV_50) { ++ nv_wo32(gpuobj, (pte * 4) + 0, dma_offset | 3); ++ pte += 1; ++ } else { ++ nv_wo32(gpuobj, (pte * 4) + 0, 0x00000000); ++ nv_wo32(gpuobj, (pte * 4) + 4, 0x00000000); ++ pte += 2; + } + dma_offset += NV_CTXDMA_PAGE_SIZE; } } @@ -3861,16 +8914,79 @@ index 1d6ee8b..491767f 100644 } nvbe->bound = false; -@@ -272,7 +244,6 @@ nouveau_sgdma_init(struct drm_device *dev) - pci_map_page(dev->pdev, dev_priv->gart_info.sg_dummy_page, 0, +@@ -242,6 +218,7 @@ int + nouveau_sgdma_init(struct drm_device *dev) + { + struct drm_nouveau_private *dev_priv = dev->dev_private; ++ struct pci_dev *pdev = dev->pdev; + struct nouveau_gpuobj *gpuobj = NULL; + uint32_t aper_size, obj_size; + int i, ret; +@@ -257,7 +234,6 @@ nouveau_sgdma_init(struct drm_device *dev) + } + + 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) { +@@ -266,35 +242,48 @@ nouveau_sgdma_init(struct drm_device *dev) + } + + dev_priv->gart_info.sg_dummy_page = +- alloc_page(GFP_KERNEL|__GFP_DMA32); ++ alloc_page(GFP_KERNEL|__GFP_DMA32|__GFP_ZERO); ++ if (!dev_priv->gart_info.sg_dummy_page) { ++ nouveau_gpuobj_ref(NULL, &gpuobj); ++ return -ENOMEM; ++ } ++ + 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, ++ pci_map_page(pdev, dev_priv->gart_info.sg_dummy_page, 0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); ++ if (pci_dma_mapping_error(pdev, dev_priv->gart_info.sg_dummy_bus)) { ++ nouveau_gpuobj_ref(NULL, &gpuobj); ++ return -EFAULT; ++ } - dev_priv->engine.instmem.prepare_access(dev, true); if (dev_priv->card_type < NV_50) { ++ /* special case, allocated from global instmem heap so ++ * cinst is invalid, we use it on all channels though so ++ * cinst needs to be valid, set it the same as pinst ++ */ ++ gpuobj->cinst = gpuobj->pinst; ++ /* Maybe use NV_DMA_TARGET_AGP for PCIE? NVIDIA do this, and * confirmed to work on c51. Perhaps means NV_DMA_TARGET_PCIE -@@ -294,7 +265,7 @@ nouveau_sgdma_init(struct drm_device *dev) - nv_wo32(dev, gpuobj, (i+4)/4, 0); + * 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); ++ nv_wo32(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(gpuobj, 4, 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); ++ nv_wo32(gpuobj, i * 4, ++ 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); ++ nv_wo32(gpuobj, i + 0, 0x00000000); ++ nv_wo32(gpuobj, i + 4, 0x00000000); } } - dev_priv->engine.instmem.finish_access(dev); @@ -3878,26 +8994,49 @@ index 1d6ee8b..491767f 100644 dev_priv->gart_info.type = NOUVEAU_GART_SGDMA; dev_priv->gart_info.aper_base = 0; -@@ -325,14 +296,11 @@ nouveau_sgdma_get_page(struct drm_device *dev, uint32_t offset, uint32_t *page) +@@ -317,7 +306,7 @@ nouveau_sgdma_takedown(struct drm_device *dev) + dev_priv->gart_info.sg_dummy_bus = 0; + } + +- nouveau_gpuobj_del(dev, &dev_priv->gart_info.sg_ctxdma); ++ nouveau_gpuobj_ref(NULL, &dev_priv->gart_info.sg_ctxdma); + } + + int +@@ -325,14 +314,11 @@ 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); +- pte = (offset >> NV_CTXDMA_PAGE_SHIFT); ++ pte = (offset >> NV_CTXDMA_PAGE_SHIFT) << 2; if (dev_priv->card_type < NV_50) { - instmem->prepare_access(dev, false); - *page = nv_ro32(dev, gpuobj, (pte + 2)) & ~NV_CTXDMA_PAGE_MASK; +- *page = nv_ro32(dev, gpuobj, (pte + 2)) & ~NV_CTXDMA_PAGE_MASK; - instmem->finish_access(dev); ++ *page = nv_ro32(gpuobj, (pte + 8)) & ~NV_CTXDMA_PAGE_MASK; return 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c -index b02a231..621e080 100644 +index b02a231..be85960 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c -@@ -54,8 +54,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) +@@ -35,9 +35,11 @@ + #include "nouveau_drv.h" + #include "nouveau_drm.h" + #include "nouveau_fbcon.h" ++#include "nouveau_ramht.h" + #include "nv50_display.h" + + static void nouveau_stub_takedown(struct drm_device *dev) {} ++static int nouveau_stub_init(struct drm_device *dev) { return 0; } + + static int nouveau_init_engine_ptrs(struct drm_device *dev) + { +@@ -54,8 +56,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->instmem.clear = nv04_instmem_clear; engine->instmem.bind = nv04_instmem_bind; engine->instmem.unbind = nv04_instmem_unbind; @@ -3907,7 +9046,31 @@ index b02a231..621e080 100644 engine->mc.init = nv04_mc_init; engine->mc.takedown = nv04_mc_takedown; engine->timer.init = nv04_timer_init; -@@ -95,8 +94,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) +@@ -78,13 +79,22 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) + engine->fifo.disable = nv04_fifo_disable; + engine->fifo.enable = nv04_fifo_enable; + engine->fifo.reassign = nv04_fifo_reassign; +- engine->fifo.cache_flush = nv04_fifo_cache_flush; + engine->fifo.cache_pull = nv04_fifo_cache_pull; + 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; ++ engine->display.early_init = nv04_display_early_init; ++ engine->display.late_takedown = nv04_display_late_takedown; ++ engine->display.create = nv04_display_create; ++ engine->display.init = nv04_display_init; ++ engine->display.destroy = nv04_display_destroy; ++ engine->gpio.init = nouveau_stub_init; ++ engine->gpio.takedown = nouveau_stub_takedown; ++ engine->gpio.get = NULL; ++ engine->gpio.set = NULL; ++ engine->gpio.irq_enable = NULL; + break; + case 0x10: + engine->instmem.init = nv04_instmem_init; +@@ -95,8 +105,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->instmem.clear = nv04_instmem_clear; engine->instmem.bind = nv04_instmem_bind; engine->instmem.unbind = nv04_instmem_unbind; @@ -3917,7 +9080,31 @@ index b02a231..621e080 100644 engine->mc.init = nv04_mc_init; engine->mc.takedown = nv04_mc_takedown; engine->timer.init = nv04_timer_init; -@@ -138,8 +136,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) +@@ -121,13 +130,22 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) + engine->fifo.disable = nv04_fifo_disable; + engine->fifo.enable = nv04_fifo_enable; + engine->fifo.reassign = nv04_fifo_reassign; +- engine->fifo.cache_flush = nv04_fifo_cache_flush; + engine->fifo.cache_pull = nv04_fifo_cache_pull; + 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; ++ engine->display.early_init = nv04_display_early_init; ++ engine->display.late_takedown = nv04_display_late_takedown; ++ engine->display.create = nv04_display_create; ++ engine->display.init = nv04_display_init; ++ engine->display.destroy = nv04_display_destroy; ++ engine->gpio.init = nouveau_stub_init; ++ engine->gpio.takedown = nouveau_stub_takedown; ++ engine->gpio.get = nv10_gpio_get; ++ engine->gpio.set = nv10_gpio_set; ++ engine->gpio.irq_enable = NULL; + break; + case 0x20: + engine->instmem.init = nv04_instmem_init; +@@ -138,8 +156,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->instmem.clear = nv04_instmem_clear; engine->instmem.bind = nv04_instmem_bind; engine->instmem.unbind = nv04_instmem_unbind; @@ -3927,7 +9114,31 @@ index b02a231..621e080 100644 engine->mc.init = nv04_mc_init; engine->mc.takedown = nv04_mc_takedown; engine->timer.init = nv04_timer_init; -@@ -181,8 +178,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) +@@ -164,13 +181,22 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) + engine->fifo.disable = nv04_fifo_disable; + engine->fifo.enable = nv04_fifo_enable; + engine->fifo.reassign = nv04_fifo_reassign; +- engine->fifo.cache_flush = nv04_fifo_cache_flush; + engine->fifo.cache_pull = nv04_fifo_cache_pull; + 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; ++ engine->display.early_init = nv04_display_early_init; ++ engine->display.late_takedown = nv04_display_late_takedown; ++ engine->display.create = nv04_display_create; ++ engine->display.init = nv04_display_init; ++ engine->display.destroy = nv04_display_destroy; ++ engine->gpio.init = nouveau_stub_init; ++ engine->gpio.takedown = nouveau_stub_takedown; ++ engine->gpio.get = nv10_gpio_get; ++ engine->gpio.set = nv10_gpio_set; ++ engine->gpio.irq_enable = NULL; + break; + case 0x30: + engine->instmem.init = nv04_instmem_init; +@@ -181,15 +207,14 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->instmem.clear = nv04_instmem_clear; engine->instmem.bind = nv04_instmem_bind; engine->instmem.unbind = nv04_instmem_unbind; @@ -3937,7 +9148,40 @@ index b02a231..621e080 100644 engine->mc.init = nv04_mc_init; engine->mc.takedown = nv04_mc_takedown; engine->timer.init = nv04_timer_init; -@@ -225,8 +221,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) + 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->fb.init = nv30_fb_init; ++ engine->fb.takedown = nv30_fb_takedown; + engine->fb.set_region_tiling = nv10_fb_set_region_tiling; + engine->graph.grclass = nv30_graph_grclass; + engine->graph.init = nv30_graph_init; +@@ -207,13 +232,22 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) + engine->fifo.disable = nv04_fifo_disable; + engine->fifo.enable = nv04_fifo_enable; + engine->fifo.reassign = nv04_fifo_reassign; +- engine->fifo.cache_flush = nv04_fifo_cache_flush; + engine->fifo.cache_pull = nv04_fifo_cache_pull; + 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; ++ engine->display.early_init = nv04_display_early_init; ++ engine->display.late_takedown = nv04_display_late_takedown; ++ engine->display.create = nv04_display_create; ++ engine->display.init = nv04_display_init; ++ engine->display.destroy = nv04_display_destroy; ++ engine->gpio.init = nouveau_stub_init; ++ engine->gpio.takedown = nouveau_stub_takedown; ++ engine->gpio.get = nv10_gpio_get; ++ engine->gpio.set = nv10_gpio_set; ++ engine->gpio.irq_enable = NULL; + break; + case 0x40: + case 0x60: +@@ -225,8 +259,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->instmem.clear = nv04_instmem_clear; engine->instmem.bind = nv04_instmem_bind; engine->instmem.unbind = nv04_instmem_unbind; @@ -3947,7 +9191,31 @@ index b02a231..621e080 100644 engine->mc.init = nv40_mc_init; engine->mc.takedown = nv40_mc_takedown; engine->timer.init = nv04_timer_init; -@@ -271,8 +266,10 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) +@@ -251,13 +284,22 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) + engine->fifo.disable = nv04_fifo_disable; + engine->fifo.enable = nv04_fifo_enable; + engine->fifo.reassign = nv04_fifo_reassign; +- engine->fifo.cache_flush = nv04_fifo_cache_flush; + engine->fifo.cache_pull = nv04_fifo_cache_pull; + 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; ++ engine->display.early_init = nv04_display_early_init; ++ engine->display.late_takedown = nv04_display_late_takedown; ++ engine->display.create = nv04_display_create; ++ engine->display.init = nv04_display_init; ++ engine->display.destroy = nv04_display_destroy; ++ engine->gpio.init = nouveau_stub_init; ++ engine->gpio.takedown = nouveau_stub_takedown; ++ engine->gpio.get = nv10_gpio_get; ++ engine->gpio.set = nv10_gpio_set; ++ engine->gpio.irq_enable = NULL; + break; + case 0x50: + case 0x80: /* gotta love NVIDIA's consistency.. */ +@@ -271,8 +313,10 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) engine->instmem.clear = nv50_instmem_clear; engine->instmem.bind = nv50_instmem_bind; engine->instmem.unbind = nv50_instmem_unbind; @@ -3960,7 +9228,123 @@ index b02a231..621e080 100644 engine->mc.init = nv50_mc_init; engine->mc.takedown = nv50_mc_takedown; engine->timer.init = nv04_timer_init; -@@ -407,11 +404,6 @@ nouveau_card_init(struct drm_device *dev) +@@ -300,6 +344,64 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev) + engine->fifo.destroy_context = nv50_fifo_destroy_context; + engine->fifo.load_context = nv50_fifo_load_context; + engine->fifo.unload_context = nv50_fifo_unload_context; ++ engine->display.early_init = nv50_display_early_init; ++ engine->display.late_takedown = nv50_display_late_takedown; ++ engine->display.create = nv50_display_create; ++ engine->display.init = nv50_display_init; ++ engine->display.destroy = nv50_display_destroy; ++ engine->gpio.init = nv50_gpio_init; ++ engine->gpio.takedown = nouveau_stub_takedown; ++ engine->gpio.get = nv50_gpio_get; ++ engine->gpio.set = nv50_gpio_set; ++ engine->gpio.irq_enable = nv50_gpio_irq_enable; ++ break; ++ case 0xC0: ++ engine->instmem.init = nvc0_instmem_init; ++ engine->instmem.takedown = nvc0_instmem_takedown; ++ engine->instmem.suspend = nvc0_instmem_suspend; ++ engine->instmem.resume = nvc0_instmem_resume; ++ engine->instmem.populate = nvc0_instmem_populate; ++ engine->instmem.clear = nvc0_instmem_clear; ++ engine->instmem.bind = nvc0_instmem_bind; ++ engine->instmem.unbind = nvc0_instmem_unbind; ++ engine->instmem.flush = nvc0_instmem_flush; ++ 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 = nvc0_fb_init; ++ engine->fb.takedown = nvc0_fb_takedown; ++ engine->graph.grclass = NULL; //nvc0_graph_grclass; ++ engine->graph.init = nvc0_graph_init; ++ engine->graph.takedown = nvc0_graph_takedown; ++ engine->graph.fifo_access = nvc0_graph_fifo_access; ++ engine->graph.channel = nvc0_graph_channel; ++ engine->graph.create_context = nvc0_graph_create_context; ++ engine->graph.destroy_context = nvc0_graph_destroy_context; ++ engine->graph.load_context = nvc0_graph_load_context; ++ engine->graph.unload_context = nvc0_graph_unload_context; ++ engine->fifo.channels = 128; ++ engine->fifo.init = nvc0_fifo_init; ++ engine->fifo.takedown = nvc0_fifo_takedown; ++ engine->fifo.disable = nvc0_fifo_disable; ++ engine->fifo.enable = nvc0_fifo_enable; ++ engine->fifo.reassign = nvc0_fifo_reassign; ++ engine->fifo.channel_id = nvc0_fifo_channel_id; ++ engine->fifo.create_context = nvc0_fifo_create_context; ++ engine->fifo.destroy_context = nvc0_fifo_destroy_context; ++ engine->fifo.load_context = nvc0_fifo_load_context; ++ engine->fifo.unload_context = nvc0_fifo_unload_context; ++ engine->display.early_init = nv50_display_early_init; ++ engine->display.late_takedown = nv50_display_late_takedown; ++ engine->display.create = nv50_display_create; ++ engine->display.init = nv50_display_init; ++ engine->display.destroy = nv50_display_destroy; ++ engine->gpio.init = nv50_gpio_init; ++ engine->gpio.takedown = nouveau_stub_takedown; ++ engine->gpio.get = nv50_gpio_get; ++ engine->gpio.set = nv50_gpio_set; ++ engine->gpio.irq_enable = nv50_gpio_irq_enable; + break; + default: + NV_ERROR(dev, "NV%02x unsupported\n", dev_priv->chipset); +@@ -331,16 +433,14 @@ static int + nouveau_card_init_channel(struct drm_device *dev) + { + struct drm_nouveau_private *dev_priv = dev->dev_private; +- struct nouveau_gpuobj *gpuobj; ++ struct nouveau_gpuobj *gpuobj = NULL; + int ret; + + ret = nouveau_channel_alloc(dev, &dev_priv->channel, +- (struct drm_file *)-2, +- NvDmaFB, NvDmaTT); ++ (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, dev_priv->vram_size, + NV_DMA_ACCESS_RW, NV_DMA_TARGET_VIDMEM, +@@ -348,26 +448,25 @@ nouveau_card_init_channel(struct drm_device *dev) + if (ret) + goto out_err; + +- ret = nouveau_gpuobj_ref_add(dev, dev_priv->channel, NvDmaVRAM, +- gpuobj, NULL); ++ ret = nouveau_ramht_insert(dev_priv->channel, NvDmaVRAM, gpuobj); ++ nouveau_gpuobj_ref(NULL, &gpuobj); + if (ret) + goto out_err; + +- 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) + goto out_err; + +- ret = nouveau_gpuobj_ref_add(dev, dev_priv->channel, NvDmaGART, +- gpuobj, NULL); ++ ret = nouveau_ramht_insert(dev_priv->channel, NvDmaGART, gpuobj); ++ nouveau_gpuobj_ref(NULL, &gpuobj); + if (ret) + goto out_err; + + return 0; ++ + out_err: +- nouveau_gpuobj_del(dev, &gpuobj); + nouveau_channel_free(dev_priv->channel); + dev_priv->channel = NULL; + return ret; +@@ -407,11 +506,6 @@ nouveau_card_init(struct drm_device *dev) struct nouveau_engine *engine; int ret; @@ -3972,13 +9356,18 @@ index b02a231..621e080 100644 vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode); vga_switcheroo_register_client(dev->pdev, nouveau_switcheroo_set_state, nouveau_switcheroo_can_switch); -@@ -421,15 +413,12 @@ nouveau_card_init(struct drm_device *dev) +@@ -421,50 +515,48 @@ nouveau_card_init(struct drm_device *dev) if (ret) goto out; engine = &dev_priv->engine; - dev_priv->init_state = NOUVEAU_CARD_INIT_FAILED; spin_lock_init(&dev_priv->context_switch_lock); ++ /* Make the CRTCs and I2C buses accessible */ ++ ret = engine->display.early_init(dev); ++ if (ret) ++ goto out; ++ /* Parse BIOS tables / Run init tables if card not POSTed */ - if (drm_core_check_feature(dev, DRIVER_MODESET)) { - ret = nouveau_bios_init(dev); @@ -3987,18 +9376,62 @@ index b02a231..621e080 100644 - } + ret = nouveau_bios_init(dev); + if (ret) -+ goto out; ++ goto out_display_early; - ret = nouveau_mem_detect(dev); +- ret = nouveau_mem_detect(dev); ++ ret = nouveau_mem_vram_init(dev); if (ret) -@@ -485,12 +474,19 @@ nouveau_card_init(struct drm_device *dev) + goto out_bios; + +- ret = nouveau_gpuobj_early_init(dev); ++ ret = nouveau_gpuobj_init(dev); + if (ret) +- goto out_bios; ++ goto out_vram; + +- /* 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) +- goto out_gpuobj_early; ++ goto out_gpuobj; + +- /* Setup the memory manager */ +- ret = nouveau_mem_init(dev); ++ ret = nouveau_mem_gart_init(dev); + if (ret) + goto out_instmem; + +- ret = nouveau_gpuobj_init(dev); +- if (ret) +- goto out_mem; +- + /* PMC */ + ret = engine->mc.init(dev); + if (ret) +- goto out_gpuobj; ++ goto out_gart; ++ ++ /* PGPIO */ ++ ret = engine->gpio.init(dev); ++ if (ret) ++ goto out_mc; + + /* PTIMER */ + ret = engine->timer.init(dev); + if (ret) +- goto out_mc; ++ goto out_gpio; + + /* PFB */ + ret = engine->fb.init(dev); +@@ -485,12 +577,16 @@ nouveau_card_init(struct drm_device *dev) goto out_graph; } -+ if (dev_priv->card_type >= NV_50) -+ ret = nv50_display_create(dev); -+ else -+ ret = nv04_display_create(dev); ++ ret = engine->display.create(dev); + if (ret) + goto out_fifo; + @@ -4012,7 +9445,7 @@ index b02a231..621e080 100644 ret = drm_vblank_init(dev, 0); if (ret) -@@ -504,35 +500,21 @@ nouveau_card_init(struct drm_device *dev) +@@ -504,35 +600,18 @@ nouveau_card_init(struct drm_device *dev) goto out_irq; } @@ -4048,22 +9481,49 @@ index b02a231..621e080 100644 out_irq: drm_irq_uninstall(dev); +out_display: -+ if (dev_priv->card_type >= NV_50) -+ nv50_display_destroy(dev); -+ else -+ nv04_display_destroy(dev); ++ engine->display.destroy(dev); out_fifo: if (!nouveau_noaccel) engine->fifo.takedown(dev); -@@ -566,45 +548,37 @@ static void nouveau_card_takedown(struct drm_device *dev) +@@ -543,19 +622,22 @@ out_fb: + engine->fb.takedown(dev); + out_timer: + engine->timer.takedown(dev); ++out_gpio: ++ engine->gpio.takedown(dev); + out_mc: + engine->mc.takedown(dev); +-out_gpuobj: +- nouveau_gpuobj_takedown(dev); +-out_mem: +- nouveau_sgdma_takedown(dev); +- nouveau_mem_close(dev); ++out_gart: ++ nouveau_mem_gart_fini(dev); + out_instmem: + engine->instmem.takedown(dev); +-out_gpuobj_early: +- nouveau_gpuobj_late_takedown(dev); ++out_gpuobj: ++ nouveau_gpuobj_takedown(dev); ++out_vram: ++ nouveau_mem_vram_fini(dev); + out_bios: + nouveau_bios_takedown(dev); ++out_display_early: ++ engine->display.late_takedown(dev); + out: + vga_client_register(dev->pdev, NULL, NULL, NULL); + return ret; +@@ -566,45 +648,38 @@ 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->init_state != NOUVEAU_CARD_INIT_DOWN) { -- - nouveau_backlight_exit(dev); - - if (dev_priv->channel) { @@ -4094,7 +9554,9 @@ index b02a231..621e080 100644 + } + engine->fb.takedown(dev); + engine->timer.takedown(dev); ++ engine->gpio.takedown(dev); + engine->mc.takedown(dev); ++ engine->display.late_takedown(dev); - nouveau_gpuobj_takedown(dev); - nouveau_mem_close(dev); @@ -4103,20 +9565,19 @@ index b02a231..621e080 100644 + ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_VRAM); + ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_TT); + mutex_unlock(&dev->struct_mutex); -+ nouveau_sgdma_takedown(dev); ++ nouveau_mem_gart_fini(dev); - if (drm_core_check_feature(dev, DRIVER_MODESET)) - drm_irq_uninstall(dev); -+ nouveau_gpuobj_takedown(dev); -+ nouveau_mem_close(dev); + engine->instmem.takedown(dev); ++ nouveau_gpuobj_takedown(dev); ++ nouveau_mem_vram_fini(dev); - nouveau_gpuobj_late_takedown(dev); - nouveau_bios_takedown(dev); + drm_irq_uninstall(dev); - vga_client_register(dev->pdev, NULL, NULL, NULL); -+ nouveau_gpuobj_late_takedown(dev); + nouveau_bios_takedown(dev); - dev_priv->init_state = NOUVEAU_CARD_INIT_DOWN; @@ -4125,15 +9586,20 @@ index b02a231..621e080 100644 } /* here a client dies, release the stuff that was allocated for its -@@ -691,6 +665,7 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) +@@ -691,22 +766,26 @@ 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; + int ret; dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL); - if (!dev_priv) -@@ -699,7 +674,6 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) +- if (!dev_priv) +- return -ENOMEM; ++ if (!dev_priv) { ++ ret = -ENOMEM; ++ goto err_out; ++ } + dev->dev_private = dev_priv; dev_priv->dev = dev; dev_priv->flags = flags & NOUVEAU_FLAGS; @@ -4141,7 +9607,76 @@ index b02a231..621e080 100644 NV_DEBUG(dev, "vendor: 0x%X device: 0x%X class: 0x%X\n", dev->pci_vendor, dev->pci_device, dev->pdev->class); -@@ -812,46 +786,28 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) + + dev_priv->wq = create_workqueue("nouveau"); +- if (!dev_priv->wq) +- return -EINVAL; ++ if (!dev_priv->wq) { ++ ret = -EINVAL; ++ goto err_priv; ++ } + + /* resource 0 is mmio regs */ + /* resource 1 is linear FB */ +@@ -719,7 +798,8 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) + if (!dev_priv->mmio) { + NV_ERROR(dev, "Unable to initialize the mmio mapping. " + "Please report your setup to " DRIVER_EMAIL "\n"); +- return -EINVAL; ++ ret = -EINVAL; ++ goto err_wq; + } + NV_DEBUG(dev, "regs mapped ok at 0x%llx\n", + (unsigned long long)mmio_start_offs); +@@ -765,19 +845,21 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) + case 0xa0: + dev_priv->card_type = NV_50; + break; ++ case 0xc0: ++ dev_priv->card_type = NV_C0; ++ break; + default: + NV_INFO(dev, "Unsupported chipset 0x%08x\n", reg0); +- return -EINVAL; ++ ret = -EINVAL; ++ goto err_mmio; + } + + NV_INFO(dev, "Detected an NV%2x generation card (0x%08x)\n", + dev_priv->card_type, reg0); + +- if (drm_core_check_feature(dev, DRIVER_MODESET)) { +- int ret = nouveau_remove_conflicting_drivers(dev); +- if (ret) +- return ret; +- } ++ ret = nouveau_remove_conflicting_drivers(dev); ++ if (ret) ++ goto err_mmio; + + /* Map PRAMIN BAR, or on older cards, the aperture withing BAR0 */ + if (dev_priv->card_type >= NV_40) { +@@ -791,7 +873,8 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) + dev_priv->ramin_size); + if (!dev_priv->ramin) { + NV_ERROR(dev, "Failed to PRAMIN BAR"); +- return -ENOMEM; ++ ret = -ENOMEM; ++ goto err_mmio; + } + } else { + dev_priv->ramin_size = 1 * 1024 * 1024; +@@ -799,7 +882,8 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) + dev_priv->ramin_size); + if (!dev_priv->ramin) { + NV_ERROR(dev, "Failed to map BAR0 PRAMIN.\n"); +- return -ENOMEM; ++ ret = -ENOMEM; ++ goto err_mmio; + } + } + +@@ -812,46 +896,38 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) dev_priv->flags |= NV_NFORCE2; /* For kernel modesetting, init card now and bring up fbcon */ @@ -4152,20 +9687,31 @@ index b02a231..621e080 100644 - } + ret = nouveau_card_init(dev); + if (ret) -+ return ret; ++ goto err_ramin; 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 allocated yet */ - if (dev_priv) - nouveau_card_takedown(dev); --} -- ++err_ramin: ++ iounmap(dev_priv->ramin); ++err_mmio: ++ iounmap(dev_priv->mmio); ++err_wq: ++ destroy_workqueue(dev_priv->wq); ++err_priv: ++ kfree(dev_priv); ++ dev->dev_private = NULL; ++err_out: ++ return ret; + } + -/* KMS: we need mmio at load time, not when the first drm client opens. */ void nouveau_lastclose(struct drm_device *dev) { @@ -4178,6 +9724,7 @@ index b02a231..621e080 100644 int nouveau_unload(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; ++ struct nouveau_engine *engine = &dev_priv->engine; - if (drm_core_check_feature(dev, DRIVER_MODESET)) { - drm_kms_helper_poll_fini(dev); @@ -4190,15 +9737,12 @@ index b02a231..621e080 100644 - } + drm_kms_helper_poll_fini(dev); + nouveau_fbcon_fini(dev); -+ if (dev_priv->card_type >= NV_50) -+ nv50_display_destroy(dev); -+ else -+ nv04_display_destroy(dev); ++ engine->display.destroy(dev); + nouveau_card_takedown(dev); iounmap(dev_priv->mmio); iounmap(dev_priv->ramin); -@@ -867,8 +823,6 @@ int nouveau_ioctl_getparam(struct drm_device *dev, void *data, +@@ -867,8 +943,6 @@ int nouveau_ioctl_getparam(struct drm_device *dev, void *data, struct drm_nouveau_private *dev_priv = dev->dev_private; struct drm_nouveau_getparam *getparam = data; @@ -4207,7 +9751,7 @@ index b02a231..621e080 100644 switch (getparam->param) { case NOUVEAU_GETPARAM_CHIPSET_ID: getparam->value = dev_priv->chipset; -@@ -937,8 +891,6 @@ nouveau_ioctl_setparam(struct drm_device *dev, void *data, +@@ -937,8 +1011,6 @@ nouveau_ioctl_setparam(struct drm_device *dev, void *data, { struct drm_nouveau_setparam *setparam = data; @@ -4216,8 +9760,17 @@ index b02a231..621e080 100644 switch (setparam->param) { default: NV_ERROR(dev, "unknown parameter %lld\n", setparam->param); +@@ -967,7 +1039,7 @@ bool nouveau_wait_until(struct drm_device *dev, uint64_t timeout, + /* Waits for PGRAPH to go completely idle */ + bool nouveau_wait_for_idle(struct drm_device *dev) + { +- if (!nv_wait(NV04_PGRAPH_STATUS, 0xffffffff, 0x00000000)) { ++ if (!nv_wait(dev, 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; diff --git a/drivers/gpu/drm/nouveau/nv04_crtc.c b/drivers/gpu/drm/nouveau/nv04_crtc.c -index eba687f..1c20c08 100644 +index eba687f..291a4cb 100644 --- a/drivers/gpu/drm/nouveau/nv04_crtc.c +++ b/drivers/gpu/drm/nouveau/nv04_crtc.c @@ -157,6 +157,7 @@ nv_crtc_dpms(struct drm_crtc *crtc, int mode) @@ -4239,11 +9792,60 @@ index eba687f..1c20c08 100644 } static bool +@@ -537,6 +542,9 @@ nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct drm_display_mode * mode) + * 1 << 30 on 0x60.830), for no apparent reason */ + regp->CRTC[NV_CIO_CRE_59] = off_chip_digital; + ++ if (dev_priv->card_type >= NV_30) ++ regp->CRTC[0x9f] = off_chip_digital ? 0x11 : 0x1; ++ + regp->crtc_830 = mode->crtc_vdisplay - 3; + regp->crtc_834 = mode->crtc_vdisplay - 1; + +@@ -710,6 +718,7 @@ static void nv_crtc_destroy(struct drm_crtc *crtc) + + drm_crtc_cleanup(crtc); + ++ nouveau_bo_unmap(nv_crtc->cursor.nvbo); + nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); + kfree(nv_crtc); + } +@@ -820,7 +829,7 @@ nv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, + 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) { ++ if (dev_priv->card_type >= NV_20) { + regp->CRTC[NV_CIO_CRE_47] = arb_lwm >> 8; + crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_47); + } diff --git a/drivers/gpu/drm/nouveau/nv04_dac.c b/drivers/gpu/drm/nouveau/nv04_dac.c -index 1cb19e3..2d0fee5 100644 +index 1cb19e3..ba6423f 100644 --- a/drivers/gpu/drm/nouveau/nv04_dac.c +++ b/drivers/gpu/drm/nouveau/nv04_dac.c -@@ -261,12 +261,11 @@ uint32_t nv17_dac_sample_load(struct drm_encoder *encoder) +@@ -220,6 +220,7 @@ uint32_t nv17_dac_sample_load(struct drm_encoder *encoder) + { + struct drm_device *dev = encoder->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; ++ struct nouveau_gpio_engine *gpio = &dev_priv->engine.gpio; + struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb; + uint32_t sample, testval, regoffset = nv04_dac_output_offset(encoder); + uint32_t saved_powerctrl_2 = 0, saved_powerctrl_4 = 0, saved_routput, +@@ -251,22 +252,21 @@ uint32_t nv17_dac_sample_load(struct drm_encoder *encoder) + 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); ++ saved_gpio1 = gpio->get(dev, DCB_GPIO_TVDAC1); ++ saved_gpio0 = 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); ++ gpio->set(dev, DCB_GPIO_TVDAC1, dcb->type == OUTPUT_TV); ++ 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; @@ -4260,7 +9862,27 @@ index 1cb19e3..2d0fee5 100644 /* nv driver and nv31 use 0xfffffeee, nv34 and 6600 use 0xfffffece */ routput = (saved_routput & 0xfffffece) | head << 8; -@@ -315,9 +314,12 @@ nv17_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector) +@@ -291,6 +291,8 @@ uint32_t nv17_dac_sample_load(struct drm_encoder *encoder) + msleep(5); + + sample = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); ++ /* do it again just in case it's a residual current */ ++ sample &= NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset); + + temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL, +@@ -304,8 +306,8 @@ uint32_t nv17_dac_sample_load(struct drm_encoder *encoder) + 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); ++ gpio->set(dev, DCB_GPIO_TVDAC1, saved_gpio1); ++ gpio->set(dev, DCB_GPIO_TVDAC0, saved_gpio0); + + return sample; + } +@@ -315,9 +317,12 @@ nv17_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector) { struct drm_device *dev = encoder->dev; struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb; @@ -4275,7 +9897,7 @@ index 1cb19e3..2d0fee5 100644 NV_INFO(dev, "Load detected on output %c\n", '@' + ffs(dcb->or)); return connector_status_connected; -@@ -330,6 +332,9 @@ static bool nv04_dac_mode_fixup(struct drm_encoder *encoder, +@@ -330,6 +335,9 @@ static bool nv04_dac_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { @@ -4285,7 +9907,30 @@ index 1cb19e3..2d0fee5 100644 return true; } -@@ -428,6 +433,17 @@ void nv04_dac_update_dacclk(struct drm_encoder *encoder, bool enable) +@@ -337,22 +345,13 @@ 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) +@@ -428,6 +427,17 @@ void nv04_dac_update_dacclk(struct drm_encoder *encoder, bool enable) } } @@ -4303,7 +9948,7 @@ index 1cb19e3..2d0fee5 100644 static void nv04_dac_dpms(struct drm_encoder *encoder, int mode) { struct drm_device *dev = encoder->dev; -@@ -501,11 +517,13 @@ static const struct drm_encoder_funcs nv04_dac_funcs = { +@@ -501,11 +511,13 @@ static const struct drm_encoder_funcs nv04_dac_funcs = { .destroy = nv04_dac_destroy, }; @@ -4319,7 +9964,7 @@ index 1cb19e3..2d0fee5 100644 nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); if (!nv_encoder) -@@ -527,5 +545,6 @@ int nv04_dac_create(struct drm_device *dev, struct dcb_entry *entry) +@@ -527,5 +539,6 @@ int nv04_dac_create(struct drm_device *dev, struct dcb_entry *entry) encoder->possible_crtcs = entry->heads; encoder->possible_clones = 0; @@ -4327,21 +9972,219 @@ index 1cb19e3..2d0fee5 100644 return 0; } diff --git a/drivers/gpu/drm/nouveau/nv04_dfp.c b/drivers/gpu/drm/nouveau/nv04_dfp.c -index 41634d4..3311f3a 100644 +index 41634d4..762d9f2 100644 --- a/drivers/gpu/drm/nouveau/nv04_dfp.c +++ b/drivers/gpu/drm/nouveau/nv04_dfp.c -@@ -413,10 +413,6 @@ static void nv04_dfp_commit(struct drm_encoder *encoder) +@@ -34,6 +34,8 @@ + #include "nouveau_hw.h" + #include "nvreg.h" + ++#include "i2c/sil164.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) +@@ -102,6 +104,8 @@ void nv04_dfp_disable(struct drm_device *dev, int head) + } + /* don't inadvertently turn it on when state written later */ + crtcstate[head].fp_control = FP_TG_CONTROL_OFF; ++ crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX] &= ++ ~NV_CIO_CRE_LCD_ROUTE_MASK; + } + + void nv04_dfp_update_fp_control(struct drm_encoder *encoder, int mode) +@@ -144,6 +148,36 @@ void nv04_dfp_update_fp_control(struct drm_encoder *encoder, int mode) + } + } + ++static struct drm_encoder *get_tmds_slave(struct drm_encoder *encoder) ++{ ++ struct drm_device *dev = encoder->dev; ++ struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb; ++ struct drm_encoder *slave; ++ ++ if (dcb->type != OUTPUT_TMDS || dcb->location == DCB_LOC_ON_CHIP) ++ return NULL; ++ ++ /* Some BIOSes (e.g. the one in a Quadro FX1000) report several ++ * TMDS transmitters at the same I2C address, in the same I2C ++ * bus. This can still work because in that case one of them is ++ * always hard-wired to a reasonable configuration using straps, ++ * and the other one needs to be programmed. ++ * ++ * I don't think there's a way to know which is which, even the ++ * blob programs the one exposed via I2C for *both* heads, so ++ * let's do the same. ++ */ ++ list_for_each_entry(slave, &dev->mode_config.encoder_list, head) { ++ struct dcb_entry *slave_dcb = nouveau_encoder(slave)->dcb; ++ ++ if (slave_dcb->type == OUTPUT_TMDS && get_slave_funcs(slave) && ++ slave_dcb->tmdsconf.slave_addr == dcb->tmdsconf.slave_addr) ++ return slave; ++ } ++ ++ return NULL; ++} ++ + static bool nv04_dfp_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +@@ -221,26 +255,21 @@ static void nv04_dfp_prepare(struct drm_encoder *encoder) + + 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); +- } ++ *cr_lcd = (*cr_lcd & ~NV_CIO_CRE_LCD_ROUTE_MASK) | 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); + } + } + } +@@ -412,10 +441,7 @@ static void nv04_dfp_commit(struct drm_encoder *encoder) + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct dcb_entry *dcbe = nv_encoder->dcb; int head = nouveau_crtc(encoder->crtc)->index; - +- - 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)); -- ++ struct drm_encoder *slave_encoder; + if (dcbe->type == OUTPUT_TMDS) run_tmds_table(dev, dcbe, head, nv_encoder->mode.clock); - else if (dcbe->type == OUTPUT_LVDS) -@@ -584,11 +580,12 @@ static const struct drm_encoder_funcs nv04_dfp_funcs = { +@@ -433,6 +459,12 @@ static void nv04_dfp_commit(struct drm_encoder *encoder) + else + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000); + ++ /* Init external transmitters */ ++ slave_encoder = get_tmds_slave(encoder); ++ if (slave_encoder) ++ get_slave_funcs(slave_encoder)->mode_set( ++ slave_encoder, &nv_encoder->mode, &nv_encoder->mode); ++ + helper->dpms(encoder, DRM_MODE_DPMS_ON); + + NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n", +@@ -440,6 +472,27 @@ static void nv04_dfp_commit(struct drm_encoder *encoder) + nv_crtc->index, '@' + ffs(nv_encoder->dcb->or)); + } + ++static void nv04_dfp_update_backlight(struct drm_encoder *encoder, int mode) ++{ ++#ifdef __powerpc__ ++ struct drm_device *dev = encoder->dev; ++ ++ /* BIOS scripts usually take care of the backlight, thanks ++ * Apple for your consistency. ++ */ ++ if (dev->pci_device == 0x0179 || dev->pci_device == 0x0189 || ++ dev->pci_device == 0x0329) { ++ if (mode == DRM_MODE_DPMS_ON) { ++ nv_mask(dev, NV_PBUS_DEBUG_DUALHEAD_CTL, 0, 1 << 31); ++ nv_mask(dev, NV_PCRTC_GPIO_EXT, 3, 1); ++ } else { ++ nv_mask(dev, NV_PBUS_DEBUG_DUALHEAD_CTL, 1 << 31, 0); ++ nv_mask(dev, NV_PCRTC_GPIO_EXT, 3, 0); ++ } ++ } ++#endif ++} ++ + static inline bool is_powersaving_dpms(int mode) + { + return (mode != DRM_MODE_DPMS_ON); +@@ -487,6 +540,7 @@ static void nv04_lvds_dpms(struct drm_encoder *encoder, int mode) + LVDS_PANEL_OFF, 0); + } + ++ nv04_dfp_update_backlight(encoder, mode); + nv04_dfp_update_fp_control(encoder, mode); + + if (mode == DRM_MODE_DPMS_ON) +@@ -510,6 +564,7 @@ static void nv04_tmds_dpms(struct drm_encoder *encoder, int mode) + NV_INFO(dev, "Setting dpms mode %d on tmds encoder (output %d)\n", + mode, nv_encoder->dcb->index); + ++ nv04_dfp_update_backlight(encoder, mode); + nv04_dfp_update_fp_control(encoder, mode); + } + +@@ -554,10 +609,42 @@ static void nv04_dfp_destroy(struct drm_encoder *encoder) + + NV_DEBUG_KMS(encoder->dev, "\n"); + ++ if (get_slave_funcs(encoder)) ++ get_slave_funcs(encoder)->destroy(encoder); ++ + drm_encoder_cleanup(encoder); + kfree(nv_encoder); + } + ++static void nv04_tmds_slave_init(struct drm_encoder *encoder) ++{ ++ struct drm_device *dev = encoder->dev; ++ struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb; ++ struct nouveau_i2c_chan *i2c = nouveau_i2c_find(dev, 2); ++ struct i2c_board_info info[] = { ++ { ++ .type = "sil164", ++ .addr = (dcb->tmdsconf.slave_addr == 0x7 ? 0x3a : 0x38), ++ .platform_data = &(struct sil164_encoder_params) { ++ SIL164_INPUT_EDGE_RISING ++ } ++ }, ++ { } ++ }; ++ int type; ++ ++ if (!nv_gf4_disp_arch(dev) || !i2c || ++ get_tmds_slave(encoder)) ++ return; ++ ++ type = nouveau_i2c_identify(dev, "TMDS transmitter", info, 2); ++ if (type < 0) ++ return; ++ ++ drm_i2c_encoder_init(dev, to_encoder_slave(encoder), ++ &i2c->adapter, &info[type]); ++} ++ + static const struct drm_encoder_helper_funcs nv04_lvds_helper_funcs = { + .dpms = nv04_lvds_dpms, + .save = nv04_dfp_save, +@@ -584,11 +671,12 @@ static const struct drm_encoder_funcs nv04_dfp_funcs = { .destroy = nv04_dfp_destroy, }; @@ -4356,7 +10199,7 @@ index 41634d4..3311f3a 100644 int type; switch (entry->type) { -@@ -613,11 +610,12 @@ int nv04_dfp_create(struct drm_device *dev, struct dcb_entry *entry) +@@ -613,11 +701,16 @@ int nv04_dfp_create(struct drm_device *dev, struct dcb_entry *entry) nv_encoder->dcb = entry; nv_encoder->or = ffs(entry->or) - 1; @@ -4367,11 +10210,15 @@ index 41634d4..3311f3a 100644 encoder->possible_crtcs = entry->heads; encoder->possible_clones = 0; ++ if (entry->type == OUTPUT_TMDS && ++ entry->location != DCB_LOC_ON_CHIP) ++ nv04_tmds_slave_init(encoder); ++ + drm_mode_connector_attach_encoder(connector, encoder); return 0; } diff --git a/drivers/gpu/drm/nouveau/nv04_display.c b/drivers/gpu/drm/nouveau/nv04_display.c -index c7898b4..c6df391 100644 +index c7898b4..9e28cf7 100644 --- a/drivers/gpu/drm/nouveau/nv04_display.c +++ b/drivers/gpu/drm/nouveau/nv04_display.c @@ -32,8 +32,6 @@ @@ -4410,22 +10257,54 @@ index c7898b4..c6df391 100644 if (slaved_on_A && !tvA) dev_priv->crtc_owner = 0x0; else if (slaved_on_B && !tvB) -@@ -79,14 +73,6 @@ nv04_display_store_initial_head_owner(struct drm_device *dev) +@@ -79,14 +73,40 @@ nv04_display_store_initial_head_owner(struct drm_device *dev) else dev_priv->crtc_owner = 0x0; } -- ++} ++ ++int ++nv04_display_early_init(struct drm_device *dev) ++{ ++ /* Make the I2C buses accessible. */ ++ if (!nv_gf4_disp_arch(dev)) { ++ uint32_t pmc_enable = nv_rd32(dev, NV03_PMC_ENABLE); ++ ++ if (!(pmc_enable & 1)) ++ nv_wr32(dev, NV03_PMC_ENABLE, pmc_enable | 1); ++ } + -ownerknown: - NV_INFO(dev, "Initial CRTC_OWNER is %d\n", dev_priv->crtc_owner); -- ++ /* Unlock the VGA CRTCs. */ ++ NVLockVgaCrtcs(dev, false); ++ ++ /* Make sure the CRTCs aren't in slaved mode. */ ++ if (nv_two_heads(dev)) { ++ nv04_display_store_initial_head_owner(dev); ++ NVSetOwner(dev, 0); ++ } ++ ++ return 0; ++} ++ ++void ++nv04_display_late_takedown(struct drm_device *dev) ++{ ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ ++ if (nv_two_heads(dev)) ++ NVSetOwner(dev, 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); ++ NVLockVgaCrtcs(dev, true); } int -@@ -94,14 +80,20 @@ nv04_display_create(struct drm_device *dev) +@@ -94,14 +114,13 @@ nv04_display_create(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct dcb_table *dcb = &dev_priv->vbios.dcb; @@ -4437,17 +10316,11 @@ index c7898b4..c6df391 100644 NV_DEBUG_KMS(dev, "\n"); - if (nv_two_heads(dev)) -+ NVLockVgaCrtcs(dev, false); -+ -+ if (nv_two_heads(dev)) { - nv04_display_store_initial_head_owner(dev); -+ NVSetOwner(dev, 0); -+ } -+ +- nv04_display_store_initial_head_owner(dev); nouveau_hw_save_vga_fonts(dev, 1); drm_mode_config_init(dev); -@@ -132,19 +124,23 @@ nv04_display_create(struct drm_device *dev) +@@ -132,19 +151,23 @@ nv04_display_create(struct drm_device *dev) for (i = 0; i < dcb->entries; i++) { struct dcb_entry *dcbent = &dcb->entry[i]; @@ -4475,7 +10348,7 @@ index c7898b4..c6df391 100644 break; default: NV_WARN(dev, "DCB type %d not known\n", dcbent->type); -@@ -155,12 +151,16 @@ nv04_display_create(struct drm_device *dev) +@@ -155,12 +178,16 @@ nv04_display_create(struct drm_device *dev) continue; } @@ -4496,15 +10369,7 @@ index c7898b4..c6df391 100644 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) crtc->funcs->save(crtc); -@@ -176,6 +176,7 @@ nv04_display_create(struct drm_device *dev) - void - nv04_display_destroy(struct drm_device *dev) - { -+ struct drm_nouveau_private *dev_priv = dev->dev_private; - struct drm_encoder *encoder; - struct drm_crtc *crtc; - -@@ -191,8 +192,6 @@ nv04_display_destroy(struct drm_device *dev) +@@ -191,8 +218,6 @@ nv04_display_destroy(struct drm_device *dev) } /* Restore state */ @@ -4513,28 +10378,28 @@ index c7898b4..c6df391 100644 list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { struct drm_encoder_helper_funcs *func = encoder->helper_private; -@@ -205,12 +204,15 @@ nv04_display_destroy(struct drm_device *dev) - drm_mode_config_cleanup(dev); - +@@ -207,15 +232,12 @@ nv04_display_destroy(struct drm_device *dev) nouveau_hw_save_vga_fonts(dev, 0); -+ -+ if (nv_two_heads(dev)) -+ NVSetOwner(dev, dev_priv->crtc_owner); -+ NVLockVgaCrtcs(dev, true); } - void - nv04_display_restore(struct drm_device *dev) +-void +-nv04_display_restore(struct drm_device *dev) ++int ++nv04_display_init(struct drm_device *dev) { - struct drm_nouveau_private *dev_priv = dev->dev_private; struct drm_encoder *encoder; struct drm_crtc *crtc; -@@ -232,13 +234,5 @@ nv04_display_restore(struct drm_device *dev) - +- 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. +@@ -233,12 +255,6 @@ nv04_display_restore(struct drm_device *dev) 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); @@ -4542,13 +10407,123 @@ index c7898b4..c6df391 100644 - } - - NVLockVgaCrtcs(dev, true); ++ return 0; } +diff --git a/drivers/gpu/drm/nouveau/nv04_fbcon.c b/drivers/gpu/drm/nouveau/nv04_fbcon.c +index 1eeac4f..33e4c93 100644 +--- a/drivers/gpu/drm/nouveau/nv04_fbcon.c ++++ b/drivers/gpu/drm/nouveau/nv04_fbcon.c +@@ -25,6 +25,7 @@ + #include "drmP.h" + #include "nouveau_drv.h" + #include "nouveau_dma.h" ++#include "nouveau_ramht.h" + #include "nouveau_fbcon.h" + + void +@@ -169,11 +170,9 @@ nv04_fbcon_grobj_new(struct drm_device *dev, int class, uint32_t handle) + if (ret) + return ret; + +- ret = nouveau_gpuobj_ref_add(dev, dev_priv->channel, handle, obj, NULL); +- if (ret) +- return ret; +- +- return 0; ++ ret = nouveau_ramht_insert(dev_priv->channel, handle, obj); ++ nouveau_gpuobj_ref(NULL, &obj); ++ return ret; + } + + int diff --git a/drivers/gpu/drm/nouveau/nv04_fifo.c b/drivers/gpu/drm/nouveau/nv04_fifo.c -index 66fe559..06cedd9 100644 +index 66fe559..708293b 100644 --- a/drivers/gpu/drm/nouveau/nv04_fifo.c +++ b/drivers/gpu/drm/nouveau/nv04_fifo.c -@@ -112,6 +112,12 @@ nv04_fifo_channel_id(struct drm_device *dev) +@@ -27,8 +27,9 @@ + #include "drmP.h" + #include "drm.h" + #include "nouveau_drv.h" ++#include "nouveau_ramht.h" + +-#define NV04_RAMFC(c) (dev_priv->ramfc_offset + ((c) * NV04_RAMFC__SIZE)) ++#define NV04_RAMFC(c) (dev_priv->ramfc->pinst + ((c) * NV04_RAMFC__SIZE)) + #define NV04_RAMFC__SIZE 32 + #define NV04_RAMFC_DMA_PUT 0x00 + #define NV04_RAMFC_DMA_GET 0x04 +@@ -38,10 +39,8 @@ + #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) ++#define RAMFC_WR(offset, val) nv_wo32(chan->ramfc, NV04_RAMFC_##offset, (val)) ++#define RAMFC_RD(offset) nv_ro32(chan->ramfc, NV04_RAMFC_##offset) + + void + nv04_fifo_disable(struct drm_device *dev) +@@ -72,37 +71,32 @@ nv04_fifo_reassign(struct drm_device *dev, bool enable) + } + + bool +-nv04_fifo_cache_flush(struct drm_device *dev) +-{ +- 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, NV03_PFIFO_CACHE1_GET) == +- nv_rd32(dev, NV03_PFIFO_CACHE1_PUT)) +- return true; +- +- } while (ptimer->read(dev) - start < 100000000); +- +- NV_ERROR(dev, "Timeout flushing the PFIFO cache.\n"); +- +- return false; +-} +- +-bool + nv04_fifo_cache_pull(struct drm_device *dev, bool enable) + { +- uint32_t pull = nv_rd32(dev, NV04_PFIFO_CACHE1_PULL0); ++ int pull = nv_mask(dev, NV04_PFIFO_CACHE1_PULL0, 1, enable); ++ ++ if (!enable) { ++ /* In some cases the PFIFO puller may be left in an ++ * inconsistent state if you try to stop it when it's ++ * busy translating handles. Sometimes you get a ++ * PFIFO_CACHE_ERROR, sometimes it just fails silently ++ * sending incorrect instance offsets to PGRAPH after ++ * it's started up again. To avoid the latter we ++ * invalidate the most recently calculated instance. ++ */ ++ if (!nv_wait(dev, NV04_PFIFO_CACHE1_PULL0, ++ NV04_PFIFO_CACHE1_PULL0_HASH_BUSY, 0)) ++ NV_ERROR(dev, "Timeout idling the PFIFO puller.\n"); ++ ++ if (nv_rd32(dev, NV04_PFIFO_CACHE1_PULL0) & ++ NV04_PFIFO_CACHE1_PULL0_HASH_FAILED) ++ nv_wr32(dev, NV03_PFIFO_INTR_0, ++ NV_PFIFO_INTR_CACHE_ERROR); + +- if (enable) { +- nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, pull | 1); +- } else { +- nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, pull & ~1); + nv_wr32(dev, NV04_PFIFO_CACHE1_HASH, 0); + } + +- return !!(pull & 1); ++ return pull & 1; + } + + int +@@ -112,6 +106,12 @@ nv04_fifo_channel_id(struct drm_device *dev) NV03_PFIFO_CACHE1_PUSH1_CHID_MASK; } @@ -4561,14 +10536,23 @@ index 66fe559..06cedd9 100644 int nv04_fifo_create_context(struct nouveau_channel *chan) { -@@ -131,18 +137,13 @@ nv04_fifo_create_context(struct nouveau_channel *chan) +@@ -124,25 +124,20 @@ nv04_fifo_create_context(struct nouveau_channel *chan) + NV04_RAMFC__SIZE, + NVOBJ_FLAG_ZERO_ALLOC | + NVOBJ_FLAG_ZERO_FREE, +- NULL, &chan->ramfc); ++ &chan->ramfc); + if (ret) + return ret; + spin_lock_irqsave(&dev_priv->context_switch_lock, flags); /* 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_INSTANCE, chan->pushbuf->instance >> 4); ++ RAMFC_WR(DMA_INSTANCE, chan->pushbuf->pinst >> 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 | @@ -4581,7 +10565,16 @@ index 66fe559..06cedd9 100644 /* enable the fifo dma operation */ nv_wr32(dev, NV04_PFIFO_MODE, -@@ -169,8 +170,6 @@ nv04_fifo_do_load_context(struct drm_device *dev, int chid) +@@ -160,7 +155,7 @@ nv04_fifo_destroy_context(struct nouveau_channel *chan) + nv_wr32(dev, NV04_PFIFO_MODE, + nv_rd32(dev, NV04_PFIFO_MODE) & ~(1 << chan->id)); + +- nouveau_gpuobj_ref_del(dev, &chan->ramfc); ++ nouveau_gpuobj_ref(NULL, &chan->ramfc); + } + + static void +@@ -169,8 +164,6 @@ 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; @@ -4590,7 +10583,7 @@ index 66fe559..06cedd9 100644 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); -@@ -181,8 +180,6 @@ nv04_fifo_do_load_context(struct drm_device *dev, int chid) +@@ -181,8 +174,6 @@ nv04_fifo_do_load_context(struct drm_device *dev, int chid) nv_wr32(dev, NV04_PFIFO_CACHE1_ENGINE, nv_ri32(dev, fc + 20)); nv_wr32(dev, NV04_PFIFO_CACHE1_PULL1, nv_ri32(dev, fc + 24)); @@ -4599,7 +10592,7 @@ index 66fe559..06cedd9 100644 nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0); nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, 0); } -@@ -223,7 +220,6 @@ nv04_fifo_unload_context(struct drm_device *dev) +@@ -223,7 +214,6 @@ nv04_fifo_unload_context(struct drm_device *dev) return -EINVAL; } @@ -4607,7 +10600,7 @@ index 66fe559..06cedd9 100644 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; -@@ -233,7 +229,6 @@ nv04_fifo_unload_context(struct drm_device *dev) +@@ -233,7 +223,6 @@ nv04_fifo_unload_context(struct drm_device *dev) 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)); @@ -4615,7 +10608,22 @@ index 66fe559..06cedd9 100644 nv04_fifo_do_load_context(dev, pfifo->channels - 1); nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, pfifo->channels - 1); -@@ -297,6 +292,7 @@ nv04_fifo_init(struct drm_device *dev) +@@ -269,10 +258,10 @@ 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); ++ ((dev_priv->ramht->bits - 9) << 16) | ++ (dev_priv->ramht->gpuobj->pinst >> 8)); ++ nv_wr32(dev, NV03_PFIFO_RAMRO, dev_priv->ramro->pinst >> 8); ++ nv_wr32(dev, NV03_PFIFO_RAMFC, dev_priv->ramfc->pinst >> 8); + } + + static void +@@ -297,6 +286,7 @@ nv04_fifo_init(struct drm_device *dev) nv04_fifo_init_intr(dev); pfifo->enable(dev); @@ -4647,30 +10655,158 @@ index 618355e..c897342 100644 } diff --git a/drivers/gpu/drm/nouveau/nv04_instmem.c b/drivers/gpu/drm/nouveau/nv04_instmem.c -index a3b9563..4408232 100644 +index a3b9563..0b5ae29 100644 --- a/drivers/gpu/drm/nouveau/nv04_instmem.c +++ b/drivers/gpu/drm/nouveau/nv04_instmem.c -@@ -49,10 +49,8 @@ nv04_instmem_determine_amount(struct drm_device *dev) - NV_DEBUG(dev, "RAMIN size: %dKiB\n", dev_priv->ramin_rsvd_vram >> 10); +@@ -1,6 +1,7 @@ + #include "drmP.h" + #include "drm.h" + #include "nouveau_drv.h" ++#include "nouveau_ramht.h" - /* 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); + /* returns the size of fifo context */ + static int +@@ -17,104 +18,51 @@ nouveau_fifo_ctx_size(struct drm_device *dev) + return 32; } - static void -@@ -106,7 +104,7 @@ int nv04_instmem_init(struct drm_device *dev) +-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) { +- 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 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) ++int nv04_instmem_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - uint32_t offset; -- int ret = 0; +- 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); ++ struct nouveau_gpuobj *ramht = NULL; ++ u32 offset, length; + int ret; ++ ++ /* RAMIN always available */ ++ dev_priv->ramin_available = true; ++ ++ /* Setup shared RAMHT */ ++ ret = nouveau_gpuobj_new_fake(dev, 0x10000, ~0, 4096, ++ NVOBJ_FLAG_ZERO_ALLOC, &ramht); ++ if (ret) ++ return ret; ++ ++ ret = nouveau_ramht_new(dev, ramht, &dev_priv->ramht); ++ nouveau_gpuobj_ref(NULL, &ramht); ++ if (ret) ++ return ret; ++ ++ /* And RAMRO */ ++ ret = nouveau_gpuobj_new_fake(dev, 0x11200, ~0, 512, ++ NVOBJ_FLAG_ZERO_ALLOC, &dev_priv->ramro); ++ if (ret) ++ return ret; ++ ++ /* And RAMFC */ ++ length = dev_priv->engine.fifo.channels * nouveau_fifo_ctx_size(dev); + switch (dev_priv->card_type) { + case NV_40: +- dev_priv->ramfc_offset = 0x20000; ++ offset = 0x20000; + break; +- case NV_30: +- case NV_20: +- case NV_10: +- case NV_04: + default: +- dev_priv->ramfc_offset = 0x11400; ++ offset = 0x11400; + break; + } +- NV_DEBUG(dev, "RAMFC offset=0x%x, size=%d\n", dev_priv->ramfc_offset, +- dev_priv->ramfc_size); +-} - nv04_instmem_determine_amount(dev); - nv04_instmem_configure_fixed_tables(dev); -@@ -129,14 +127,14 @@ int nv04_instmem_init(struct drm_device *dev) +-int nv04_instmem_init(struct drm_device *dev) +-{ +- struct drm_nouveau_private *dev_priv = dev->dev_private; +- uint32_t offset; +- int ret = 0; ++ ret = nouveau_gpuobj_new_fake(dev, offset, ~0, length, ++ NVOBJ_FLAG_ZERO_ALLOC, &dev_priv->ramfc); ++ if (ret) ++ return ret; + +- 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; ++ /* Only allow space after RAMFC to be used for object allocation */ ++ offset += length; + + /* It appears RAMRO (or something?) is controlled by 0x2220/0x2230 + * on certain NV4x chipsets as well as RAMFC. When 0x2230 == 0 +@@ -129,69 +77,52 @@ int nv04_instmem_init(struct drm_device *dev) offset = 0x40000; } @@ -4690,7 +10826,56 @@ index a3b9563..4408232 100644 } void -@@ -186,12 +184,7 @@ nv04_instmem_unbind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj) + nv04_instmem_takedown(struct drm_device *dev) + { ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ ++ nouveau_ramht_ref(NULL, &dev_priv->ramht, NULL); ++ nouveau_gpuobj_ref(NULL, &dev_priv->ramro); ++ nouveau_gpuobj_ref(NULL, &dev_priv->ramfc); + } + + int +-nv04_instmem_populate(struct drm_device *dev, struct nouveau_gpuobj *gpuobj, uint32_t *sz) ++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 @@ -4720,7 +10905,7 @@ index 617ed1e..2af43a1 100644 } diff --git a/drivers/gpu/drm/nouveau/nv04_tv.c b/drivers/gpu/drm/nouveau/nv04_tv.c -index c4e3404..94e299c 100644 +index c4e3404..9915a3b 100644 --- a/drivers/gpu/drm/nouveau/nv04_tv.c +++ b/drivers/gpu/drm/nouveau/nv04_tv.c @@ -34,69 +34,26 @@ @@ -4801,14 +10986,48 @@ index c4e3404..94e299c 100644 #define PLLSEL_TV_CRTC1_MASK \ (NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 \ | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1) -@@ -214,30 +171,32 @@ static void nv04_tv_commit(struct drm_encoder *encoder) +@@ -132,7 +89,7 @@ static void nv04_tv_dpms(struct drm_encoder *encoder, int mode) + + NVWriteRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT, state->pllsel); + +- to_encoder_slave(encoder)->slave_funcs->dpms(encoder, mode); ++ get_slave_funcs(encoder)->dpms(encoder, mode); + } + + static void nv04_tv_bind(struct drm_device *dev, int head, bool bind) +@@ -142,12 +99,10 @@ static void nv04_tv_bind(struct drm_device *dev, int head, bool bind) + + state->tv_setup = 0; + +- if (bind) { +- state->CRTC[NV_CIO_CRE_LCD__INDEX] = 0; ++ if (bind) + state->CRTC[NV_CIO_CRE_49] |= 0x10; +- } else { ++ else + state->CRTC[NV_CIO_CRE_49] &= ~0x10; +- } + + NVWriteVgaCrtc(dev, head, NV_CIO_CRE_LCD__INDEX, + state->CRTC[NV_CIO_CRE_LCD__INDEX]); +@@ -195,7 +150,7 @@ static void nv04_tv_mode_set(struct drm_encoder *encoder, + regp->tv_vskew = 1; + regp->tv_vsync_delay = 1; + +- to_encoder_slave(encoder)->slave_funcs->mode_set(encoder, mode, adjusted_mode); ++ get_slave_funcs(encoder)->mode_set(encoder, mode, adjusted_mode); + } + + static void nv04_tv_commit(struct drm_encoder *encoder) +@@ -214,30 +169,31 @@ static void nv04_tv_commit(struct drm_encoder *encoder) 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); - +- to_encoder_slave(encoder)->slave_funcs->destroy(encoder); +- ++ get_slave_funcs(encoder)->destroy(encoder); drm_encoder_cleanup(encoder); - kfree(nv_encoder); @@ -4846,7 +11065,7 @@ index c4e3404..94e299c 100644 if (type < 0) return type; -@@ -246,41 +205,32 @@ int nv04_tv_create(struct drm_device *dev, struct dcb_entry *entry) +@@ -246,40 +202,31 @@ int nv04_tv_create(struct drm_device *dev, struct dcb_entry *entry) if (!nv_encoder) return -ENOMEM; @@ -4889,23 +11108,22 @@ index c4e3404..94e299c 100644 + goto fail_cleanup; /* Fill the function pointers */ - sfuncs = to_encoder_slave(encoder)->slave_funcs; - +- sfuncs = to_encoder_slave(encoder)->slave_funcs; +- - *funcs = (struct drm_encoder_funcs) { - .destroy = nv04_tv_destroy, - }; -- ++ sfuncs = get_slave_funcs(encoder); + *hfuncs = (struct drm_encoder_helper_funcs) { .dpms = nv04_tv_dpms, - .save = sfuncs->save, -@@ -292,14 +242,17 @@ int nv04_tv_create(struct drm_device *dev, struct dcb_entry *entry) +@@ -292,14 +239,16 @@ int nv04_tv_create(struct drm_device *dev, struct dcb_entry *entry) .detect = sfuncs->detect, }; - /* Set the slave encoder configuration */ - sfuncs->set_config(encoder, nv04_tv_encoder_info[type].params); + /* Attach it to the specified connector. */ -+ sfuncs->set_config(encoder, nv04_tv_encoder_info[type].platform_data); + sfuncs->create_resources(encoder, connector); + drm_mode_connector_attach_encoder(connector, encoder); @@ -4921,18 +11139,41 @@ index c4e3404..94e299c 100644 return ret; } diff --git a/drivers/gpu/drm/nouveau/nv10_fifo.c b/drivers/gpu/drm/nouveau/nv10_fifo.c -index 7aeabf2..7a4069c 100644 +index 7aeabf2..f1b03ad 100644 --- a/drivers/gpu/drm/nouveau/nv10_fifo.c +++ b/drivers/gpu/drm/nouveau/nv10_fifo.c -@@ -55,7 +55,6 @@ nv10_fifo_create_context(struct nouveau_channel *chan) +@@ -27,8 +27,9 @@ + #include "drmP.h" + #include "drm.h" + #include "nouveau_drv.h" ++#include "nouveau_ramht.h" + +-#define NV10_RAMFC(c) (dev_priv->ramfc_offset + ((c) * NV10_RAMFC__SIZE)) ++#define NV10_RAMFC(c) (dev_priv->ramfc->pinst + ((c) * NV10_RAMFC__SIZE)) + #define NV10_RAMFC__SIZE ((dev_priv->chipset) >= 0x17 ? 64 : 32) + + int +@@ -48,17 +49,16 @@ nv10_fifo_create_context(struct nouveau_channel *chan) + + 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); ++ NVOBJ_FLAG_ZERO_FREE, &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); -@@ -66,7 +65,6 @@ nv10_fifo_create_context(struct nouveau_channel *chan) +- nv_wi32(dev, fc + 12, chan->pushbuf->instance >> 4); ++ nv_wi32(dev, fc + 12, chan->pushbuf->pinst >> 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 | +@@ -66,7 +66,6 @@ nv10_fifo_create_context(struct nouveau_channel *chan) NV_PFIFO_CACHE1_BIG_ENDIAN | #endif 0); @@ -4940,7 +11181,16 @@ index 7aeabf2..7a4069c 100644 /* enable the fifo dma operation */ nv_wr32(dev, NV04_PFIFO_MODE, -@@ -91,8 +89,6 @@ nv10_fifo_do_load_context(struct drm_device *dev, int chid) +@@ -82,7 +81,7 @@ nv10_fifo_destroy_context(struct nouveau_channel *chan) + nv_wr32(dev, NV04_PFIFO_MODE, + nv_rd32(dev, NV04_PFIFO_MODE) & ~(1 << chan->id)); + +- nouveau_gpuobj_ref_del(dev, &chan->ramfc); ++ nouveau_gpuobj_ref(NULL, &chan->ramfc); + } + + static void +@@ -91,8 +90,6 @@ 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; @@ -4949,7 +11199,7 @@ index 7aeabf2..7a4069c 100644 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)); -@@ -117,8 +113,6 @@ nv10_fifo_do_load_context(struct drm_device *dev, int chid) +@@ -117,8 +114,6 @@ nv10_fifo_do_load_context(struct drm_device *dev, int chid) nv_wr32(dev, NV10_PFIFO_CACHE1_DMA_SUBROUTINE, nv_ri32(dev, fc + 48)); out: @@ -4958,7 +11208,7 @@ index 7aeabf2..7a4069c 100644 nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0); nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, 0); } -@@ -155,8 +149,6 @@ nv10_fifo_unload_context(struct drm_device *dev) +@@ -155,8 +150,6 @@ nv10_fifo_unload_context(struct drm_device *dev) return 0; fc = NV10_RAMFC(chid); @@ -4967,7 +11217,7 @@ index 7aeabf2..7a4069c 100644 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)); -@@ -179,8 +171,6 @@ nv10_fifo_unload_context(struct drm_device *dev) +@@ -179,8 +172,6 @@ nv10_fifo_unload_context(struct drm_device *dev) nv_wi32(dev, fc + 48, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET)); out: @@ -4976,11 +11226,481 @@ index 7aeabf2..7a4069c 100644 nv10_fifo_do_load_context(dev, pfifo->channels - 1); nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, pfifo->channels - 1); return 0; +@@ -212,14 +203,14 @@ 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); ++ ((dev_priv->ramht->bits - 9) << 16) | ++ (dev_priv->ramht->gpuobj->pinst >> 8)); ++ nv_wr32(dev, NV03_PFIFO_RAMRO, dev_priv->ramro->pinst >> 8); + + if (dev_priv->chipset < 0x17) { +- nv_wr32(dev, NV03_PFIFO_RAMFC, dev_priv->ramfc_offset >> 8); ++ nv_wr32(dev, NV03_PFIFO_RAMFC, dev_priv->ramfc->pinst >> 8); + } else { +- nv_wr32(dev, NV03_PFIFO_RAMFC, (dev_priv->ramfc_offset >> 8) | ++ nv_wr32(dev, NV03_PFIFO_RAMFC, (dev_priv->ramfc->pinst >> 8) | + (1 << 16) /* 64 Bytes entry*/); + /* XXX nvidia blob set bit 18, 21,23 for nv20 & nv30 */ + } +diff --git a/drivers/gpu/drm/nouveau/nv10_gpio.c b/drivers/gpu/drm/nouveau/nv10_gpio.c +new file mode 100644 +index 0000000..007fc29 +--- /dev/null ++++ b/drivers/gpu/drm/nouveau/nv10_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 ++nv10_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, ®, &shift, &mask)) ++ return -ENODEV; ++ ++ value = NVReadCRTC(dev, 0, reg) >> shift; ++ ++ return (ent->invert ? 1 : 0) ^ (value & 1); ++} ++ ++int ++nv10_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, ®, &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/nv10_graph.c b/drivers/gpu/drm/nouveau/nv10_graph.c +index fcf2cdd..8e68c97 100644 +--- a/drivers/gpu/drm/nouveau/nv10_graph.c ++++ b/drivers/gpu/drm/nouveau/nv10_graph.c +@@ -43,51 +43,51 @@ struct pipe_state { + }; + + 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_SWITCH(0), ++ NV10_PGRAPH_CTX_SWITCH(1), ++ NV10_PGRAPH_CTX_SWITCH(2), ++ NV10_PGRAPH_CTX_SWITCH(3), ++ NV10_PGRAPH_CTX_SWITCH(4), ++ NV10_PGRAPH_CTX_CACHE(0, 0), ++ NV10_PGRAPH_CTX_CACHE(0, 1), ++ NV10_PGRAPH_CTX_CACHE(0, 2), ++ NV10_PGRAPH_CTX_CACHE(0, 3), ++ NV10_PGRAPH_CTX_CACHE(0, 4), ++ NV10_PGRAPH_CTX_CACHE(1, 0), ++ NV10_PGRAPH_CTX_CACHE(1, 1), ++ NV10_PGRAPH_CTX_CACHE(1, 2), ++ NV10_PGRAPH_CTX_CACHE(1, 3), ++ NV10_PGRAPH_CTX_CACHE(1, 4), ++ NV10_PGRAPH_CTX_CACHE(2, 0), ++ NV10_PGRAPH_CTX_CACHE(2, 1), ++ NV10_PGRAPH_CTX_CACHE(2, 2), ++ NV10_PGRAPH_CTX_CACHE(2, 3), ++ NV10_PGRAPH_CTX_CACHE(2, 4), ++ NV10_PGRAPH_CTX_CACHE(3, 0), ++ NV10_PGRAPH_CTX_CACHE(3, 1), ++ NV10_PGRAPH_CTX_CACHE(3, 2), ++ NV10_PGRAPH_CTX_CACHE(3, 3), ++ NV10_PGRAPH_CTX_CACHE(3, 4), ++ NV10_PGRAPH_CTX_CACHE(4, 0), ++ NV10_PGRAPH_CTX_CACHE(4, 1), ++ NV10_PGRAPH_CTX_CACHE(4, 2), ++ NV10_PGRAPH_CTX_CACHE(4, 3), ++ NV10_PGRAPH_CTX_CACHE(4, 4), ++ NV10_PGRAPH_CTX_CACHE(5, 0), ++ NV10_PGRAPH_CTX_CACHE(5, 1), ++ NV10_PGRAPH_CTX_CACHE(5, 2), ++ NV10_PGRAPH_CTX_CACHE(5, 3), ++ NV10_PGRAPH_CTX_CACHE(5, 4), ++ NV10_PGRAPH_CTX_CACHE(6, 0), ++ NV10_PGRAPH_CTX_CACHE(6, 1), ++ NV10_PGRAPH_CTX_CACHE(6, 2), ++ NV10_PGRAPH_CTX_CACHE(6, 3), ++ NV10_PGRAPH_CTX_CACHE(6, 4), ++ NV10_PGRAPH_CTX_CACHE(7, 0), ++ NV10_PGRAPH_CTX_CACHE(7, 1), ++ NV10_PGRAPH_CTX_CACHE(7, 2), ++ NV10_PGRAPH_CTX_CACHE(7, 3), ++ NV10_PGRAPH_CTX_CACHE(7, 4), + NV10_PGRAPH_CTX_USER, + NV04_PGRAPH_DMA_START_0, + NV04_PGRAPH_DMA_START_1, +@@ -653,6 +653,78 @@ static int nv17_graph_ctx_regs_find_offset(struct drm_device *dev, int reg) + return -1; + } + ++static void nv10_graph_load_dma_vtxbuf(struct nouveau_channel *chan, ++ uint32_t inst) ++{ ++ struct drm_device *dev = chan->dev; ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; ++ uint32_t st2, st2_dl, st2_dh, fifo_ptr, fifo[0x60/4]; ++ uint32_t ctx_user, ctx_switch[5]; ++ int i, subchan = -1; ++ ++ /* NV10TCL_DMA_VTXBUF (method 0x18c) modifies hidden state ++ * that cannot be restored via MMIO. Do it through the FIFO ++ * instead. ++ */ ++ ++ /* Look for a celsius object */ ++ for (i = 0; i < 8; i++) { ++ int class = nv_rd32(dev, NV10_PGRAPH_CTX_CACHE(i, 0)) & 0xfff; ++ ++ if (class == 0x56 || class == 0x96 || class == 0x99) { ++ subchan = i; ++ break; ++ } ++ } ++ ++ if (subchan < 0 || !inst) ++ return; ++ ++ /* Save the current ctx object */ ++ ctx_user = nv_rd32(dev, NV10_PGRAPH_CTX_USER); ++ for (i = 0; i < 5; i++) ++ ctx_switch[i] = nv_rd32(dev, NV10_PGRAPH_CTX_SWITCH(i)); ++ ++ /* Save the FIFO state */ ++ st2 = nv_rd32(dev, NV10_PGRAPH_FFINTFC_ST2); ++ st2_dl = nv_rd32(dev, NV10_PGRAPH_FFINTFC_ST2_DL); ++ st2_dh = nv_rd32(dev, NV10_PGRAPH_FFINTFC_ST2_DH); ++ fifo_ptr = nv_rd32(dev, NV10_PGRAPH_FFINTFC_FIFO_PTR); ++ ++ for (i = 0; i < ARRAY_SIZE(fifo); i++) ++ fifo[i] = nv_rd32(dev, 0x4007a0 + 4 * i); ++ ++ /* Switch to the celsius subchannel */ ++ for (i = 0; i < 5; i++) ++ nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH(i), ++ nv_rd32(dev, NV10_PGRAPH_CTX_CACHE(subchan, i))); ++ nv_mask(dev, NV10_PGRAPH_CTX_USER, 0xe000, subchan << 13); ++ ++ /* Inject NV10TCL_DMA_VTXBUF */ ++ nv_wr32(dev, NV10_PGRAPH_FFINTFC_FIFO_PTR, 0); ++ nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2, ++ 0x2c000000 | chan->id << 20 | subchan << 16 | 0x18c); ++ nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2_DL, inst); ++ nv_mask(dev, NV10_PGRAPH_CTX_CONTROL, 0, 0x10000); ++ pgraph->fifo_access(dev, true); ++ pgraph->fifo_access(dev, false); ++ ++ /* Restore the FIFO state */ ++ for (i = 0; i < ARRAY_SIZE(fifo); i++) ++ nv_wr32(dev, 0x4007a0 + 4 * i, fifo[i]); ++ ++ nv_wr32(dev, NV10_PGRAPH_FFINTFC_FIFO_PTR, fifo_ptr); ++ nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2, st2); ++ nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2_DL, st2_dl); ++ nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2_DH, st2_dh); ++ ++ /* Restore the current ctx object */ ++ for (i = 0; i < 5; i++) ++ nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH(i), ctx_switch[i]); ++ nv_wr32(dev, NV10_PGRAPH_CTX_USER, ctx_user); ++} ++ + int nv10_graph_load_context(struct nouveau_channel *chan) + { + struct drm_device *dev = chan->dev; +@@ -670,6 +742,8 @@ int nv10_graph_load_context(struct nouveau_channel *chan) + } + + nv10_graph_load_pipe(chan); ++ nv10_graph_load_dma_vtxbuf(chan, (nv_rd32(dev, NV10_PGRAPH_GLOBALSTATE1) ++ & 0xffff)); + + nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10010100); + tmp = nv_rd32(dev, NV10_PGRAPH_CTX_USER); +@@ -729,7 +803,7 @@ nv10_graph_context_switch(struct drm_device *dev) + /* Load context for next channel */ + chid = (nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR) >> 20) & 0x1f; + chan = dev_priv->fifos[chid]; +- if (chan) ++ if (chan && chan->pgraph_ctx) + nv10_graph_load_context(chan); + + pgraph->fifo_access(dev, true); +@@ -856,11 +930,12 @@ int nv10_graph_init(struct drm_device *dev) + for (i = 0; i < NV10_PFB_TILE__SIZE; i++) + nv10_graph_set_region_tiling(dev, i, 0, 0, 0); + +- 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); ++ nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH(0), 0x00000000); ++ nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH(1), 0x00000000); ++ nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH(2), 0x00000000); ++ nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH(3), 0x00000000); ++ nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH(4), 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; +diff --git a/drivers/gpu/drm/nouveau/nv17_gpio.c b/drivers/gpu/drm/nouveau/nv17_gpio.c +deleted file mode 100644 +index 2e58c33..0000000 +--- a/drivers/gpu/drm/nouveau/nv17_gpio.c ++++ /dev/null +@@ -1,92 +0,0 @@ +-/* +- * 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, ®, &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, ®, &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 -index 74c8803..bb3a284 100644 +index 74c8803..28119fd 100644 --- a/drivers/gpu/drm/nouveau/nv17_tv.c +++ b/drivers/gpu/drm/nouveau/nv17_tv.c -@@ -116,6 +116,21 @@ static uint32_t nv42_tv_sample_load(struct drm_encoder *encoder) +@@ -37,6 +37,7 @@ static uint32_t nv42_tv_sample_load(struct drm_encoder *encoder) + { + struct drm_device *dev = encoder->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; ++ struct nouveau_gpio_engine *gpio = &dev_priv->engine.gpio; + uint32_t testval, regoffset = nv04_dac_output_offset(encoder); + uint32_t gpio0, gpio1, fp_htotal, fp_hsync_start, fp_hsync_end, + fp_control, test_ctrl, dacclk, ctv_14, ctv_1c, ctv_6c; +@@ -52,8 +53,8 @@ static uint32_t nv42_tv_sample_load(struct drm_encoder *encoder) + head = (dacclk & 0x100) >> 8; + + /* Save the previous state. */ +- gpio1 = nv17_gpio_get(dev, DCB_GPIO_TVDAC1); +- gpio0 = nv17_gpio_get(dev, DCB_GPIO_TVDAC0); ++ gpio1 = gpio->get(dev, DCB_GPIO_TVDAC1); ++ gpio0 = gpio->get(dev, DCB_GPIO_TVDAC0); + fp_htotal = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_HTOTAL); + fp_hsync_start = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_START); + fp_hsync_end = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_END); +@@ -64,8 +65,8 @@ static uint32_t nv42_tv_sample_load(struct drm_encoder *encoder) + ctv_6c = NVReadRAMDAC(dev, head, 0x680c6c); + + /* Prepare the DAC for load detection. */ +- nv17_gpio_set(dev, DCB_GPIO_TVDAC1, true); +- nv17_gpio_set(dev, DCB_GPIO_TVDAC0, true); ++ gpio->set(dev, DCB_GPIO_TVDAC1, true); ++ gpio->set(dev, DCB_GPIO_TVDAC0, true); + + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HTOTAL, 1343); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_START, 1047); +@@ -110,12 +111,31 @@ static uint32_t nv42_tv_sample_load(struct drm_encoder *encoder) + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_END, fp_hsync_end); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_START, fp_hsync_start); + NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HTOTAL, fp_htotal); +- nv17_gpio_set(dev, DCB_GPIO_TVDAC1, gpio1); +- nv17_gpio_set(dev, DCB_GPIO_TVDAC0, gpio0); ++ gpio->set(dev, DCB_GPIO_TVDAC1, gpio1); ++ gpio->set(dev, DCB_GPIO_TVDAC0, gpio0); + return sample; } @@ -4988,10 +11708,14 @@ index 74c8803..bb3a284 100644 +get_tv_detect_quirks(struct drm_device *dev, uint32_t *pin_mask) +{ + /* Zotac FX5200 */ -+ if (dev->pdev->device == 0x0322 && -+ dev->pdev->subsystem_vendor == 0x19da && -+ (dev->pdev->subsystem_device == 0x1035 || -+ dev->pdev->subsystem_device == 0x2035)) { ++ if (nv_match_device(dev, 0x0322, 0x19da, 0x1035) || ++ nv_match_device(dev, 0x0322, 0x19da, 0x2035)) { ++ *pin_mask = 0xc; ++ return false; ++ } ++ ++ /* MSI nForce2 IGP */ ++ if (nv_match_device(dev, 0x01f0, 0x1462, 0x5710)) { + *pin_mask = 0xc; + return false; + } @@ -5002,7 +11726,7 @@ index 74c8803..bb3a284 100644 static enum drm_connector_status nv17_tv_detect(struct drm_encoder *encoder, struct drm_connector *connector) { -@@ -124,12 +139,20 @@ nv17_tv_detect(struct drm_encoder *encoder, struct drm_connector *connector) +@@ -124,12 +144,20 @@ nv17_tv_detect(struct drm_encoder *encoder, struct drm_connector *connector) struct drm_mode_config *conf = &dev->mode_config; struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder); struct dcb_entry *dcb = tv_enc->base.dcb; @@ -5028,7 +11752,7 @@ index 74c8803..bb3a284 100644 switch (tv_enc->pin_mask) { case 0x2: -@@ -154,7 +177,9 @@ nv17_tv_detect(struct drm_encoder *encoder, struct drm_connector *connector) +@@ -154,7 +182,9 @@ nv17_tv_detect(struct drm_encoder *encoder, struct drm_connector *connector) conf->tv_subconnector_property, tv_enc->subconnector); @@ -5039,7 +11763,149 @@ index 74c8803..bb3a284 100644 NV_INFO(dev, "Load detected on output %c\n", '@' + ffs(dcb->or)); return connector_status_connected; -@@ -296,6 +321,9 @@ static bool nv17_tv_mode_fixup(struct drm_encoder *encoder, +@@ -163,55 +193,56 @@ nv17_tv_detect(struct drm_encoder *encoder, struct drm_connector *connector) + } + } + +-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) ++static int nv17_tv_get_ld_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; ++ struct drm_display_mode *mode, *tv_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); + +- 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; + +- 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->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; ++ 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; ++ drm_mode_probed_add(connector, mode); ++ n++; + } + +- /* tv_norm->kind == CTV_ENC_MODE */ +- output_mode = &tv_norm->ctv_enc_mode.mode; ++ return n; ++} ++ ++static int nv17_tv_get_hd_modes(struct drm_encoder *encoder, ++ struct drm_connector *connector) ++{ ++ struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); ++ struct drm_display_mode *output_mode = &tv_norm->ctv_enc_mode.mode; ++ struct drm_display_mode *mode; ++ 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 } ++ }; ++ int i, n = 0; ++ + for (i = 0; i < ARRAY_SIZE(modes); i++) { + if (modes[i].hdisplay > output_mode->hdisplay || + modes[i].vdisplay > output_mode->vdisplay) +@@ -221,11 +252,12 @@ static int nv17_tv_get_modes(struct drm_encoder *encoder, + 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); ++ modes[i].vdisplay, 60, false, ++ (output_mode->flags & ++ DRM_MODE_FLAG_INTERLACE), false); + } + + /* CVT modes are sometimes unsuitable... */ +@@ -236,6 +268,7 @@ static int nv17_tv_get_modes(struct drm_encoder *encoder, + - 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; +@@ -246,9 +279,21 @@ static int nv17_tv_get_modes(struct drm_encoder *encoder, + drm_mode_probed_add(connector, mode); + n++; + } ++ + return n; + } + ++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); ++ ++ if (tv_norm->kind == CTV_ENC_MODE) ++ return nv17_tv_get_hd_modes(encoder, connector); ++ else ++ return nv17_tv_get_ld_modes(encoder, connector); ++} ++ + static int nv17_tv_mode_valid(struct drm_encoder *encoder, + struct drm_display_mode *mode) + { +@@ -296,6 +341,9 @@ static bool nv17_tv_mode_fixup(struct drm_encoder *encoder, { struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); @@ -5049,7 +11915,45 @@ index 74c8803..bb3a284 100644 if (tv_norm->kind == CTV_ENC_MODE) adjusted_mode->clock = tv_norm->ctv_enc_mode.mode.clock; else -@@ -744,8 +772,10 @@ static struct drm_encoder_funcs nv17_tv_funcs = { +@@ -307,6 +355,8 @@ static bool nv17_tv_mode_fixup(struct drm_encoder *encoder, + static void nv17_tv_dpms(struct drm_encoder *encoder, int mode) + { + struct drm_device *dev = encoder->dev; ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ struct nouveau_gpio_engine *gpio = &dev_priv->engine.gpio; + struct nv17_tv_state *regs = &to_tv_enc(encoder)->state; + struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder); + +@@ -331,8 +381,8 @@ static void nv17_tv_dpms(struct drm_encoder *encoder, int mode) + + 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); ++ gpio->set(dev, DCB_GPIO_TVDAC1, mode == DRM_MODE_DPMS_ON); ++ gpio->set(dev, DCB_GPIO_TVDAC0, mode == DRM_MODE_DPMS_ON); + + nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON); + } +@@ -373,15 +423,8 @@ static void nv17_tv_prepare(struct drm_encoder *encoder) + + } + +- /* 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; +- } ++ if (tv_norm->kind == CTV_ENC_MODE) ++ *cr_lcd |= 0x1 | (head ? 0x0 : 0x8); + + /* Set the DACCLK register */ + dacclk = (NVReadRAMDAC(dev, 0, dacclk_off) & ~0x30) | 0x1; +@@ -744,8 +787,10 @@ static struct drm_encoder_funcs nv17_tv_funcs = { .destroy = nv17_tv_destroy, }; @@ -5061,7 +11965,7 @@ index 74c8803..bb3a284 100644 struct drm_encoder *encoder; struct nv17_tv_encoder *tv_enc = NULL; -@@ -774,5 +804,7 @@ int nv17_tv_create(struct drm_device *dev, struct dcb_entry *entry) +@@ -774,5 +819,7 @@ int nv17_tv_create(struct drm_device *dev, struct dcb_entry *entry) encoder->possible_crtcs = entry->heads; encoder->possible_clones = 0; @@ -5069,18 +11973,717 @@ index 74c8803..bb3a284 100644 + drm_mode_connector_attach_encoder(connector, encoder); return 0; } +diff --git a/drivers/gpu/drm/nouveau/nv17_tv.h b/drivers/gpu/drm/nouveau/nv17_tv.h +index c00977c..6bf0384 100644 +--- a/drivers/gpu/drm/nouveau/nv17_tv.h ++++ b/drivers/gpu/drm/nouveau/nv17_tv.h +@@ -127,7 +127,8 @@ 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) ++static inline void nv_write_ptv(struct drm_device *dev, uint32_t reg, ++ uint32_t val) + { + nv_wr32(dev, reg, val); + } +@@ -137,7 +138,8 @@ 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) ++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); +@@ -149,8 +151,11 @@ static inline uint8_t nv_read_tv_enc(struct drm_device *dev, uint8_t 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]) ++#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 +index d64683d..9d3893c 100644 +--- a/drivers/gpu/drm/nouveau/nv17_tv_modes.c ++++ b/drivers/gpu/drm/nouveau/nv17_tv_modes.c +@@ -336,12 +336,17 @@ static void tv_setup_filter(struct drm_encoder *encoder) + 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); ++ 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); + } + } + } +@@ -349,7 +354,8 @@ static void tv_setup_filter(struct drm_encoder *encoder) + + /* Hardware state saving/restoring */ + +-static void tv_save_filter(struct drm_device *dev, uint32_t base, uint32_t regs[4][7]) ++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 }; +@@ -360,7 +366,8 @@ static void tv_save_filter(struct drm_device *dev, uint32_t base, uint32_t regs[ + } + } + +-static void tv_load_filter(struct drm_device *dev, uint32_t base, uint32_t regs[4][7]) ++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 }; +@@ -504,10 +511,10 @@ void nv17_tv_update_properties(struct drm_encoder *encoder) + 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[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); +@@ -541,7 +548,8 @@ void nv17_ctv_update_rescaler(struct drm_encoder *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; ++ 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. */ +@@ -553,13 +561,15 @@ void nv17_ctv_update_rescaler(struct drm_encoder *encoder) + 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); ++ 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; ++ 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; diff --git a/drivers/gpu/drm/nouveau/nv20_graph.c b/drivers/gpu/drm/nouveau/nv20_graph.c -index d6fc0a8..191c15c 100644 +index d6fc0a8..93f0d8a 100644 --- a/drivers/gpu/drm/nouveau/nv20_graph.c +++ b/drivers/gpu/drm/nouveau/nv20_graph.c -@@ -370,68 +370,54 @@ nv20_graph_create_context(struct nouveau_channel *chan) +@@ -37,49 +37,49 @@ 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); ++ nv_wo32(ctx, 0x033c, 0xffff0000); ++ nv_wo32(ctx, 0x03a0, 0x0fff0000); ++ nv_wo32(ctx, 0x03a4, 0x0fff0000); ++ nv_wo32(ctx, 0x047c, 0x00000101); ++ nv_wo32(ctx, 0x0490, 0x00000111); ++ nv_wo32(ctx, 0x04a8, 0x44400000); + for (i = 0x04d4; i <= 0x04e0; i += 4) +- nv_wo32(dev, ctx, i/4, 0x00030303); ++ nv_wo32(ctx, i, 0x00030303); + for (i = 0x04f4; i <= 0x0500; i += 4) +- nv_wo32(dev, ctx, i/4, 0x00080000); ++ nv_wo32(ctx, i, 0x00080000); + for (i = 0x050c; i <= 0x0518; i += 4) +- nv_wo32(dev, ctx, i/4, 0x01012000); ++ nv_wo32(ctx, i, 0x01012000); + for (i = 0x051c; i <= 0x0528; i += 4) +- nv_wo32(dev, ctx, i/4, 0x000105b8); ++ nv_wo32(ctx, i, 0x000105b8); + for (i = 0x052c; i <= 0x0538; i += 4) +- nv_wo32(dev, ctx, i/4, 0x00080008); ++ nv_wo32(ctx, i, 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); ++ nv_wo32(ctx, i, 0x07ff0000); ++ nv_wo32(ctx, 0x05a4, 0x4b7fffff); ++ nv_wo32(ctx, 0x05fc, 0x00000001); ++ nv_wo32(ctx, 0x0604, 0x00004000); ++ nv_wo32(ctx, 0x0610, 0x00000001); ++ nv_wo32(ctx, 0x0618, 0x00040000); ++ nv_wo32(ctx, 0x061c, 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(ctx, (i + 0), 0x10700ff9); ++ nv_wo32(ctx, (i + 4), 0x0436086c); ++ nv_wo32(ctx, (i + 8), 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); ++ nv_wo32(ctx, 0x281c, 0x3f800000); ++ nv_wo32(ctx, 0x2830, 0x3f800000); ++ nv_wo32(ctx, 0x285c, 0x40000000); ++ nv_wo32(ctx, 0x2860, 0x3f800000); ++ nv_wo32(ctx, 0x2864, 0x3f000000); ++ nv_wo32(ctx, 0x286c, 0x40000000); ++ nv_wo32(ctx, 0x2870, 0x3f800000); ++ nv_wo32(ctx, 0x2878, 0xbf800000); ++ nv_wo32(ctx, 0x2880, 0xbf800000); ++ nv_wo32(ctx, 0x34a4, 0x000fe000); ++ nv_wo32(ctx, 0x3530, 0x000003f8); ++ nv_wo32(ctx, 0x3540, 0x002fe000); + for (i = 0x355c; i <= 0x3578; i += 4) +- nv_wo32(dev, ctx, i/4, 0x001c527c); ++ nv_wo32(ctx, i, 0x001c527c); + } + + static void +@@ -87,58 +87,58 @@ 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); ++ nv_wo32(ctx, 0x035c, 0xffff0000); ++ nv_wo32(ctx, 0x03c0, 0x0fff0000); ++ nv_wo32(ctx, 0x03c4, 0x0fff0000); ++ nv_wo32(ctx, 0x049c, 0x00000101); ++ nv_wo32(ctx, 0x04b0, 0x00000111); ++ nv_wo32(ctx, 0x04c8, 0x00000080); ++ nv_wo32(ctx, 0x04cc, 0xffff0000); ++ nv_wo32(ctx, 0x04d0, 0x00000001); ++ nv_wo32(ctx, 0x04e4, 0x44400000); ++ nv_wo32(ctx, 0x04fc, 0x4b800000); + for (i = 0x0510; i <= 0x051c; i += 4) +- nv_wo32(dev, ctx, i/4, 0x00030303); ++ nv_wo32(ctx, i, 0x00030303); + for (i = 0x0530; i <= 0x053c; i += 4) +- nv_wo32(dev, ctx, i/4, 0x00080000); ++ nv_wo32(ctx, i, 0x00080000); + for (i = 0x0548; i <= 0x0554; i += 4) +- nv_wo32(dev, ctx, i/4, 0x01012000); ++ nv_wo32(ctx, i, 0x01012000); + for (i = 0x0558; i <= 0x0564; i += 4) +- nv_wo32(dev, ctx, i/4, 0x000105b8); ++ nv_wo32(ctx, i, 0x000105b8); + for (i = 0x0568; i <= 0x0574; i += 4) +- nv_wo32(dev, ctx, i/4, 0x00080008); ++ nv_wo32(ctx, i, 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); ++ nv_wo32(ctx, i, 0x07ff0000); ++ nv_wo32(ctx, 0x05e0, 0x4b7fffff); ++ nv_wo32(ctx, 0x0620, 0x00000080); ++ nv_wo32(ctx, 0x0624, 0x30201000); ++ nv_wo32(ctx, 0x0628, 0x70605040); ++ nv_wo32(ctx, 0x062c, 0xb0a09080); ++ nv_wo32(ctx, 0x0630, 0xf0e0d0c0); ++ nv_wo32(ctx, 0x0664, 0x00000001); ++ nv_wo32(ctx, 0x066c, 0x00004000); ++ nv_wo32(ctx, 0x0678, 0x00000001); ++ nv_wo32(ctx, 0x0680, 0x00040000); ++ nv_wo32(ctx, 0x0684, 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(ctx, (i + 0), 0x10700ff9); ++ nv_wo32(ctx, (i + 4), 0x0436086c); ++ nv_wo32(ctx, (i + 8), 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); ++ nv_wo32(ctx, 0x2704, 0x3f800000); ++ nv_wo32(ctx, 0x2718, 0x3f800000); ++ nv_wo32(ctx, 0x2744, 0x40000000); ++ nv_wo32(ctx, 0x2748, 0x3f800000); ++ nv_wo32(ctx, 0x274c, 0x3f000000); ++ nv_wo32(ctx, 0x2754, 0x40000000); ++ nv_wo32(ctx, 0x2758, 0x3f800000); ++ nv_wo32(ctx, 0x2760, 0xbf800000); ++ nv_wo32(ctx, 0x2768, 0xbf800000); ++ nv_wo32(ctx, 0x308c, 0x000fe000); ++ nv_wo32(ctx, 0x3108, 0x000003f8); ++ nv_wo32(ctx, 0x3468, 0x002fe000); + for (i = 0x3484; i <= 0x34a0; i += 4) +- nv_wo32(dev, ctx, i/4, 0x001c527c); ++ nv_wo32(ctx, i, 0x001c527c); + } + + static void +@@ -146,49 +146,49 @@ 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); ++ nv_wo32(ctx, 0x033c, 0xffff0000); ++ nv_wo32(ctx, 0x03a0, 0x0fff0000); ++ nv_wo32(ctx, 0x03a4, 0x0fff0000); ++ nv_wo32(ctx, 0x047c, 0x00000101); ++ nv_wo32(ctx, 0x0490, 0x00000111); ++ nv_wo32(ctx, 0x04a8, 0x44400000); + for (i = 0x04d4; i <= 0x04e0; i += 4) +- nv_wo32(dev, ctx, i/4, 0x00030303); ++ nv_wo32(ctx, i, 0x00030303); + for (i = 0x04f4; i <= 0x0500; i += 4) +- nv_wo32(dev, ctx, i/4, 0x00080000); ++ nv_wo32(ctx, i, 0x00080000); + for (i = 0x050c; i <= 0x0518; i += 4) +- nv_wo32(dev, ctx, i/4, 0x01012000); ++ nv_wo32(ctx, i, 0x01012000); + for (i = 0x051c; i <= 0x0528; i += 4) +- nv_wo32(dev, ctx, i/4, 0x000105b8); ++ nv_wo32(ctx, i, 0x000105b8); + for (i = 0x052c; i <= 0x0538; i += 4) +- nv_wo32(dev, ctx, i/4, 0x00080008); ++ nv_wo32(ctx, i, 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); ++ nv_wo32(ctx, i, 0x07ff0000); ++ nv_wo32(ctx, 0x05a4, 0x4b7fffff); ++ nv_wo32(ctx, 0x05fc, 0x00000001); ++ nv_wo32(ctx, 0x0604, 0x00004000); ++ nv_wo32(ctx, 0x0610, 0x00000001); ++ nv_wo32(ctx, 0x0618, 0x00040000); ++ nv_wo32(ctx, 0x061c, 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(ctx, (i + 0), 0x10700ff9); ++ nv_wo32(ctx, (i + 4), 0x0436086c); ++ nv_wo32(ctx, (i + 8), 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); ++ nv_wo32(ctx, 0x269c, 0x3f800000); ++ nv_wo32(ctx, 0x26b0, 0x3f800000); ++ nv_wo32(ctx, 0x26dc, 0x40000000); ++ nv_wo32(ctx, 0x26e0, 0x3f800000); ++ nv_wo32(ctx, 0x26e4, 0x3f000000); ++ nv_wo32(ctx, 0x26ec, 0x40000000); ++ nv_wo32(ctx, 0x26f0, 0x3f800000); ++ nv_wo32(ctx, 0x26f8, 0xbf800000); ++ nv_wo32(ctx, 0x2700, 0xbf800000); ++ nv_wo32(ctx, 0x3024, 0x000fe000); ++ nv_wo32(ctx, 0x30a0, 0x000003f8); ++ nv_wo32(ctx, 0x33fc, 0x002fe000); + for (i = 0x341c; i <= 0x3438; i += 4) +- nv_wo32(dev, ctx, i/4, 0x001c527c); ++ nv_wo32(ctx, i, 0x001c527c); + } + + static void +@@ -196,57 +196,57 @@ 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); ++ nv_wo32(ctx, 0x0410, 0x00000101); ++ nv_wo32(ctx, 0x0424, 0x00000111); ++ nv_wo32(ctx, 0x0428, 0x00000060); ++ nv_wo32(ctx, 0x0444, 0x00000080); ++ nv_wo32(ctx, 0x0448, 0xffff0000); ++ nv_wo32(ctx, 0x044c, 0x00000001); ++ nv_wo32(ctx, 0x0460, 0x44400000); ++ nv_wo32(ctx, 0x048c, 0xffff0000); + for (i = 0x04e0; i < 0x04e8; i += 4) +- nv_wo32(dev, ctx, i/4, 0x0fff0000); +- nv_wo32(dev, ctx, 0x04ec/4, 0x00011100); ++ nv_wo32(ctx, i, 0x0fff0000); ++ nv_wo32(ctx, 0x04ec, 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); ++ nv_wo32(ctx, i, 0x07ff0000); ++ nv_wo32(ctx, 0x0550, 0x4b7fffff); ++ nv_wo32(ctx, 0x058c, 0x00000080); ++ nv_wo32(ctx, 0x0590, 0x30201000); ++ nv_wo32(ctx, 0x0594, 0x70605040); ++ nv_wo32(ctx, 0x0598, 0xb8a89888); ++ nv_wo32(ctx, 0x059c, 0xf8e8d8c8); ++ nv_wo32(ctx, 0x05b0, 0xb0000000); + for (i = 0x0600; i < 0x0640; i += 4) +- nv_wo32(dev, ctx, i/4, 0x00010588); ++ nv_wo32(ctx, i, 0x00010588); + for (i = 0x0640; i < 0x0680; i += 4) +- nv_wo32(dev, ctx, i/4, 0x00030303); ++ nv_wo32(ctx, i, 0x00030303); + for (i = 0x06c0; i < 0x0700; i += 4) +- nv_wo32(dev, ctx, i/4, 0x0008aae4); ++ nv_wo32(ctx, i, 0x0008aae4); + for (i = 0x0700; i < 0x0740; i += 4) +- nv_wo32(dev, ctx, i/4, 0x01012000); ++ nv_wo32(ctx, i, 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); ++ nv_wo32(ctx, i, 0x00080008); ++ nv_wo32(ctx, 0x085c, 0x00040000); ++ nv_wo32(ctx, 0x0860, 0x00010000); + for (i = 0x0864; i < 0x0874; i += 4) +- nv_wo32(dev, ctx, i/4, 0x00040004); ++ nv_wo32(ctx, i, 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); ++ nv_wo32(ctx, i + 0, 0x10700ff9); ++ nv_wo32(ctx, i + 1, 0x0436086c); ++ nv_wo32(ctx, i + 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); ++ nv_wo32(ctx, i, 0x0000ffff); ++ nv_wo32(ctx, 0x344c, 0x3f800000); ++ nv_wo32(ctx, 0x3808, 0x3f800000); ++ nv_wo32(ctx, 0x381c, 0x3f800000); ++ nv_wo32(ctx, 0x3848, 0x40000000); ++ nv_wo32(ctx, 0x384c, 0x3f800000); ++ nv_wo32(ctx, 0x3850, 0x3f000000); ++ nv_wo32(ctx, 0x3858, 0x40000000); ++ nv_wo32(ctx, 0x385c, 0x3f800000); ++ nv_wo32(ctx, 0x3864, 0xbf800000); ++ nv_wo32(ctx, 0x386c, 0xbf800000); + } + + static void +@@ -254,57 +254,57 @@ 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); ++ nv_wo32(ctx, 0x040c, 0x01000101); ++ nv_wo32(ctx, 0x0420, 0x00000111); ++ nv_wo32(ctx, 0x0424, 0x00000060); ++ nv_wo32(ctx, 0x0440, 0x00000080); ++ nv_wo32(ctx, 0x0444, 0xffff0000); ++ nv_wo32(ctx, 0x0448, 0x00000001); ++ nv_wo32(ctx, 0x045c, 0x44400000); ++ nv_wo32(ctx, 0x0480, 0xffff0000); + for (i = 0x04d4; i < 0x04dc; i += 4) +- nv_wo32(dev, ctx, i/4, 0x0fff0000); +- nv_wo32(dev, ctx, 0x04e0/4, 0x00011100); ++ nv_wo32(ctx, i, 0x0fff0000); ++ nv_wo32(ctx, 0x04e0, 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); ++ nv_wo32(ctx, i, 0x07ff0000); ++ nv_wo32(ctx, 0x0544, 0x4b7fffff); ++ nv_wo32(ctx, 0x057c, 0x00000080); ++ nv_wo32(ctx, 0x0580, 0x30201000); ++ nv_wo32(ctx, 0x0584, 0x70605040); ++ nv_wo32(ctx, 0x0588, 0xb8a89888); ++ nv_wo32(ctx, 0x058c, 0xf8e8d8c8); ++ nv_wo32(ctx, 0x05a0, 0xb0000000); + for (i = 0x05f0; i < 0x0630; i += 4) +- nv_wo32(dev, ctx, i/4, 0x00010588); ++ nv_wo32(ctx, i, 0x00010588); + for (i = 0x0630; i < 0x0670; i += 4) +- nv_wo32(dev, ctx, i/4, 0x00030303); ++ nv_wo32(ctx, i, 0x00030303); + for (i = 0x06b0; i < 0x06f0; i += 4) +- nv_wo32(dev, ctx, i/4, 0x0008aae4); ++ nv_wo32(ctx, i, 0x0008aae4); + for (i = 0x06f0; i < 0x0730; i += 4) +- nv_wo32(dev, ctx, i/4, 0x01012000); ++ nv_wo32(ctx, i, 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); ++ nv_wo32(ctx, i, 0x00080008); ++ nv_wo32(ctx, 0x0850, 0x00040000); ++ nv_wo32(ctx, 0x0854, 0x00010000); + for (i = 0x0858; i < 0x0868; i += 4) +- nv_wo32(dev, ctx, i/4, 0x00040004); ++ nv_wo32(ctx, i, 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); ++ nv_wo32(ctx, i + 0, 0x10700ff9); ++ nv_wo32(ctx, i + 1, 0x0436086c); ++ nv_wo32(ctx, i + 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); ++ nv_wo32(ctx, i, 0x0000ffff); ++ nv_wo32(ctx, 0x2ae0, 0x3f800000); ++ nv_wo32(ctx, 0x2e9c, 0x3f800000); ++ nv_wo32(ctx, 0x2eb0, 0x3f800000); ++ nv_wo32(ctx, 0x2edc, 0x40000000); ++ nv_wo32(ctx, 0x2ee0, 0x3f800000); ++ nv_wo32(ctx, 0x2ee4, 0x3f000000); ++ nv_wo32(ctx, 0x2eec, 0x40000000); ++ nv_wo32(ctx, 0x2ef0, 0x3f800000); ++ nv_wo32(ctx, 0x2ef8, 0xbf800000); ++ nv_wo32(ctx, 0x2f00, 0xbf800000); + } + + static void +@@ -312,57 +312,57 @@ 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); ++ nv_wo32(ctx, 0x040c, 0x00000101); ++ nv_wo32(ctx, 0x0420, 0x00000111); ++ nv_wo32(ctx, 0x0424, 0x00000060); ++ nv_wo32(ctx, 0x0440, 0x00000080); ++ nv_wo32(ctx, 0x0444, 0xffff0000); ++ nv_wo32(ctx, 0x0448, 0x00000001); ++ nv_wo32(ctx, 0x045c, 0x44400000); ++ nv_wo32(ctx, 0x0488, 0xffff0000); + for (i = 0x04dc; i < 0x04e4; i += 4) +- nv_wo32(dev, ctx, i/4, 0x0fff0000); +- nv_wo32(dev, ctx, 0x04e8/4, 0x00011100); ++ nv_wo32(ctx, i, 0x0fff0000); ++ nv_wo32(ctx, 0x04e8, 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); ++ nv_wo32(ctx, i, 0x07ff0000); ++ nv_wo32(ctx, 0x054c, 0x4b7fffff); ++ nv_wo32(ctx, 0x0588, 0x00000080); ++ nv_wo32(ctx, 0x058c, 0x30201000); ++ nv_wo32(ctx, 0x0590, 0x70605040); ++ nv_wo32(ctx, 0x0594, 0xb8a89888); ++ nv_wo32(ctx, 0x0598, 0xf8e8d8c8); ++ nv_wo32(ctx, 0x05ac, 0xb0000000); + for (i = 0x0604; i < 0x0644; i += 4) +- nv_wo32(dev, ctx, i/4, 0x00010588); ++ nv_wo32(ctx, i, 0x00010588); + for (i = 0x0644; i < 0x0684; i += 4) +- nv_wo32(dev, ctx, i/4, 0x00030303); ++ nv_wo32(ctx, i, 0x00030303); + for (i = 0x06c4; i < 0x0704; i += 4) +- nv_wo32(dev, ctx, i/4, 0x0008aae4); ++ nv_wo32(ctx, i, 0x0008aae4); + for (i = 0x0704; i < 0x0744; i += 4) +- nv_wo32(dev, ctx, i/4, 0x01012000); ++ nv_wo32(ctx, i, 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); ++ nv_wo32(ctx, i, 0x00080008); ++ nv_wo32(ctx, 0x0860, 0x00040000); ++ nv_wo32(ctx, 0x0864, 0x00010000); + for (i = 0x0868; i < 0x0878; i += 4) +- nv_wo32(dev, ctx, i/4, 0x00040004); ++ nv_wo32(ctx, i, 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); ++ nv_wo32(ctx, i + 0, 0x10700ff9); ++ nv_wo32(ctx, i + 4, 0x0436086c); ++ nv_wo32(ctx, i + 8, 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); ++ nv_wo32(ctx, i, 0x0000ffff); ++ nv_wo32(ctx, 0x3450, 0x3f800000); ++ nv_wo32(ctx, 0x380c, 0x3f800000); ++ nv_wo32(ctx, 0x3820, 0x3f800000); ++ nv_wo32(ctx, 0x384c, 0x40000000); ++ nv_wo32(ctx, 0x3850, 0x3f800000); ++ nv_wo32(ctx, 0x3854, 0x3f000000); ++ nv_wo32(ctx, 0x385c, 0x40000000); ++ nv_wo32(ctx, 0x3860, 0x3f800000); ++ nv_wo32(ctx, 0x3868, 0xbf800000); ++ nv_wo32(ctx, 0x3870, 0xbf800000); + } + + int +@@ -370,68 +370,52 @@ nv20_graph_create_context(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; void (*ctx_init)(struct drm_device *, struct nouveau_gpuobj *); - unsigned int ctx_size; - unsigned int idoffs = 0x28/4; +- unsigned int idoffs = 0x28/4; ++ unsigned int idoffs = 0x28; int ret; switch (dev_priv->chipset) { @@ -5126,46 +12729,66 @@ index d6fc0a8..191c15c 100644 - ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, ctx_size, 16, - NVOBJ_FLAG_ZERO_ALLOC, - &chan->ramin_grctx); -+ ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, pgraph->grctx_size, -+ 16, NVOBJ_FLAG_ZERO_ALLOC, -+ &chan->ramin_grctx); ++ ret = nouveau_gpuobj_new(dev, chan, pgraph->grctx_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); +- ctx_init(dev, chan->ramin_grctx->gpuobj); ++ ctx_init(dev, chan->ramin_grctx); /* 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, chan->ramin_grctx->gpuobj, idoffs, +- (chan->id << 24) | 0x1); /* CTX_USER */ ++ nv_wo32(chan->ramin_grctx, 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); -+ nv_wo32(dev, pgraph->ctx_table->gpuobj, chan->id, -+ chan->ramin_grctx->instance >> 4); ++ nv_wo32(pgraph->ctx_table, chan->id * 4, chan->ramin_grctx->pinst >> 4); return 0; } -@@ -440,13 +426,12 @@ nv20_graph_destroy_context(struct nouveau_channel *chan) +@@ -440,13 +424,10 @@ nv20_graph_destroy_context(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; - if (chan->ramin_grctx) - nouveau_gpuobj_ref_del(dev, &chan->ramin_grctx); - +- 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); -+ nv_wo32(dev, pgraph->ctx_table->gpuobj, chan->id, 0); ++ nouveau_gpuobj_ref(NULL, &chan->ramin_grctx); ++ nv_wo32(pgraph->ctx_table, chan->id * 4, 0); } int -@@ -538,29 +523,44 @@ nv20_graph_set_region_tiling(struct drm_device *dev, int i, uint32_t addr, +@@ -457,7 +438,7 @@ nv20_graph_load_context(struct nouveau_channel *chan) + + if (!chan->ramin_grctx) + return -EINVAL; +- inst = chan->ramin_grctx->instance >> 4; ++ inst = chan->ramin_grctx->pinst >> 4; + + nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst); + nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_XFER, +@@ -480,7 +461,7 @@ nv20_graph_unload_context(struct drm_device *dev) + chan = pgraph->channel(dev); + if (!chan) + return 0; +- inst = chan->ramin_grctx->instance >> 4; ++ inst = chan->ramin_grctx->pinst >> 4; + + nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst); + nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_XFER, +@@ -538,29 +519,44 @@ nv20_graph_set_region_tiling(struct drm_device *dev, int i, uint32_t addr, int nv20_graph_init(struct drm_device *dev) { @@ -5204,28 +12827,29 @@ index d6fc0a8..191c15c 100644 - dev_priv->ctx_table_size = 32 * 4; - ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, - dev_priv->ctx_table_size, 16, -+ ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, 32 * 4, 16, - NVOBJ_FLAG_ZERO_ALLOC, +- NVOBJ_FLAG_ZERO_ALLOC, - &dev_priv->ctx_table); -+ &pgraph->ctx_table); ++ ret = nouveau_gpuobj_new(dev, NULL, 32 * 4, 16, ++ NVOBJ_FLAG_ZERO_ALLOC, ++ &pgraph->ctx_table); if (ret) return ret; } nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_TABLE, - dev_priv->ctx_table->instance >> 4); -+ pgraph->ctx_table->instance >> 4); ++ pgraph->ctx_table->pinst >> 4); nv20_graph_rdi(dev); -@@ -644,34 +644,52 @@ void +@@ -644,34 +640,52 @@ void nv20_graph_takedown(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; - nouveau_gpuobj_ref_del(dev, &dev_priv->ctx_table); -+ nouveau_gpuobj_ref_del(dev, &pgraph->ctx_table); ++ nouveau_gpuobj_ref(NULL, &pgraph->ctx_table); } int @@ -5264,40 +12888,177 @@ index d6fc0a8..191c15c 100644 - dev_priv->ctx_table_size = 32 * 4; - ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, - dev_priv->ctx_table_size, 16, -+ ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, 32 * 4, 16, - NVOBJ_FLAG_ZERO_ALLOC, +- NVOBJ_FLAG_ZERO_ALLOC, - &dev_priv->ctx_table); -+ &pgraph->ctx_table); ++ ret = nouveau_gpuobj_new(dev, NULL, 32 * 4, 16, ++ NVOBJ_FLAG_ZERO_ALLOC, ++ &pgraph->ctx_table); if (ret) return ret; } nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_TABLE, - dev_priv->ctx_table->instance >> 4); -+ pgraph->ctx_table->instance >> 4); ++ pgraph->ctx_table->pinst >> 4); nv_wr32(dev, NV03_PGRAPH_INTR , 0xFFFFFFFF); nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF); +diff --git a/drivers/gpu/drm/nouveau/nv30_fb.c b/drivers/gpu/drm/nouveau/nv30_fb.c +new file mode 100644 +index 0000000..4a3f2f0 +--- /dev/null ++++ b/drivers/gpu/drm/nouveau/nv30_fb.c +@@ -0,0 +1,95 @@ ++/* ++ * Copyright (C) 2010 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.h" ++#include "nouveau_drv.h" ++#include "nouveau_drm.h" ++ ++static int ++calc_bias(struct drm_device *dev, int k, int i, int j) ++{ ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ int b = (dev_priv->chipset > 0x30 ? ++ nv_rd32(dev, 0x122c + 0x10 * k + 0x4 * j) >> (4 * (i ^ 1)) : ++ 0) & 0xf; ++ ++ return 2 * (b & 0x8 ? b - 0x10 : b); ++} ++ ++static int ++calc_ref(struct drm_device *dev, int l, int k, int i) ++{ ++ int j, x = 0; ++ ++ for (j = 0; j < 4; j++) { ++ int m = (l >> (8 * i) & 0xff) + calc_bias(dev, k, i, j); ++ ++ x |= (0x80 | clamp(m, 0, 0x1f)) << (8 * j); ++ } ++ ++ return x; ++} ++ ++int ++nv30_fb_init(struct drm_device *dev) ++{ ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ struct nouveau_fb_engine *pfb = &dev_priv->engine.fb; ++ int i, j; ++ ++ pfb->num_tiles = NV10_PFB_TILE__SIZE; ++ ++ /* Turn all the tiling regions off. */ ++ for (i = 0; i < pfb->num_tiles; i++) ++ pfb->set_region_tiling(dev, i, 0, 0, 0); ++ ++ /* Init the memory timing regs at 0x10037c/0x1003ac */ ++ if (dev_priv->chipset == 0x30 || ++ dev_priv->chipset == 0x31 || ++ dev_priv->chipset == 0x35) { ++ /* Related to ROP count */ ++ int n = (dev_priv->chipset == 0x31 ? 2 : 4); ++ int l = nv_rd32(dev, 0x1003d0); ++ ++ for (i = 0; i < n; i++) { ++ for (j = 0; j < 3; j++) ++ nv_wr32(dev, 0x10037c + 0xc * i + 0x4 * j, ++ calc_ref(dev, l, 0, j)); ++ ++ for (j = 0; j < 2; j++) ++ nv_wr32(dev, 0x1003ac + 0x8 * i + 0x4 * j, ++ calc_ref(dev, l, 1, j)); ++ } ++ } ++ ++ return 0; ++} ++ ++void ++nv30_fb_takedown(struct drm_device *dev) ++{ ++} diff --git a/drivers/gpu/drm/nouveau/nv40_fifo.c b/drivers/gpu/drm/nouveau/nv40_fifo.c -index 500ccfd..2b67f18 100644 +index 500ccfd..d337b8b 100644 --- a/drivers/gpu/drm/nouveau/nv40_fifo.c +++ b/drivers/gpu/drm/nouveau/nv40_fifo.c -@@ -48,7 +48,6 @@ nv40_fifo_create_context(struct nouveau_channel *chan) +@@ -27,8 +27,9 @@ + #include "drmP.h" + #include "nouveau_drv.h" + #include "nouveau_drm.h" ++#include "nouveau_ramht.h" + +-#define NV40_RAMFC(c) (dev_priv->ramfc_offset + ((c) * NV40_RAMFC__SIZE)) ++#define NV40_RAMFC(c) (dev_priv->ramfc->pinst + ((c) * NV40_RAMFC__SIZE)) + #define NV40_RAMFC__SIZE 128 + + int +@@ -42,16 +43,15 @@ nv40_fifo_create_context(struct nouveau_channel *chan) + + 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); ++ NVOBJ_FLAG_ZERO_FREE, &chan->ramfc); + if (ret) + return ret; spin_lock_irqsave(&dev_priv->context_switch_lock, flags); - 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); -@@ -61,7 +60,6 @@ nv40_fifo_create_context(struct nouveau_channel *chan) +- nv_wi32(dev, fc + 12, chan->pushbuf->instance >> 4); ++ nv_wi32(dev, fc + 12, chan->pushbuf->pinst >> 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 | +@@ -59,9 +59,8 @@ nv40_fifo_create_context(struct nouveau_channel *chan) + NV_PFIFO_CACHE1_BIG_ENDIAN | + #endif 0x30000000 /* no idea.. */); - nv_wi32(dev, fc + 56, chan->ramin_grctx->instance >> 4); +- nv_wi32(dev, fc + 56, chan->ramin_grctx->instance >> 4); ++ nv_wi32(dev, fc + 56, chan->ramin_grctx->pinst >> 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, +@@ -79,8 +78,7 @@ nv40_fifo_destroy_context(struct nouveau_channel *chan) + 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); ++ nouveau_gpuobj_ref(NULL, &chan->ramfc); + } + + static void @@ -89,8 +87,6 @@ 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; @@ -5332,19 +13093,54 @@ index 500ccfd..2b67f18 100644 nv40_fifo_do_load_context(dev, pfifo->channels - 1); nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, +@@ -249,9 +241,9 @@ 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); ++ ((dev_priv->ramht->bits - 9) << 16) | ++ (dev_priv->ramht->gpuobj->pinst >> 8)); ++ nv_wr32(dev, NV03_PFIFO_RAMRO, dev_priv->ramro->pinst >> 8); + + switch (dev_priv->chipset) { + case 0x47: +@@ -279,7 +271,7 @@ nv40_fifo_init_ramxx(struct drm_device *dev) + nv_wr32(dev, 0x2230, 0); + nv_wr32(dev, NV40_PFIFO_RAMFC, + ((dev_priv->vram_size - 512 * 1024 + +- dev_priv->ramfc_offset) >> 16) | (3 << 16)); ++ dev_priv->ramfc->pinst) >> 16) | (3 << 16)); + break; + } + } diff --git a/drivers/gpu/drm/nouveau/nv40_graph.c b/drivers/gpu/drm/nouveau/nv40_graph.c -index 704a25d..ef550ce 100644 +index 704a25d..2424289 100644 --- a/drivers/gpu/drm/nouveau/nv40_graph.c +++ b/drivers/gpu/drm/nouveau/nv40_graph.c -@@ -58,6 +58,7 @@ nv40_graph_create_context(struct nouveau_channel *chan) +@@ -45,7 +45,7 @@ nv40_graph_channel(struct drm_device *dev) + struct nouveau_channel *chan = dev_priv->fifos[i]; + + if (chan && chan->ramin_grctx && +- chan->ramin_grctx->instance == inst) ++ chan->ramin_grctx->pinst == inst) + return chan; + } + +@@ -58,36 +58,28 @@ 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_pgraph_engine *pgraph = &dev_priv->engine.graph; + struct nouveau_grctx ctx = {}; int ret; - ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, pgraph->grctx_size, -@@ -67,20 +68,13 @@ nv40_graph_create_context(struct nouveau_channel *chan) +- ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, pgraph->grctx_size, +- 16, NVOBJ_FLAG_ZERO_ALLOC, +- &chan->ramin_grctx); ++ ret = nouveau_gpuobj_new(dev, chan, pgraph->grctx_size, 16, ++ NVOBJ_FLAG_ZERO_ALLOC, &chan->ramin_grctx); + if (ret) return ret; /* Initialise default context values */ @@ -5359,18 +13155,36 @@ index 704a25d..ef550ce 100644 - } else { - nouveau_grctx_vals_load(dev, chan->ramin_grctx->gpuobj); - } +- nv_wo32(dev, chan->ramin_grctx->gpuobj, 0, +- chan->ramin_grctx->gpuobj->im_pramin->start); +- dev_priv->engine.instmem.finish_access(dev); + ctx.dev = chan->dev; + ctx.mode = NOUVEAU_GRCTX_VALS; -+ ctx.data = chan->ramin_grctx->gpuobj; ++ ctx.data = chan->ramin_grctx; + nv40_grctx_init(&ctx); + - nv_wo32(dev, chan->ramin_grctx->gpuobj, 0, - chan->ramin_grctx->gpuobj->im_pramin->start); -- dev_priv->engine.instmem.finish_access(dev); ++ nv_wo32(chan->ramin_grctx, 0, chan->ramin_grctx->pinst); return 0; } -@@ -238,7 +232,8 @@ nv40_graph_init(struct drm_device *dev) + void + nv40_graph_destroy_context(struct nouveau_channel *chan) + { +- nouveau_gpuobj_ref_del(chan->dev, &chan->ramin_grctx); ++ nouveau_gpuobj_ref(NULL, &chan->ramin_grctx); + } + + static int +@@ -141,7 +133,7 @@ nv40_graph_load_context(struct nouveau_channel *chan) + + if (!chan->ramin_grctx) + return -EINVAL; +- inst = chan->ramin_grctx->instance >> 4; ++ inst = chan->ramin_grctx->pinst >> 4; + + ret = nv40_graph_transfer_context(dev, inst, 0); + if (ret) +@@ -238,7 +230,8 @@ nv40_graph_init(struct drm_device *dev) struct drm_nouveau_private *dev_priv = (struct drm_nouveau_private *)dev->dev_private; struct nouveau_fb_engine *pfb = &dev_priv->engine.fb; @@ -5380,7 +13194,7 @@ index 704a25d..ef550ce 100644 int i, j; nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) & -@@ -246,32 +241,22 @@ nv40_graph_init(struct drm_device *dev) +@@ -246,32 +239,22 @@ nv40_graph_init(struct drm_device *dev) nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) | NV_PMC_ENABLE_PGRAPH); @@ -5426,7 +13240,7 @@ index 704a25d..ef550ce 100644 /* No context present currently */ nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, 0x00000000); -@@ -407,7 +392,6 @@ nv40_graph_init(struct drm_device *dev) +@@ -407,7 +390,6 @@ nv40_graph_init(struct drm_device *dev) void nv40_graph_takedown(struct drm_device *dev) { @@ -5434,6 +13248,27 @@ index 704a25d..ef550ce 100644 } struct nouveau_pgraph_object_class nv40_graph_grclass[] = { +diff --git a/drivers/gpu/drm/nouveau/nv40_grctx.c b/drivers/gpu/drm/nouveau/nv40_grctx.c +index 9b5c974..ce58509 100644 +--- a/drivers/gpu/drm/nouveau/nv40_grctx.c ++++ b/drivers/gpu/drm/nouveau/nv40_grctx.c +@@ -596,13 +596,13 @@ nv40_graph_construct_shader(struct nouveau_grctx *ctx) + + offset += 0x0280/4; + for (i = 0; i < 16; i++, offset += 2) +- nv_wo32(dev, obj, offset, 0x3f800000); ++ nv_wo32(obj, offset * 4, 0x3f800000); + + for (vs = 0; vs < vs_nr; vs++, offset += vs_len) { + for (i = 0; i < vs_nr_b0 * 6; i += 6) +- nv_wo32(dev, obj, offset + b0_offset + i, 0x00000001); ++ nv_wo32(obj, (offset + b0_offset + i) * 4, 0x00000001); + for (i = 0; i < vs_nr_b1 * 4; i += 4) +- nv_wo32(dev, obj, offset + b1_offset + i, 0x3f800000); ++ nv_wo32(obj, (offset + b1_offset + i) * 4, 0x3f800000); + } + } + diff --git a/drivers/gpu/drm/nouveau/nv40_mc.c b/drivers/gpu/drm/nouveau/nv40_mc.c index 2a3495e..e4e72c1 100644 --- a/drivers/gpu/drm/nouveau/nv40_mc.c @@ -5448,10 +13283,67 @@ index 2a3495e..e4e72c1 100644 nv_wr32(dev, NV40_PMC_1704, 0); nv_wr32(dev, NV40_PMC_1708, 0); diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c -index b4e4a3b..5d11ea1 100644 +index b4e4a3b..2423c92 100644 --- a/drivers/gpu/drm/nouveau/nv50_crtc.c +++ b/drivers/gpu/drm/nouveau/nv50_crtc.c -@@ -440,47 +440,15 @@ nv50_crtc_prepare(struct drm_crtc *crtc) +@@ -264,11 +264,16 @@ nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, int scaling_mode, bool update) + int + nv50_crtc_set_clock(struct drm_device *dev, int head, int pclk) + { +- uint32_t reg = NV50_PDISPLAY_CRTC_CLK_CTRL1(head); ++ struct drm_nouveau_private *dev_priv = dev->dev_private; + struct pll_lims pll; +- uint32_t reg1, reg2; ++ uint32_t reg, reg1, reg2; + int ret, N1, M1, N2, M2, P; + ++ if (dev_priv->chipset < NV_C0) ++ reg = NV50_PDISPLAY_CRTC_CLK_CTRL1(head); ++ else ++ reg = 0x614140 + (head * 0x800); ++ + ret = get_pll_limits(dev, reg, &pll); + if (ret) + return ret; +@@ -286,7 +291,8 @@ nv50_crtc_set_clock(struct drm_device *dev, int head, int pclk) + nv_wr32(dev, reg, 0x10000611); + nv_wr32(dev, reg + 4, reg1 | (M1 << 16) | N1); + nv_wr32(dev, reg + 8, reg2 | (P << 28) | (M2 << 16) | N2); +- } else { ++ } else ++ if (dev_priv->chipset < NV_C0) { + ret = nv50_calc_pll2(dev, &pll, pclk, &N1, &N2, &M1, &P); + if (ret <= 0) + return 0; +@@ -298,6 +304,17 @@ nv50_crtc_set_clock(struct drm_device *dev, int head, int pclk) + nv_wr32(dev, reg, 0x50000610); + nv_wr32(dev, reg + 4, reg1 | (P << 16) | (M1 << 8) | N1); + nv_wr32(dev, reg + 8, N2); ++ } else { ++ ret = nv50_calc_pll2(dev, &pll, pclk, &N1, &N2, &M1, &P); ++ if (ret <= 0) ++ return 0; ++ ++ NV_DEBUG(dev, "pclk %d out %d N %d fN 0x%04x M %d P %d\n", ++ pclk, ret, N1, N2, M1, P); ++ ++ nv_mask(dev, reg + 0x0c, 0x00000000, 0x00000100); ++ nv_wr32(dev, reg + 0x04, (P << 16) | (N1 << 8) | M1); ++ nv_wr32(dev, reg + 0x10, N2 << 16); + } + + return 0; +@@ -321,7 +338,9 @@ nv50_crtc_destroy(struct drm_crtc *crtc) + + nv50_cursor_fini(nv_crtc); + ++ nouveau_bo_unmap(nv_crtc->lut.nvbo); + nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo); ++ nouveau_bo_unmap(nv_crtc->cursor.nvbo); + nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); + kfree(nv_crtc->mode); + kfree(nv_crtc); +@@ -440,47 +459,15 @@ nv50_crtc_prepare(struct drm_crtc *crtc) { struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); struct drm_device *dev = crtc->dev; @@ -5499,7 +13391,7 @@ index b4e4a3b..5d11ea1 100644 struct drm_device *dev = crtc->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_channel *evo = dev_priv->evo; -@@ -491,20 +459,14 @@ nv50_crtc_commit(struct drm_crtc *crtc) +@@ -491,20 +478,14 @@ nv50_crtc_commit(struct drm_crtc *crtc) nv50_crtc_blank(nv_crtc, false); @@ -5522,8 +13414,21 @@ index b4e4a3b..5d11ea1 100644 } static bool +diff --git a/drivers/gpu/drm/nouveau/nv50_cursor.c b/drivers/gpu/drm/nouveau/nv50_cursor.c +index 03ad7ab..1b9ce30 100644 +--- a/drivers/gpu/drm/nouveau/nv50_cursor.c ++++ b/drivers/gpu/drm/nouveau/nv50_cursor.c +@@ -147,7 +147,7 @@ nv50_cursor_fini(struct nouveau_crtc *nv_crtc) + NV_DEBUG_KMS(dev, "\n"); + + nv_wr32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(idx), 0); +- if (!nv_wait(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(idx), ++ if (!nv_wait(dev, 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", diff --git a/drivers/gpu/drm/nouveau/nv50_dac.c b/drivers/gpu/drm/nouveau/nv50_dac.c -index 1fd9537..1bc0859 100644 +index 1fd9537..875414b 100644 --- a/drivers/gpu/drm/nouveau/nv50_dac.c +++ b/drivers/gpu/drm/nouveau/nv50_dac.c @@ -37,22 +37,31 @@ @@ -5562,6 +13467,24 @@ index 1fd9537..1bc0859 100644 } static enum drm_connector_status +@@ -70,7 +79,7 @@ nv50_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector) + + 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), ++ if (!nv_wait(dev, 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, +@@ -121,7 +130,7 @@ nv50_dac_dpms(struct drm_encoder *encoder, int mode) + NV_DEBUG_KMS(dev, "or %d mode %d\n", or, mode); + + /* wait for it to be done */ +- if (!nv_wait(NV50_PDISPLAY_DAC_DPMS_CTRL(or), ++ if (!nv_wait(dev, 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, @@ -213,7 +222,8 @@ nv50_dac_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, uint32_t mode_ctl = 0, mode_ctl2 = 0; int ret; @@ -5633,37 +13556,287 @@ index 1fd9537..1bc0859 100644 } diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c -index 580a5d1..c19ed8c 100644 +index 580a5d1..11d366a 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c -@@ -71,14 +71,13 @@ nv50_evo_dmaobj_new(struct nouveau_channel *evo, uint32_t class, uint32_t name, +@@ -30,8 +30,22 @@ + #include "nouveau_connector.h" + #include "nouveau_fb.h" + #include "nouveau_fbcon.h" ++#include "nouveau_ramht.h" + #include "drm_crtc_helper.h" + ++static inline int ++nv50_sor_nr(struct drm_device *dev) ++{ ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ ++ if (dev_priv->chipset < 0x90 || ++ dev_priv->chipset == 0x92 || ++ dev_priv->chipset == 0xa0) ++ return 2; ++ ++ return 4; ++} ++ + static void + nv50_evo_channel_del(struct nouveau_channel **pchan) + { +@@ -42,6 +56,7 @@ nv50_evo_channel_del(struct nouveau_channel **pchan) + *pchan = NULL; + + nouveau_gpuobj_channel_takedown(chan); ++ nouveau_bo_unmap(chan->pushbuf_bo); + nouveau_bo_ref(NULL, &chan->pushbuf_bo); + + if (chan->user) +@@ -65,21 +80,23 @@ nv50_evo_dmaobj_new(struct nouveau_channel *evo, uint32_t class, uint32_t name, + return ret; + obj->engine = NVOBJ_ENGINE_DISPLAY; + +- ret = nouveau_gpuobj_ref_add(dev, evo, name, obj, NULL); ++ nv_wo32(obj, 0, (tile_flags << 22) | (magic_flags << 16) | class); ++ nv_wo32(obj, 4, limit); ++ nv_wo32(obj, 8, offset); ++ nv_wo32(obj, 12, 0x00000000); ++ nv_wo32(obj, 16, 0x00000000); ++ if (dev_priv->card_type < NV_C0) ++ nv_wo32(obj, 20, 0x00010000); ++ else ++ nv_wo32(obj, 20, 0x00020000); ++ dev_priv->engine.instmem.flush(dev); ++ ++ ret = nouveau_ramht_insert(evo, name, obj); ++ nouveau_gpuobj_ref(NULL, &obj); + 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); +- 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); -+ dev_priv->engine.instmem.flush(dev); - +- return 0; } -@@ -110,8 +109,8 @@ nv50_evo_channel_new(struct drm_device *dev, struct nouveau_channel **pchan) + +@@ -87,6 +104,7 @@ 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_gpuobj *ramht = NULL; + struct nouveau_channel *chan; + int ret; + +@@ -100,32 +118,35 @@ nv50_evo_channel_new(struct drm_device *dev, struct nouveau_channel **pchan) + 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); ++ ret = nouveau_gpuobj_new(dev, NULL, 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); -+ ret = drm_mm_init(&chan->ramin_heap, -+ chan->ramin->gpuobj->im_pramin->start, 32768); ++ ret = drm_mm_init(&chan->ramin_heap, 0, 32768); if (ret) { NV_ERROR(dev, "Error initialising EVO PRAMIN heap: %d\n", ret); nv50_evo_channel_del(pchan); -@@ -465,6 +464,7 @@ int nv50_display_create(struct drm_device *dev) + return ret; + } + +- ret = nouveau_gpuobj_new_ref(dev, chan, chan, 0, 4096, 16, +- 0, &chan->ramht); ++ ret = nouveau_gpuobj_new(dev, chan, 4096, 16, 0, &ramht); + if (ret) { + NV_ERROR(dev, "Unable to allocate EVO RAMHT: %d\n", ret); + nv50_evo_channel_del(pchan); + return ret; + } + ++ ret = nouveau_ramht_new(dev, ramht, &chan->ramht); ++ nouveau_gpuobj_ref(NULL, &ramht); ++ if (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); +@@ -179,13 +200,25 @@ nv50_evo_channel_new(struct drm_device *dev, struct nouveau_channel **pchan) + } + + int ++nv50_display_early_init(struct drm_device *dev) ++{ ++ return 0; ++} ++ ++void ++nv50_display_late_takedown(struct drm_device *dev) ++{ ++} ++ ++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_gpio_engine *pgpio = &dev_priv->engine.gpio; + struct nouveau_channel *evo = dev_priv->evo; + struct drm_connector *connector; +- uint32_t val, ram_amount, hpd_en[2]; ++ uint32_t val, ram_amount; + uint64_t start; + int ret, i; + +@@ -213,11 +246,11 @@ nv50_display_init(struct drm_device *dev) + nv_wr32(dev, 0x006101d0 + (i * 0x04), val); + } + /* SOR */ +- for (i = 0; i < 4; i++) { ++ for (i = 0; i < nv50_sor_nr(dev); i++) { + val = nv_rd32(dev, 0x0061c000 + (i * 0x800)); + nv_wr32(dev, 0x006101e0 + (i * 0x04), val); + } +- /* Something not yet in use, tv-out maybe. */ ++ /* EXT */ + for (i = 0; i < 3; i++) { + val = nv_rd32(dev, 0x0061e000 + (i * 0x800)); + nv_wr32(dev, 0x006101f0 + (i * 0x04), val); +@@ -246,7 +279,7 @@ nv50_display_init(struct drm_device *dev) + 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)) { ++ if (!nv_wait(dev, 0x006194e8, 2, 0)) { + NV_ERROR(dev, "timeout: (0x6194e8 & 2) != 0\n"); + NV_ERROR(dev, "0x6194e8 = 0x%08x\n", + nv_rd32(dev, 0x6194e8)); +@@ -277,7 +310,8 @@ nv50_display_init(struct drm_device *dev) + + 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)) { ++ if (!nv_wait(dev, 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))); +@@ -286,7 +320,7 @@ nv50_display_init(struct drm_device *dev) + + 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), ++ if (!nv_wait(dev, 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", +@@ -296,7 +330,7 @@ nv50_display_init(struct drm_device *dev) + + nv_wr32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), + NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_ON); +- if (!nv_wait(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), ++ if (!nv_wait(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), + NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS, + NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_ACTIVE)) { + NV_ERROR(dev, "timeout: " +@@ -307,7 +341,7 @@ nv50_display_init(struct drm_device *dev) + } + } + +- nv_wr32(dev, NV50_PDISPLAY_OBJECTS, (evo->ramin->instance >> 8) | 9); ++ nv_wr32(dev, NV50_PDISPLAY_OBJECTS, (evo->ramin->vinst >> 8) | 9); + + /* initialise fifo */ + nv_wr32(dev, NV50_PDISPLAY_CHANNEL_DMA_CB(0), +@@ -316,7 +350,7 @@ nv50_display_init(struct drm_device *dev) + 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)) { ++ if (!nv_wait(dev, 0x610200, 0x80000000, 0x00000000)) { + NV_ERROR(dev, "timeout: (0x610200 & 0x80000000) == 0\n"); + NV_ERROR(dev, "0x610200 = 0x%08x\n", nv_rd32(dev, 0x610200)); + return -EBUSY; +@@ -356,7 +390,7 @@ nv50_display_init(struct drm_device *dev) + 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)) ++ if (!nv_wait(dev, 0x640004, 0xffffffff, evo->dma.put << 2)) + NV_ERROR(dev, "evo pushbuf stalled\n"); + + /* enable clock change interrupts. */ +@@ -366,26 +400,13 @@ nv50_display_init(struct drm_device *dev) + 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 (conn->dcb->gpio_tag == 0xff) + 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]); ++ pgpio->irq_enable(dev, conn->dcb->gpio_tag, true); + } + + return 0; +@@ -423,7 +444,7 @@ static int nv50_display_disable(struct drm_device *dev) + continue; + + nv_wr32(dev, NV50_PDISPLAY_INTR_1, mask); +- if (!nv_wait(NV50_PDISPLAY_INTR_1, mask, mask)) { ++ if (!nv_wait(dev, 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", +@@ -433,14 +454,14 @@ static int nv50_display_disable(struct drm_device *dev) + + 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)) { ++ if (!nv_wait(dev, 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), ++ if (!nv_wait(dev, 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, +@@ -465,6 +486,7 @@ int nv50_display_create(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct dcb_table *dcb = &dev_priv->vbios.dcb; @@ -5671,7 +13844,7 @@ index 580a5d1..c19ed8c 100644 int ret, i; NV_DEBUG_KMS(dev, "\n"); -@@ -507,14 +507,18 @@ int nv50_display_create(struct drm_device *dev) +@@ -507,14 +529,18 @@ int nv50_display_create(struct drm_device *dev) continue; } @@ -5692,7 +13865,7 @@ index 580a5d1..c19ed8c 100644 break; default: NV_WARN(dev, "DCB encoder %d unknown\n", entry->type); -@@ -522,11 +526,13 @@ int nv50_display_create(struct drm_device *dev) +@@ -522,11 +548,13 @@ int nv50_display_create(struct drm_device *dev) } } @@ -5711,10 +13884,24 @@ index 580a5d1..c19ed8c 100644 } ret = nv50_display_init(dev); -@@ -552,131 +558,28 @@ int nv50_display_destroy(struct drm_device *dev) +@@ -538,7 +566,8 @@ int nv50_display_create(struct drm_device *dev) return 0; } +-int nv50_display_destroy(struct drm_device *dev) ++void ++nv50_display_destroy(struct drm_device *dev) + { + struct drm_nouveau_private *dev_priv = dev->dev_private; + +@@ -548,135 +577,30 @@ int nv50_display_destroy(struct drm_device *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) -{ @@ -5815,8 +14002,8 @@ index 580a5d1..c19ed8c 100644 - - 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) @@ -5850,7 +14037,7 @@ index 580a5d1..c19ed8c 100644 case OUTPUT_LVDS: script = (mc >> 8) & 0xf; if (bios->fp_no_ddc) { -@@ -767,17 +670,88 @@ nv50_display_vblank_handler(struct drm_device *dev, uint32_t intr) +@@ -767,17 +691,88 @@ nv50_display_vblank_handler(struct drm_device *dev, uint32_t intr) static void nv50_display_unk10_handler(struct drm_device *dev) { @@ -5899,14 +14086,14 @@ index 580a5d1..c19ed8c 100644 + or = i; + } + -+ for (i = 0; type == OUTPUT_ANY && i < 4; i++) { ++ for (i = 0; type == OUTPUT_ANY && i < nv50_sor_nr(dev); i++) { + if (dev_priv->chipset < 0x90 || + dev_priv->chipset == 0x92 || + dev_priv->chipset == 0xa0) + mc = nv_rd32(dev, NV50_PDISPLAY_SOR_MODE_CTRL_C(i)); + else + mc = nv_rd32(dev, NV90_PDISPLAY_SOR_MODE_CTRL_C(i)); - ++ + NV_DEBUG_KMS(dev, "SOR-%d mc: 0x%08x\n", i, mc); + if (!(mc & (1 << crtc))) + continue; @@ -5940,12 +14127,12 @@ index 580a5d1..c19ed8c 100644 + goto ack; + } + } -+ + + NV_ERROR(dev, "no dcb for %d %d 0x%08x\n", or, type, mc); ack: nv_wr32(dev, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK10); nv_wr32(dev, 0x610030, 0x80000000); -@@ -817,33 +791,103 @@ nv50_display_unk20_dp_hack(struct drm_device *dev, struct dcb_entry *dcb) +@@ -817,33 +812,103 @@ nv50_display_unk20_dp_hack(struct drm_device *dev, struct dcb_entry *dcb) static void nv50_display_unk20_handler(struct drm_device *dev) { @@ -5987,17 +14174,14 @@ index 580a5d1..c19ed8c 100644 - pclk = nv_rd32(dev, NV50_PDISPLAY_CRTC_P(head, CLOCK)) & 0x3fffff; - script = nv50_display_script_select(dev, dcbent, pclk); + pclk = nv_rd32(dev, NV50_PDISPLAY_CRTC_P(crtc, CLOCK)) & 0x003fffff; - -- NV_DEBUG_KMS(dev, "head %d pxclk: %dKHz\n", head, pclk); ++ + /* Find which encoder is connected to the CRTC */ + for (i = 0; type == OUTPUT_ANY && i < 3; i++) { + mc = nv_rd32(dev, NV50_PDISPLAY_DAC_MODE_CTRL_P(i)); + NV_DEBUG_KMS(dev, "DAC-%d mc: 0x%08x\n", i, mc); + if (!(mc & (1 << crtc))) + continue; - -- if (dcbent->type != OUTPUT_DP) -- nouveau_bios_run_display_table(dev, dcbent, 0, -2); ++ + switch ((mc & 0x00000f00) >> 8) { + case 0: type = OUTPUT_ANALOG; break; + case 1: type = OUTPUT_TV; break; @@ -6005,28 +14189,22 @@ index 580a5d1..c19ed8c 100644 + NV_ERROR(dev, "invalid mc, DAC-%d: 0x%08x\n", i, mc); + goto ack; + } - -- nv50_crtc_set_clock(dev, head, pclk); ++ + or = i; + } - -- nouveau_bios_run_display_table(dev, dcbent, script, pclk); -+ for (i = 0; type == OUTPUT_ANY && i < 4; i++) { ++ ++ for (i = 0; type == OUTPUT_ANY && i < nv50_sor_nr(dev); i++) { + if (dev_priv->chipset < 0x90 || + dev_priv->chipset == 0x92 || + dev_priv->chipset == 0xa0) + mc = nv_rd32(dev, NV50_PDISPLAY_SOR_MODE_CTRL_P(i)); + else + mc = nv_rd32(dev, NV90_PDISPLAY_SOR_MODE_CTRL_P(i)); - -- nv50_display_unk20_dp_hack(dev, dcbent); ++ + NV_DEBUG_KMS(dev, "SOR-%d mc: 0x%08x\n", i, mc); + if (!(mc & (1 << crtc))) + continue; - -- tmp = nv_rd32(dev, NV50_PDISPLAY_CRTC_CLK_CTRL2(head)); -- tmp &= ~0x000000f; -- nv_wr32(dev, NV50_PDISPLAY_CRTC_CLK_CTRL2(head), tmp); ++ + switch ((mc & 0x00000f00) >> 8) { + case 0: type = OUTPUT_LVDS; break; + case 1: type = OUTPUT_TMDS; break; @@ -6038,28 +14216,37 @@ index 580a5d1..c19ed8c 100644 + NV_ERROR(dev, "invalid mc, SOR-%d: 0x%08x\n", i, mc); + goto ack; + } -+ + +- NV_DEBUG_KMS(dev, "head %d pxclk: %dKHz\n", head, pclk); + or = i; + } -+ + +- if (dcbent->type != OUTPUT_DP) +- nouveau_bios_run_display_table(dev, dcbent, 0, -2); + if (type == OUTPUT_ANY) + goto ack; -+ + +- nv50_crtc_set_clock(dev, head, pclk); + /* Enable the encoder */ + for (i = 0; i < dev_priv->vbios.dcb.entries; i++) { + dcb = &dev_priv->vbios.dcb.entry[i]; + if (dcb->type == type && (dcb->or & (1 << or))) + break; + } -+ + +- nouveau_bios_run_display_table(dev, dcbent, script, pclk); + if (i == dev_priv->vbios.dcb.entries) { + NV_ERROR(dev, "no dcb for %d %d 0x%08x\n", or, type, mc); + goto ack; + } -+ + +- nv50_display_unk20_dp_hack(dev, dcbent); + script = nv50_display_script_select(dev, dcb, mc, pclk); + nouveau_bios_run_display_table(dev, dcb, script, pclk); -+ + +- tmp = nv_rd32(dev, NV50_PDISPLAY_CRTC_CLK_CTRL2(head)); +- tmp &= ~0x000000f; +- nv_wr32(dev, NV50_PDISPLAY_CRTC_CLK_CTRL2(head), tmp); + nv50_display_unk20_dp_hack(dev, dcb); - if (dcbent->type != OUTPUT_ANALOG) { @@ -6067,7 +14254,7 @@ index 580a5d1..c19ed8c 100644 tmp = nv_rd32(dev, NV50_PDISPLAY_SOR_CLK_CTRL2(or)); tmp &= ~0x00000f0f; if (script & 0x0100) -@@ -853,24 +897,61 @@ nv50_display_unk20_handler(struct drm_device *dev) +@@ -853,24 +918,61 @@ nv50_display_unk20_handler(struct drm_device *dev) nv_wr32(dev, NV50_PDISPLAY_DAC_CLK_CTRL2(or), 0); } @@ -6136,29 +14323,127 @@ index 580a5d1..c19ed8c 100644 ack: nv_wr32(dev, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK40); +diff --git a/drivers/gpu/drm/nouveau/nv50_display.h b/drivers/gpu/drm/nouveau/nv50_display.h +index 581d405..c551f0b 100644 +--- a/drivers/gpu/drm/nouveau/nv50_display.h ++++ b/drivers/gpu/drm/nouveau/nv50_display.h +@@ -38,9 +38,11 @@ + void nv50_display_irq_handler(struct drm_device *dev); + void nv50_display_irq_handler_bh(struct work_struct *work); + void nv50_display_irq_hotplug_bh(struct work_struct *work); +-int nv50_display_init(struct drm_device *dev); ++int nv50_display_early_init(struct drm_device *dev); ++void nv50_display_late_takedown(struct drm_device *dev); + int nv50_display_create(struct drm_device *dev); +-int nv50_display_destroy(struct drm_device *dev); ++int nv50_display_init(struct drm_device *dev); ++void 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); + +diff --git a/drivers/gpu/drm/nouveau/nv50_fb.c b/drivers/gpu/drm/nouveau/nv50_fb.c +index 32611bd..cd1988b 100644 +--- a/drivers/gpu/drm/nouveau/nv50_fb.c ++++ b/drivers/gpu/drm/nouveau/nv50_fb.c +@@ -20,6 +20,7 @@ nv50_fb_init(struct drm_device *dev) + case 0x50: + nv_wr32(dev, 0x100c90, 0x0707ff); + break; ++ case 0xa3: + case 0xa5: + case 0xa8: + nv_wr32(dev, 0x100c90, 0x0d0fff); +@@ -36,3 +37,42 @@ void + nv50_fb_takedown(struct drm_device *dev) + { + } ++ ++void ++nv50_fb_vm_trap(struct drm_device *dev, int display, const char *name) ++{ ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ u32 trap[6], idx, chinst; ++ int i, ch; ++ ++ idx = nv_rd32(dev, 0x100c90); ++ if (!(idx & 0x80000000)) ++ return; ++ idx &= 0x00ffffff; ++ ++ for (i = 0; i < 6; i++) { ++ nv_wr32(dev, 0x100c90, idx | i << 24); ++ trap[i] = nv_rd32(dev, 0x100c94); ++ } ++ nv_wr32(dev, 0x100c90, idx | 0x80000000); ++ ++ if (!display) ++ return; ++ ++ chinst = (trap[2] << 16) | trap[1]; ++ for (ch = 0; ch < dev_priv->engine.fifo.channels; ch++) { ++ struct nouveau_channel *chan = dev_priv->fifos[ch]; ++ ++ if (!chan || !chan->ramin) ++ continue; ++ ++ if (chinst == chan->ramin->vinst >> 12) ++ break; ++ } ++ ++ NV_INFO(dev, "%s - VM: Trapped %s at %02x%04x%04x status %08x " ++ "channel %d (0x%08x)\n", ++ name, (trap[5] & 0x100 ? "read" : "write"), ++ trap[5] & 0xff, trap[4] & 0xffff, trap[3] & 0xffff, ++ trap[0], ch, chinst); ++} +diff --git a/drivers/gpu/drm/nouveau/nv50_fbcon.c b/drivers/gpu/drm/nouveau/nv50_fbcon.c +index 6bf025c..6dcf048 100644 +--- a/drivers/gpu/drm/nouveau/nv50_fbcon.c ++++ b/drivers/gpu/drm/nouveau/nv50_fbcon.c +@@ -1,6 +1,7 @@ + #include "drmP.h" + #include "nouveau_drv.h" + #include "nouveau_dma.h" ++#include "nouveau_ramht.h" + #include "nouveau_fbcon.h" + + void +@@ -193,7 +194,8 @@ nv50_fbcon_accel_init(struct fb_info *info) + if (ret) + return ret; + +- ret = nouveau_gpuobj_ref_add(dev, dev_priv->channel, Nv2D, eng2d, NULL); ++ ret = nouveau_ramht_insert(dev_priv->channel, Nv2D, eng2d); ++ nouveau_gpuobj_ref(NULL, &eng2d); + if (ret) + return ret; + diff --git a/drivers/gpu/drm/nouveau/nv50_fifo.c b/drivers/gpu/drm/nouveau/nv50_fifo.c -index e20c0e2..fb0281a 100644 +index e20c0e2..a46a961 100644 --- a/drivers/gpu/drm/nouveau/nv50_fifo.c +++ b/drivers/gpu/drm/nouveau/nv50_fifo.c -@@ -28,41 +28,33 @@ +@@ -27,42 +27,37 @@ + #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) -- ++#include "nouveau_ramht.h" + static void -nv50_fifo_init_thingo(struct drm_device *dev) +nv50_fifo_playlist_update(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; + struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; - struct nouveau_gpuobj_ref *cur; ++ struct nouveau_gpuobj *cur; int i, nr; NV_DEBUG(dev, "\n"); @@ -6171,13 +14456,18 @@ index e20c0e2..fb0281a 100644 /* 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); +- if (dev_priv->fifos[i] && dev_priv->fifos[i]->ramfc) +- nv_wo32(dev, cur->gpuobj, nr++, i); ++ if (dev_priv->fifos[i] && dev_priv->fifos[i]->ramfc) { ++ nv_wo32(cur, (nr * 4), i); ++ nr++; ++ } } - dev_priv->engine.instmem.finish_access(dev); + dev_priv->engine.instmem.flush(dev); - nv_wr32(dev, 0x32f4, cur->instance >> 12); +- nv_wr32(dev, 0x32f4, cur->instance >> 12); ++ nv_wr32(dev, 0x32f4, cur->vinst >> 12); nv_wr32(dev, 0x32ec, nr); nv_wr32(dev, 0x2500, 0x101); } @@ -6189,7 +14479,7 @@ index e20c0e2..fb0281a 100644 { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_channel *chan = dev_priv->fifos[channel]; -@@ -70,37 +62,28 @@ nv50_fifo_channel_enable(struct drm_device *dev, int channel, bool nt) +@@ -70,37 +65,28 @@ nv50_fifo_channel_enable(struct drm_device *dev, int channel, bool nt) NV_DEBUG(dev, "ch%d\n", channel); @@ -6197,12 +14487,14 @@ index e20c0e2..fb0281a 100644 - return -EINVAL; - - if (IS_G80) +- inst = chan->ramfc->instance >> 12; + if (dev_priv->chipset == 0x50) - inst = chan->ramfc->instance >> 12; ++ inst = chan->ramfc->vinst >> 12; else - inst = chan->ramfc->instance >> 8; +- inst = chan->ramfc->instance >> 8; - nv_wr32(dev, NV50_PFIFO_CTX_TABLE(channel), - inst | NV50_PFIFO_CTX_TABLE_CHANNEL_ENABLED); ++ inst = chan->ramfc->vinst >> 8; - if (!nt) - nv50_fifo_init_thingo(dev); @@ -6233,7 +14525,7 @@ index e20c0e2..fb0281a 100644 } static void -@@ -133,12 +116,12 @@ nv50_fifo_init_context_table(struct drm_device *dev) +@@ -133,12 +119,12 @@ nv50_fifo_init_context_table(struct drm_device *dev) for (i = 0; i < NV50_PFIFO_CTX_TABLE__SIZE; i++) { if (dev_priv->fifos[i]) @@ -6249,7 +14541,7 @@ index e20c0e2..fb0281a 100644 } static void -@@ -162,41 +145,38 @@ nv50_fifo_init_regs(struct drm_device *dev) +@@ -162,41 +148,38 @@ nv50_fifo_init_regs(struct drm_device *dev) nv_wr32(dev, 0x3270, 0); /* Enable dummy channels setup by nv50_instmem.c */ @@ -6282,28 +14574,30 @@ index e20c0e2..fb0281a 100644 - return -ENOMEM; - dev_priv->engine.fifo.priv = priv; - - ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, 128*4, 0x1000, +- ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, 128*4, 0x1000, - NVOBJ_FLAG_ZERO_ALLOC, &priv->thingo[0]); -+ NVOBJ_FLAG_ZERO_ALLOC, -+ &pfifo->playlist[0]); ++ ret = nouveau_gpuobj_new(dev, NULL, 128*4, 0x1000, ++ NVOBJ_FLAG_ZERO_ALLOC, ++ &pfifo->playlist[0]); if (ret) { - NV_ERROR(dev, "error creating thingo0: %d\n", ret); + NV_ERROR(dev, "error creating playlist 0: %d\n", ret); return ret; } - ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, 128*4, 0x1000, +- ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, 128*4, 0x1000, - NVOBJ_FLAG_ZERO_ALLOC, &priv->thingo[1]); -+ NVOBJ_FLAG_ZERO_ALLOC, -+ &pfifo->playlist[1]); ++ ret = nouveau_gpuobj_new(dev, NULL, 128*4, 0x1000, ++ NVOBJ_FLAG_ZERO_ALLOC, ++ &pfifo->playlist[1]); if (ret) { - NV_ERROR(dev, "error creating thingo1: %d\n", ret); -+ nouveau_gpuobj_ref_del(dev, &pfifo->playlist[0]); ++ nouveau_gpuobj_ref(NULL, &pfifo->playlist[0]); + NV_ERROR(dev, "error creating playlist 1: %d\n", ret); return ret; } -@@ -216,18 +196,15 @@ void +@@ -216,18 +199,15 @@ void nv50_fifo_takedown(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; @@ -6321,45 +14615,102 @@ index e20c0e2..fb0281a 100644 - - dev_priv->engine.fifo.priv = NULL; - kfree(priv); -+ nouveau_gpuobj_ref_del(dev, &pfifo->playlist[0]); -+ nouveau_gpuobj_ref_del(dev, &pfifo->playlist[1]); ++ nouveau_gpuobj_ref(NULL, &pfifo->playlist[0]); ++ nouveau_gpuobj_ref(NULL, &pfifo->playlist[1]); } int -@@ -248,7 +225,7 @@ nv50_fifo_create_context(struct nouveau_channel *chan) +@@ -248,72 +228,61 @@ nv50_fifo_create_context(struct nouveau_channel *chan) 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, + if (dev_priv->chipset == 0x50) { - 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, chan->ramin->pinst, ++ chan->ramin->vinst, 0x100, ++ NVOBJ_FLAG_ZERO_ALLOC | ++ NVOBJ_FLAG_ZERO_FREE, + &chan->ramfc); + if (ret) + return ret; -@@ -281,10 +258,10 @@ nv50_fifo_create_context(struct nouveau_channel *chan) +- ret = nouveau_gpuobj_new_fake(dev, ramin_poffset + 0x0400, +- ramin_voffset + 0x0400, 4096, +- 0, NULL, &chan->cache); ++ ret = nouveau_gpuobj_new_fake(dev, chan->ramin->pinst + 0x0400, ++ chan->ramin->vinst + 0x0400, ++ 4096, 0, &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); ++ ret = nouveau_gpuobj_new(dev, chan, 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, 1024, +- 0, &chan->cache); ++ ret = nouveau_gpuobj_new(dev, chan, 4096, 1024, ++ 0, &chan->cache); + if (ret) + return ret; + } ++ ramfc = chan->ramfc; spin_lock_irqsave(&dev_priv->context_switch_lock, flags); - dev_priv->engine.instmem.prepare_access(dev, true); - - nv_wo32(dev, ramfc, 0x48/4, chan->pushbuf->instance >> 4); +- 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, 0x80/4, (0 << 27) /* 4KiB */ | -+ (4 << 24) /* SEARCH_FULL */ | -+ (chan->ramht->instance >> 4)); - nv_wo32(dev, ramfc, 0x44/4, 0x2101ffff); - nv_wo32(dev, ramfc, 0x60/4, 0x7fffffff); - nv_wo32(dev, ramfc, 0x40/4, 0x00000000); -@@ -295,7 +272,7 @@ nv50_fifo_create_context(struct nouveau_channel *chan) - chan->dma.ib_base * 4); - nv_wo32(dev, ramfc, 0x54/4, drm_order(chan->dma.ib_max + 1) << 16); - +- 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, 0x3c/4, 0x403f6078); +- nv_wo32(dev, ramfc, 0x50/4, chan->pushbuf_base + +- chan->dma.ib_base * 4); +- nv_wo32(dev, ramfc, 0x54/4, drm_order(chan->dma.ib_max + 1) << 16); +- - 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); ++ nv_wo32(ramfc, 0x48, chan->pushbuf->cinst >> 4); ++ nv_wo32(ramfc, 0x80, ((chan->ramht->bits - 9) << 27) | ++ (4 << 24) /* SEARCH_FULL */ | ++ (chan->ramht->gpuobj->cinst >> 4)); ++ nv_wo32(ramfc, 0x44, 0x2101ffff); ++ nv_wo32(ramfc, 0x60, 0x7fffffff); ++ nv_wo32(ramfc, 0x40, 0x00000000); ++ nv_wo32(ramfc, 0x7c, 0x30000001); ++ nv_wo32(ramfc, 0x78, 0x00000000); ++ nv_wo32(ramfc, 0x3c, 0x403f6078); ++ nv_wo32(ramfc, 0x50, chan->pushbuf_base + chan->dma.ib_base * 4); ++ nv_wo32(ramfc, 0x54, drm_order(chan->dma.ib_max + 1) << 16); ++ + if (dev_priv->chipset != 0x50) { - nv_wo32(dev, chan->ramin->gpuobj, 0, chan->id); - nv_wo32(dev, chan->ramin->gpuobj, 1, - chan->ramfc->instance >> 8); -@@ -304,16 +281,10 @@ nv50_fifo_create_context(struct nouveau_channel *chan) - nv_wo32(dev, ramfc, 0x98/4, chan->ramin->instance >> 12); ++ nv_wo32(chan->ramin, 0, chan->id); ++ nv_wo32(chan->ramin, 4, chan->ramfc->vinst >> 8); ++ ++ nv_wo32(ramfc, 0x88, chan->cache->vinst >> 10); ++ nv_wo32(ramfc, 0x98, chan->ramin->vinst >> 12); } - dev_priv->engine.instmem.finish_access(dev); @@ -6378,11 +14729,20 @@ index e20c0e2..fb0281a 100644 spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags); return 0; } -@@ -328,11 +299,12 @@ nv50_fifo_destroy_context(struct nouveau_channel *chan) +@@ -322,20 +291,22 @@ void + nv50_fifo_destroy_context(struct nouveau_channel *chan) + { + struct drm_device *dev = chan->dev; +- struct nouveau_gpuobj_ref *ramfc = chan->ramfc; ++ struct nouveau_gpuobj *ramfc = NULL; + + NV_DEBUG(dev, "ch%d\n", chan->id); /* This will ensure the channel is seen as disabled. */ - chan->ramfc = NULL; +- chan->ramfc = NULL; - nv50_fifo_channel_disable(dev, chan->id, false); ++ nouveau_gpuobj_ref(chan->ramfc, &ramfc); ++ nouveau_gpuobj_ref(NULL, &chan->ramfc); + nv50_fifo_channel_disable(dev, chan->id); /* Dummy channel, also used on ch 127 */ @@ -6391,28 +14751,121 @@ index e20c0e2..fb0281a 100644 + nv50_fifo_channel_disable(dev, 127); + nv50_fifo_playlist_update(dev); - nouveau_gpuobj_ref_del(dev, &ramfc); - nouveau_gpuobj_ref_del(dev, &chan->cache); -@@ -349,8 +321,6 @@ nv50_fifo_load_context(struct nouveau_channel *chan) +- nouveau_gpuobj_ref_del(dev, &ramfc); +- nouveau_gpuobj_ref_del(dev, &chan->cache); ++ nouveau_gpuobj_ref(NULL, &ramfc); ++ nouveau_gpuobj_ref(NULL, &chan->cache); + } + + int +@@ -343,69 +314,65 @@ 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; ++ struct nouveau_gpuobj *ramfc = chan->ramfc; ++ struct nouveau_gpuobj *cache = chan->cache; + 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)); -@@ -396,7 +366,7 @@ nv50_fifo_load_context(struct nouveau_channel *chan) +- 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); ++ nv_wr32(dev, 0x3330, nv_ro32(ramfc, 0x00)); ++ nv_wr32(dev, 0x3334, nv_ro32(ramfc, 0x04)); ++ nv_wr32(dev, 0x3240, nv_ro32(ramfc, 0x08)); ++ nv_wr32(dev, 0x3320, nv_ro32(ramfc, 0x0c)); ++ nv_wr32(dev, 0x3244, nv_ro32(ramfc, 0x10)); ++ nv_wr32(dev, 0x3328, nv_ro32(ramfc, 0x14)); ++ nv_wr32(dev, 0x3368, nv_ro32(ramfc, 0x18)); ++ nv_wr32(dev, 0x336c, nv_ro32(ramfc, 0x1c)); ++ nv_wr32(dev, 0x3370, nv_ro32(ramfc, 0x20)); ++ nv_wr32(dev, 0x3374, nv_ro32(ramfc, 0x24)); ++ nv_wr32(dev, 0x3378, nv_ro32(ramfc, 0x28)); ++ nv_wr32(dev, 0x337c, nv_ro32(ramfc, 0x2c)); ++ nv_wr32(dev, 0x3228, nv_ro32(ramfc, 0x30)); ++ nv_wr32(dev, 0x3364, nv_ro32(ramfc, 0x34)); ++ nv_wr32(dev, 0x32a0, nv_ro32(ramfc, 0x38)); ++ nv_wr32(dev, 0x3224, nv_ro32(ramfc, 0x3c)); ++ nv_wr32(dev, 0x324c, nv_ro32(ramfc, 0x40)); ++ nv_wr32(dev, 0x2044, nv_ro32(ramfc, 0x44)); ++ nv_wr32(dev, 0x322c, nv_ro32(ramfc, 0x48)); ++ nv_wr32(dev, 0x3234, nv_ro32(ramfc, 0x4c)); ++ nv_wr32(dev, 0x3340, nv_ro32(ramfc, 0x50)); ++ nv_wr32(dev, 0x3344, nv_ro32(ramfc, 0x54)); ++ nv_wr32(dev, 0x3280, nv_ro32(ramfc, 0x58)); ++ nv_wr32(dev, 0x3254, nv_ro32(ramfc, 0x5c)); ++ nv_wr32(dev, 0x3260, nv_ro32(ramfc, 0x60)); ++ nv_wr32(dev, 0x3264, nv_ro32(ramfc, 0x64)); ++ nv_wr32(dev, 0x3268, nv_ro32(ramfc, 0x68)); ++ nv_wr32(dev, 0x326c, nv_ro32(ramfc, 0x6c)); ++ nv_wr32(dev, 0x32e4, nv_ro32(ramfc, 0x70)); ++ nv_wr32(dev, 0x3248, nv_ro32(ramfc, 0x74)); ++ nv_wr32(dev, 0x2088, nv_ro32(ramfc, 0x78)); ++ nv_wr32(dev, 0x2058, nv_ro32(ramfc, 0x7c)); ++ nv_wr32(dev, 0x2210, nv_ro32(ramfc, 0x80)); ++ ++ cnt = nv_ro32(ramfc, 0x84); + for (ptr = 0; ptr < cnt; ptr++) { + nv_wr32(dev, NV40_PFIFO_CACHE1_METHOD(ptr), +- nv_ro32(dev, cache, (ptr * 2) + 0)); ++ nv_ro32(cache, (ptr * 8) + 0)); + nv_wr32(dev, NV40_PFIFO_CACHE1_DATA(ptr), +- nv_ro32(dev, cache, (ptr * 2) + 1)); ++ nv_ro32(cache, (ptr * 8) + 4)); + } + nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, cnt << 2); nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 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)); + if (dev_priv->chipset != 0x50) { - 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)); -@@ -404,8 +374,6 @@ nv50_fifo_load_context(struct nouveau_channel *chan) - nv_wr32(dev, 0x3410, nv_ro32(dev, ramfc, 0x98/4)); ++ nv_wr32(dev, 0x340c, nv_ro32(ramfc, 0x88)); ++ nv_wr32(dev, 0x3400, nv_ro32(ramfc, 0x8c)); ++ nv_wr32(dev, 0x3404, nv_ro32(ramfc, 0x90)); ++ nv_wr32(dev, 0x3408, nv_ro32(ramfc, 0x94)); ++ nv_wr32(dev, 0x3410, nv_ro32(ramfc, 0x98)); } - dev_priv->engine.instmem.finish_access(dev); @@ -6420,26 +14873,116 @@ index e20c0e2..fb0281a 100644 nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, chan->id | (1<<16)); return 0; } -@@ -434,8 +402,6 @@ nv50_fifo_unload_context(struct drm_device *dev) - ramfc = chan->ramfc->gpuobj; - cache = chan->cache->gpuobj; - +@@ -431,67 +398,66 @@ nv50_fifo_unload_context(struct drm_device *dev) + 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)); -@@ -482,7 +448,7 @@ nv50_fifo_unload_context(struct drm_device *dev) +- 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)); ++ ramfc = chan->ramfc; ++ cache = chan->cache; ++ ++ nv_wo32(ramfc, 0x00, nv_rd32(dev, 0x3330)); ++ nv_wo32(ramfc, 0x04, nv_rd32(dev, 0x3334)); ++ nv_wo32(ramfc, 0x08, nv_rd32(dev, 0x3240)); ++ nv_wo32(ramfc, 0x0c, nv_rd32(dev, 0x3320)); ++ nv_wo32(ramfc, 0x10, nv_rd32(dev, 0x3244)); ++ nv_wo32(ramfc, 0x14, nv_rd32(dev, 0x3328)); ++ nv_wo32(ramfc, 0x18, nv_rd32(dev, 0x3368)); ++ nv_wo32(ramfc, 0x1c, nv_rd32(dev, 0x336c)); ++ nv_wo32(ramfc, 0x20, nv_rd32(dev, 0x3370)); ++ nv_wo32(ramfc, 0x24, nv_rd32(dev, 0x3374)); ++ nv_wo32(ramfc, 0x28, nv_rd32(dev, 0x3378)); ++ nv_wo32(ramfc, 0x2c, nv_rd32(dev, 0x337c)); ++ nv_wo32(ramfc, 0x30, nv_rd32(dev, 0x3228)); ++ nv_wo32(ramfc, 0x34, nv_rd32(dev, 0x3364)); ++ nv_wo32(ramfc, 0x38, nv_rd32(dev, 0x32a0)); ++ nv_wo32(ramfc, 0x3c, nv_rd32(dev, 0x3224)); ++ nv_wo32(ramfc, 0x40, nv_rd32(dev, 0x324c)); ++ nv_wo32(ramfc, 0x44, nv_rd32(dev, 0x2044)); ++ nv_wo32(ramfc, 0x48, nv_rd32(dev, 0x322c)); ++ nv_wo32(ramfc, 0x4c, nv_rd32(dev, 0x3234)); ++ nv_wo32(ramfc, 0x50, nv_rd32(dev, 0x3340)); ++ nv_wo32(ramfc, 0x54, nv_rd32(dev, 0x3344)); ++ nv_wo32(ramfc, 0x58, nv_rd32(dev, 0x3280)); ++ nv_wo32(ramfc, 0x5c, nv_rd32(dev, 0x3254)); ++ nv_wo32(ramfc, 0x60, nv_rd32(dev, 0x3260)); ++ nv_wo32(ramfc, 0x64, nv_rd32(dev, 0x3264)); ++ nv_wo32(ramfc, 0x68, nv_rd32(dev, 0x3268)); ++ nv_wo32(ramfc, 0x6c, nv_rd32(dev, 0x326c)); ++ nv_wo32(ramfc, 0x70, nv_rd32(dev, 0x32e4)); ++ nv_wo32(ramfc, 0x74, nv_rd32(dev, 0x3248)); ++ nv_wo32(ramfc, 0x78, nv_rd32(dev, 0x2088)); ++ nv_wo32(ramfc, 0x7c, nv_rd32(dev, 0x2058)); ++ nv_wo32(ramfc, 0x80, 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))); ++ nv_wo32(cache, ptr + 0, ++ nv_rd32(dev, NV40_PFIFO_CACHE1_METHOD(get))); ++ nv_wo32(cache, ptr + 4, ++ nv_rd32(dev, NV40_PFIFO_CACHE1_DATA(get))); + get = (get + 1) & 0x1ff; ++ ptr += 8; } /* 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)); + if (dev_priv->chipset != 0x50) { - 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)); -@@ -491,7 +457,7 @@ nv50_fifo_unload_context(struct drm_device *dev) - nv_wo32(dev, ramfc, 0x98/4, nv_rd32(dev, 0x3410)); ++ nv_wo32(ramfc, 0x84, ptr >> 3); ++ nv_wo32(ramfc, 0x88, nv_rd32(dev, 0x340c)); ++ nv_wo32(ramfc, 0x8c, nv_rd32(dev, 0x3400)); ++ nv_wo32(ramfc, 0x90, nv_rd32(dev, 0x3404)); ++ nv_wo32(ramfc, 0x94, nv_rd32(dev, 0x3408)); ++ nv_wo32(ramfc, 0x98, nv_rd32(dev, 0x3410)); } - dev_priv->engine.instmem.finish_access(dev); @@ -6447,12 +14990,59 @@ index e20c0e2..fb0281a 100644 /*XXX: probably reload ch127 (NULL) state back too */ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, 127); +diff --git a/drivers/gpu/drm/nouveau/nv50_gpio.c b/drivers/gpu/drm/nouveau/nv50_gpio.c +index bb47ad7..b2fab2b 100644 +--- a/drivers/gpu/drm/nouveau/nv50_gpio.c ++++ b/drivers/gpu/drm/nouveau/nv50_gpio.c +@@ -74,3 +74,38 @@ nv50_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state) + nv_wr32(dev, r, v); + return 0; + } ++ ++void ++nv50_gpio_irq_enable(struct drm_device *dev, enum dcb_gpio_tag tag, bool on) ++{ ++ struct dcb_gpio_entry *gpio; ++ u32 reg, mask; ++ ++ gpio = nouveau_bios_gpio_entry(dev, tag); ++ if (!gpio) { ++ NV_ERROR(dev, "gpio tag 0x%02x not found\n", tag); ++ return; ++ } ++ ++ reg = gpio->line < 16 ? 0xe050 : 0xe070; ++ mask = 0x00010001 << (gpio->line & 0xf); ++ ++ nv_wr32(dev, reg + 4, mask); ++ nv_mask(dev, reg + 0, mask, on ? mask : 0); ++} ++ ++int ++nv50_gpio_init(struct drm_device *dev) ++{ ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ ++ /* disable, and ack any pending gpio interrupts */ ++ nv_wr32(dev, 0xe050, 0x00000000); ++ nv_wr32(dev, 0xe054, 0xffffffff); ++ if (dev_priv->chipset >= 0x90) { ++ nv_wr32(dev, 0xe070, 0x00000000); ++ nv_wr32(dev, 0xe074, 0xffffffff); ++ } ++ ++ return 0; ++} diff --git a/drivers/gpu/drm/nouveau/nv50_graph.c b/drivers/gpu/drm/nouveau/nv50_graph.c -index b203d06..1413028 100644 +index b203d06..cbf5ae2 100644 --- a/drivers/gpu/drm/nouveau/nv50_graph.c +++ b/drivers/gpu/drm/nouveau/nv50_graph.c -@@ -30,8 +30,6 @@ - +@@ -27,11 +27,9 @@ + #include "drmP.h" + #include "drm.h" + #include "nouveau_drv.h" +- ++#include "nouveau_ramht.h" #include "nouveau_grctx.h" -#define IS_G80 ((dev_priv->chipset & 0xf0) == 0x50) @@ -6528,34 +15118,56 @@ index b203d06..1413028 100644 } void -@@ -212,8 +205,9 @@ nv50_graph_create_context(struct nouveau_channel *chan) +@@ -188,7 +181,7 @@ nv50_graph_channel(struct drm_device *dev) + /* Be sure we're not in the middle of a context switch or bad things + * will happen, such as unloading the wrong pgraph context. + */ +- if (!nv_wait(0x400300, 0x00000001, 0x00000000)) ++ if (!nv_wait(dev, 0x400300, 0x00000001, 0x00000000)) + NV_ERROR(dev, "Ctxprog is still running\n"); + + inst = nv_rd32(dev, NV50_PGRAPH_CTXCTL_CUR); +@@ -199,7 +192,7 @@ nv50_graph_channel(struct drm_device *dev) + 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) ++ if (chan && chan->ramin && chan->ramin->vinst == inst) + return chan; + } + +@@ -211,44 +204,36 @@ 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 *ramin = chan->ramin->gpuobj; - struct nouveau_gpuobj *ctx; -+ struct nouveau_gpuobj *obj; ++ struct nouveau_gpuobj *ramin = chan->ramin; struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; + struct nouveau_grctx ctx = {}; int hdr, ret; NV_DEBUG(dev, "ch%d\n", chan->id); -@@ -223,10 +217,9 @@ nv50_graph_create_context(struct nouveau_channel *chan) - NVOBJ_FLAG_ZERO_FREE, &chan->ramin_grctx); + +- ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, pgraph->grctx_size, +- 0x1000, NVOBJ_FLAG_ZERO_ALLOC | +- NVOBJ_FLAG_ZERO_FREE, &chan->ramin_grctx); ++ ret = nouveau_gpuobj_new(dev, chan, pgraph->grctx_size, 0x1000, ++ NVOBJ_FLAG_ZERO_ALLOC | ++ NVOBJ_FLAG_ZERO_FREE, &chan->ramin_grctx); if (ret) return ret; - ctx = chan->ramin_grctx->gpuobj; -+ obj = chan->ramin_grctx->gpuobj; - +- - hdr = IS_G80 ? 0x200 : 0x20; - dev_priv->engine.instmem.prepare_access(dev, true); -+ hdr = (dev_priv->chipset == 0x50) ? 0x200 : 0x20; - nv_wo32(dev, ramin, (hdr + 0x00)/4, 0x00190002); - nv_wo32(dev, ramin, (hdr + 0x04)/4, chan->ramin_grctx->instance + - pgraph->grctx_size - 1); -@@ -234,21 +227,15 @@ nv50_graph_create_context(struct nouveau_channel *chan) - nv_wo32(dev, ramin, (hdr + 0x0c)/4, 0); - nv_wo32(dev, ramin, (hdr + 0x10)/4, 0); - nv_wo32(dev, ramin, (hdr + 0x14)/4, 0x00010000); +- nv_wo32(dev, ramin, (hdr + 0x00)/4, 0x00190002); +- nv_wo32(dev, ramin, (hdr + 0x04)/4, chan->ramin_grctx->instance + +- pgraph->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); @@ -6571,18 +15183,27 @@ index b203d06..1413028 100644 - nv_wo32(dev, ctx, 0x00000/4, chan->ramin->instance >> 12); - dev_priv->engine.instmem.finish_access(dev); ++ hdr = (dev_priv->chipset == 0x50) ? 0x200 : 0x20; ++ nv_wo32(ramin, hdr + 0x00, 0x00190002); ++ nv_wo32(ramin, hdr + 0x04, chan->ramin_grctx->vinst + ++ pgraph->grctx_size - 1); ++ nv_wo32(ramin, hdr + 0x08, chan->ramin_grctx->vinst); ++ nv_wo32(ramin, hdr + 0x0c, 0); ++ nv_wo32(ramin, hdr + 0x10, 0); ++ nv_wo32(ramin, hdr + 0x14, 0x00010000); ++ + ctx.dev = chan->dev; + ctx.mode = NOUVEAU_GRCTX_VALS; -+ ctx.data = obj; ++ ctx.data = chan->ramin_grctx; + nv50_grctx_init(&ctx); + -+ nv_wo32(dev, obj, 0x00000/4, chan->ramin->instance >> 12); ++ nv_wo32(chan->ramin_grctx, 0x00000, chan->ramin->vinst >> 12); + + dev_priv->engine.instmem.flush(dev); return 0; } -@@ -257,17 +244,16 @@ nv50_graph_destroy_context(struct nouveau_channel *chan) +@@ -257,19 +242,18 @@ nv50_graph_destroy_context(struct nouveau_channel *chan) { struct drm_device *dev = chan->dev; struct drm_nouveau_private *dev_priv = dev->dev_private; @@ -6591,74 +15212,4376 @@ index b203d06..1413028 100644 NV_DEBUG(dev, "ch%d\n", chan->id); - if (!chan->ramin || !chan->ramin->gpuobj) +- if (!chan->ramin || !chan->ramin->gpuobj) ++ if (!chan->ramin) 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); +- nv_wo32(dev, chan->ramin->gpuobj, i/4, 0); - dev_priv->engine.instmem.finish_access(dev); ++ nv_wo32(chan->ramin, i, 0); + dev_priv->engine.instmem.flush(dev); - nouveau_gpuobj_ref_del(dev, &chan->ramin_grctx); +- nouveau_gpuobj_ref_del(dev, &chan->ramin_grctx); ++ nouveau_gpuobj_ref(NULL, &chan->ramin_grctx); } + + static int +@@ -296,7 +280,7 @@ nv50_graph_do_load_context(struct drm_device *dev, uint32_t inst) + int + nv50_graph_load_context(struct nouveau_channel *chan) + { +- uint32_t inst = chan->ramin->instance >> 12; ++ uint32_t inst = chan->ramin->vinst >> 12; + + NV_DEBUG(chan->dev, "ch%d\n", chan->id); + return nv50_graph_do_load_context(chan->dev, inst); +@@ -341,15 +325,16 @@ static int + nv50_graph_nvsw_dma_vblsem(struct nouveau_channel *chan, int grclass, + int mthd, uint32_t data) + { +- struct nouveau_gpuobj_ref *ref = NULL; ++ struct nouveau_gpuobj *gpuobj; + +- if (nouveau_gpuobj_ref_find(chan, data, &ref)) ++ gpuobj = nouveau_ramht_find(chan, data); ++ if (!gpuobj) + return -ENOENT; + +- if (nouveau_notifier_offset(ref->gpuobj, NULL)) ++ if (nouveau_notifier_offset(gpuobj, NULL)) + return -EINVAL; + +- chan->nvsw.vblsem = ref->gpuobj; ++ chan->nvsw.vblsem = gpuobj; + chan->nvsw.vblsem_offset = ~0; + return 0; + } +diff --git a/drivers/gpu/drm/nouveau/nv50_grctx.c b/drivers/gpu/drm/nouveau/nv50_grctx.c +index 42a8fb2..336aab2 100644 +--- a/drivers/gpu/drm/nouveau/nv50_grctx.c ++++ b/drivers/gpu/drm/nouveau/nv50_grctx.c +@@ -103,6 +103,9 @@ + #include "nouveau_drv.h" + #include "nouveau_grctx.h" + ++#define IS_NVA3F(x) (((x) > 0xa0 && (x) < 0xaa) || (x) == 0xaf) ++#define IS_NVAAF(x) ((x) >= 0xaa && (x) <= 0xac) ++ + /* + * This code deals with PGRAPH contexts on NV50 family cards. Like NV40, it's + * the GPU itself that does context-switching, but it needs a special +@@ -182,6 +185,7 @@ nv50_grctx_init(struct nouveau_grctx *ctx) + case 0xa8: + case 0xaa: + case 0xac: ++ case 0xaf: + break; + default: + NV_ERROR(ctx->dev, "I don't know how to make a ctxprog for " +@@ -268,6 +272,9 @@ nv50_grctx_init(struct nouveau_grctx *ctx) + */ + + static void ++nv50_graph_construct_mmio_ddata(struct nouveau_grctx *ctx); ++ ++static void + nv50_graph_construct_mmio(struct nouveau_grctx *ctx) + { + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; +@@ -286,7 +293,7 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx) + gr_def(ctx, 0x400840, 0xffe806a8); + } + gr_def(ctx, 0x400844, 0x00000002); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) ++ if (IS_NVA3F(dev_priv->chipset)) + gr_def(ctx, 0x400894, 0x00001000); + gr_def(ctx, 0x4008e8, 0x00000003); + gr_def(ctx, 0x4008ec, 0x00001000); +@@ -299,13 +306,15 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx) + + if (dev_priv->chipset >= 0xa0) + cp_ctx(ctx, 0x400b00, 0x1); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) { ++ if (IS_NVA3F(dev_priv->chipset)) { + cp_ctx(ctx, 0x400b10, 0x1); + gr_def(ctx, 0x400b10, 0x0001629d); + cp_ctx(ctx, 0x400b20, 0x1); + gr_def(ctx, 0x400b20, 0x0001629d); + } + ++ nv50_graph_construct_mmio_ddata(ctx); ++ + /* 0C00: VFETCH */ + cp_ctx(ctx, 0x400c08, 0x2); + gr_def(ctx, 0x400c08, 0x0000fe0c); +@@ -314,7 +323,7 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx) + if (dev_priv->chipset < 0xa0) { + cp_ctx(ctx, 0x401008, 0x4); + gr_def(ctx, 0x401014, 0x00001000); +- } else if (dev_priv->chipset == 0xa0 || dev_priv->chipset >= 0xaa) { ++ } else if (!IS_NVA3F(dev_priv->chipset)) { + cp_ctx(ctx, 0x401008, 0x5); + gr_def(ctx, 0x401018, 0x00001000); + } else { +@@ -368,10 +377,13 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx) + case 0xa3: + case 0xa5: + case 0xa8: ++ case 0xaf: + gr_def(ctx, 0x401c00, 0x142500df); + break; + } + ++ /* 2000 */ ++ + /* 2400 */ + cp_ctx(ctx, 0x402400, 0x1); + if (dev_priv->chipset == 0x50) +@@ -380,12 +392,12 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx) + cp_ctx(ctx, 0x402408, 0x2); + gr_def(ctx, 0x402408, 0x00000600); + +- /* 2800 */ ++ /* 2800: CSCHED */ + cp_ctx(ctx, 0x402800, 0x1); + if (dev_priv->chipset == 0x50) + gr_def(ctx, 0x402800, 0x00000006); + +- /* 2C00 */ ++ /* 2C00: ZCULL */ + cp_ctx(ctx, 0x402c08, 0x6); + if (dev_priv->chipset != 0x50) + gr_def(ctx, 0x402c14, 0x01000000); +@@ -396,23 +408,23 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx) + cp_ctx(ctx, 0x402ca0, 0x2); + if (dev_priv->chipset < 0xa0) + gr_def(ctx, 0x402ca0, 0x00000400); +- else if (dev_priv->chipset == 0xa0 || dev_priv->chipset >= 0xaa) ++ else if (!IS_NVA3F(dev_priv->chipset)) + gr_def(ctx, 0x402ca0, 0x00000800); + else + gr_def(ctx, 0x402ca0, 0x00000400); + cp_ctx(ctx, 0x402cac, 0x4); + +- /* 3000 */ ++ /* 3000: ENG2D */ + cp_ctx(ctx, 0x403004, 0x1); + gr_def(ctx, 0x403004, 0x00000001); + +- /* 3404 */ ++ /* 3400 */ + if (dev_priv->chipset >= 0xa0) { + cp_ctx(ctx, 0x403404, 0x1); + gr_def(ctx, 0x403404, 0x00000001); + } + +- /* 5000 */ ++ /* 5000: CCACHE */ + cp_ctx(ctx, 0x405000, 0x1); + switch (dev_priv->chipset) { + case 0x50: +@@ -425,6 +437,7 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx) + case 0xa8: + case 0xaa: + case 0xac: ++ case 0xaf: + gr_def(ctx, 0x405000, 0x000e0080); + break; + case 0x86: +@@ -441,210 +454,6 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx) + cp_ctx(ctx, 0x405024, 0x1); + cp_ctx(ctx, 0x40502c, 0x1); + +- /* 5400 or maybe 4800 */ +- if (dev_priv->chipset == 0x50) { +- offset = 0x405400; +- cp_ctx(ctx, 0x405400, 0xea); +- } else if (dev_priv->chipset < 0x94) { +- offset = 0x405400; +- cp_ctx(ctx, 0x405400, 0xcb); +- } else if (dev_priv->chipset < 0xa0) { +- offset = 0x405400; +- cp_ctx(ctx, 0x405400, 0xcc); +- } else if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) { +- offset = 0x404800; +- cp_ctx(ctx, 0x404800, 0xda); +- } else { +- offset = 0x405400; +- cp_ctx(ctx, 0x405400, 0xd4); +- } +- gr_def(ctx, offset + 0x0c, 0x00000002); +- gr_def(ctx, offset + 0x10, 0x00000001); +- if (dev_priv->chipset >= 0x94) +- offset += 4; +- gr_def(ctx, offset + 0x1c, 0x00000001); +- gr_def(ctx, offset + 0x20, 0x00000100); +- gr_def(ctx, offset + 0x38, 0x00000002); +- gr_def(ctx, offset + 0x3c, 0x00000001); +- gr_def(ctx, offset + 0x40, 0x00000001); +- gr_def(ctx, offset + 0x50, 0x00000001); +- gr_def(ctx, offset + 0x54, 0x003fffff); +- gr_def(ctx, offset + 0x58, 0x00001fff); +- gr_def(ctx, offset + 0x60, 0x00000001); +- gr_def(ctx, offset + 0x64, 0x00000001); +- gr_def(ctx, offset + 0x6c, 0x00000001); +- gr_def(ctx, offset + 0x70, 0x00000001); +- gr_def(ctx, offset + 0x74, 0x00000001); +- gr_def(ctx, offset + 0x78, 0x00000004); +- gr_def(ctx, offset + 0x7c, 0x00000001); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) +- offset += 4; +- gr_def(ctx, offset + 0x80, 0x00000001); +- gr_def(ctx, offset + 0x84, 0x00000001); +- gr_def(ctx, offset + 0x88, 0x00000007); +- gr_def(ctx, offset + 0x8c, 0x00000001); +- gr_def(ctx, offset + 0x90, 0x00000007); +- gr_def(ctx, offset + 0x94, 0x00000001); +- gr_def(ctx, offset + 0x98, 0x00000001); +- gr_def(ctx, offset + 0x9c, 0x00000001); +- if (dev_priv->chipset == 0x50) { +- gr_def(ctx, offset + 0xb0, 0x00000001); +- gr_def(ctx, offset + 0xb4, 0x00000001); +- gr_def(ctx, offset + 0xbc, 0x00000001); +- gr_def(ctx, offset + 0xc0, 0x0000000a); +- gr_def(ctx, offset + 0xd0, 0x00000040); +- gr_def(ctx, offset + 0xd8, 0x00000002); +- gr_def(ctx, offset + 0xdc, 0x00000100); +- gr_def(ctx, offset + 0xe0, 0x00000001); +- gr_def(ctx, offset + 0xe4, 0x00000100); +- gr_def(ctx, offset + 0x100, 0x00000001); +- gr_def(ctx, offset + 0x124, 0x00000004); +- gr_def(ctx, offset + 0x13c, 0x00000001); +- gr_def(ctx, offset + 0x140, 0x00000100); +- gr_def(ctx, offset + 0x148, 0x00000001); +- gr_def(ctx, offset + 0x154, 0x00000100); +- gr_def(ctx, offset + 0x158, 0x00000001); +- gr_def(ctx, offset + 0x15c, 0x00000100); +- gr_def(ctx, offset + 0x164, 0x00000001); +- gr_def(ctx, offset + 0x170, 0x00000100); +- gr_def(ctx, offset + 0x174, 0x00000001); +- gr_def(ctx, offset + 0x17c, 0x00000001); +- gr_def(ctx, offset + 0x188, 0x00000002); +- gr_def(ctx, offset + 0x190, 0x00000001); +- gr_def(ctx, offset + 0x198, 0x00000001); +- gr_def(ctx, offset + 0x1ac, 0x00000003); +- offset += 0xd0; +- } else { +- gr_def(ctx, offset + 0xb0, 0x00000001); +- gr_def(ctx, offset + 0xb4, 0x00000100); +- gr_def(ctx, offset + 0xbc, 0x00000001); +- gr_def(ctx, offset + 0xc8, 0x00000100); +- gr_def(ctx, offset + 0xcc, 0x00000001); +- gr_def(ctx, offset + 0xd0, 0x00000100); +- gr_def(ctx, offset + 0xd8, 0x00000001); +- gr_def(ctx, offset + 0xe4, 0x00000100); +- } +- gr_def(ctx, offset + 0xf8, 0x00000004); +- gr_def(ctx, offset + 0xfc, 0x00000070); +- gr_def(ctx, offset + 0x100, 0x00000080); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) +- offset += 4; +- gr_def(ctx, offset + 0x114, 0x0000000c); +- if (dev_priv->chipset == 0x50) +- offset -= 4; +- gr_def(ctx, offset + 0x11c, 0x00000008); +- gr_def(ctx, offset + 0x120, 0x00000014); +- if (dev_priv->chipset == 0x50) { +- gr_def(ctx, offset + 0x124, 0x00000026); +- offset -= 0x18; +- } else { +- gr_def(ctx, offset + 0x128, 0x00000029); +- gr_def(ctx, offset + 0x12c, 0x00000027); +- gr_def(ctx, offset + 0x130, 0x00000026); +- gr_def(ctx, offset + 0x134, 0x00000008); +- gr_def(ctx, offset + 0x138, 0x00000004); +- gr_def(ctx, offset + 0x13c, 0x00000027); +- } +- gr_def(ctx, offset + 0x148, 0x00000001); +- gr_def(ctx, offset + 0x14c, 0x00000002); +- gr_def(ctx, offset + 0x150, 0x00000003); +- gr_def(ctx, offset + 0x154, 0x00000004); +- gr_def(ctx, offset + 0x158, 0x00000005); +- gr_def(ctx, offset + 0x15c, 0x00000006); +- gr_def(ctx, offset + 0x160, 0x00000007); +- gr_def(ctx, offset + 0x164, 0x00000001); +- gr_def(ctx, offset + 0x1a8, 0x000000cf); +- if (dev_priv->chipset == 0x50) +- offset -= 4; +- gr_def(ctx, offset + 0x1d8, 0x00000080); +- gr_def(ctx, offset + 0x1dc, 0x00000004); +- gr_def(ctx, offset + 0x1e0, 0x00000004); +- if (dev_priv->chipset == 0x50) +- offset -= 4; +- else +- gr_def(ctx, offset + 0x1e4, 0x00000003); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) { +- gr_def(ctx, offset + 0x1ec, 0x00000003); +- offset += 8; +- } +- gr_def(ctx, offset + 0x1e8, 0x00000001); +- if (dev_priv->chipset == 0x50) +- offset -= 4; +- gr_def(ctx, offset + 0x1f4, 0x00000012); +- gr_def(ctx, offset + 0x1f8, 0x00000010); +- gr_def(ctx, offset + 0x1fc, 0x0000000c); +- gr_def(ctx, offset + 0x200, 0x00000001); +- gr_def(ctx, offset + 0x210, 0x00000004); +- gr_def(ctx, offset + 0x214, 0x00000002); +- gr_def(ctx, offset + 0x218, 0x00000004); +- if (dev_priv->chipset >= 0xa0) +- offset += 4; +- gr_def(ctx, offset + 0x224, 0x003fffff); +- gr_def(ctx, offset + 0x228, 0x00001fff); +- if (dev_priv->chipset == 0x50) +- offset -= 0x20; +- else if (dev_priv->chipset >= 0xa0) { +- gr_def(ctx, offset + 0x250, 0x00000001); +- gr_def(ctx, offset + 0x254, 0x00000001); +- gr_def(ctx, offset + 0x258, 0x00000002); +- offset += 0x10; +- } +- gr_def(ctx, offset + 0x250, 0x00000004); +- gr_def(ctx, offset + 0x254, 0x00000014); +- gr_def(ctx, offset + 0x258, 0x00000001); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) +- offset += 4; +- gr_def(ctx, offset + 0x264, 0x00000002); +- if (dev_priv->chipset >= 0xa0) +- offset += 8; +- gr_def(ctx, offset + 0x270, 0x00000001); +- gr_def(ctx, offset + 0x278, 0x00000002); +- gr_def(ctx, offset + 0x27c, 0x00001000); +- if (dev_priv->chipset == 0x50) +- offset -= 0xc; +- else { +- gr_def(ctx, offset + 0x280, 0x00000e00); +- gr_def(ctx, offset + 0x284, 0x00001000); +- gr_def(ctx, offset + 0x288, 0x00001e00); +- } +- gr_def(ctx, offset + 0x290, 0x00000001); +- gr_def(ctx, offset + 0x294, 0x00000001); +- gr_def(ctx, offset + 0x298, 0x00000001); +- gr_def(ctx, offset + 0x29c, 0x00000001); +- gr_def(ctx, offset + 0x2a0, 0x00000001); +- gr_def(ctx, offset + 0x2b0, 0x00000200); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) { +- gr_def(ctx, offset + 0x2b4, 0x00000200); +- offset += 4; +- } +- if (dev_priv->chipset < 0xa0) { +- gr_def(ctx, offset + 0x2b8, 0x00000001); +- gr_def(ctx, offset + 0x2bc, 0x00000070); +- gr_def(ctx, offset + 0x2c0, 0x00000080); +- gr_def(ctx, offset + 0x2cc, 0x00000001); +- gr_def(ctx, offset + 0x2d0, 0x00000070); +- gr_def(ctx, offset + 0x2d4, 0x00000080); +- } else { +- gr_def(ctx, offset + 0x2b8, 0x00000001); +- gr_def(ctx, offset + 0x2bc, 0x000000f0); +- gr_def(ctx, offset + 0x2c0, 0x000000ff); +- gr_def(ctx, offset + 0x2cc, 0x00000001); +- gr_def(ctx, offset + 0x2d0, 0x000000f0); +- gr_def(ctx, offset + 0x2d4, 0x000000ff); +- gr_def(ctx, offset + 0x2dc, 0x00000009); +- offset += 4; +- } +- gr_def(ctx, offset + 0x2e4, 0x00000001); +- gr_def(ctx, offset + 0x2e8, 0x000000cf); +- gr_def(ctx, offset + 0x2f0, 0x00000001); +- gr_def(ctx, offset + 0x300, 0x000000cf); +- gr_def(ctx, offset + 0x308, 0x00000002); +- gr_def(ctx, offset + 0x310, 0x00000001); +- gr_def(ctx, offset + 0x318, 0x00000001); +- gr_def(ctx, offset + 0x320, 0x000000cf); +- gr_def(ctx, offset + 0x324, 0x000000cf); +- gr_def(ctx, offset + 0x328, 0x00000001); +- + /* 6000? */ + if (dev_priv->chipset == 0x50) + cp_ctx(ctx, 0x4063e0, 0x1); +@@ -661,7 +470,7 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx) + gr_def(ctx, 0x406818, 0x00000f80); + else + gr_def(ctx, 0x406818, 0x00001f80); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) ++ if (IS_NVA3F(dev_priv->chipset)) + gr_def(ctx, 0x40681c, 0x00000030); + cp_ctx(ctx, 0x406830, 0x3); + } +@@ -706,7 +515,7 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx) + + if (dev_priv->chipset < 0xa0) + cp_ctx(ctx, 0x407094 + (i<<8), 1); +- else if (dev_priv->chipset <= 0xa0 || dev_priv->chipset >= 0xaa) ++ else if (!IS_NVA3F(dev_priv->chipset)) + cp_ctx(ctx, 0x407094 + (i<<8), 3); + else { + cp_ctx(ctx, 0x407094 + (i<<8), 4); +@@ -799,6 +608,7 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx) + case 0xa8: + case 0xaa: + case 0xac: ++ case 0xaf: + gr_def(ctx, offset + 0x1c, 0x300c0000); + break; + } +@@ -825,7 +635,7 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx) + gr_def(ctx, base + 0x304, 0x00007070); + else if (dev_priv->chipset < 0xa0) + gr_def(ctx, base + 0x304, 0x00027070); +- else if (dev_priv->chipset <= 0xa0 || dev_priv->chipset >= 0xaa) ++ else if (!IS_NVA3F(dev_priv->chipset)) + gr_def(ctx, base + 0x304, 0x01127070); + else + gr_def(ctx, base + 0x304, 0x05127070); +@@ -849,7 +659,7 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx) + if (dev_priv->chipset < 0xa0) { + cp_ctx(ctx, base + 0x340, 9); + offset = base + 0x340; +- } else if (dev_priv->chipset <= 0xa0 || dev_priv->chipset >= 0xaa) { ++ } else if (!IS_NVA3F(dev_priv->chipset)) { + cp_ctx(ctx, base + 0x33c, 0xb); + offset = base + 0x344; + } else { +@@ -880,7 +690,7 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx) + gr_def(ctx, offset + 0x0, 0x000001f0); + gr_def(ctx, offset + 0x4, 0x00000001); + gr_def(ctx, offset + 0x8, 0x00000003); +- if (dev_priv->chipset == 0x50 || dev_priv->chipset >= 0xaa) ++ if (dev_priv->chipset == 0x50 || IS_NVAAF(dev_priv->chipset)) + gr_def(ctx, offset + 0xc, 0x00008000); + gr_def(ctx, offset + 0x14, 0x00039e00); + cp_ctx(ctx, offset + 0x1c, 2); +@@ -892,7 +702,7 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx) + + if (dev_priv->chipset >= 0xa0) { + cp_ctx(ctx, base + 0x54c, 2); +- if (dev_priv->chipset <= 0xa0 || dev_priv->chipset >= 0xaa) ++ if (!IS_NVA3F(dev_priv->chipset)) + gr_def(ctx, base + 0x54c, 0x003fe006); + else + gr_def(ctx, base + 0x54c, 0x003fe007); +@@ -948,6 +758,336 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx) + } + } + ++static void ++dd_emit(struct nouveau_grctx *ctx, int num, uint32_t val) { ++ int i; ++ if (val && ctx->mode == NOUVEAU_GRCTX_VALS) ++ for (i = 0; i < num; i++) ++ nv_wo32(ctx->data, 4 * (ctx->ctxvals_pos + i), val); ++ ctx->ctxvals_pos += num; ++} ++ ++static void ++nv50_graph_construct_mmio_ddata(struct nouveau_grctx *ctx) ++{ ++ struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; ++ int base, num; ++ base = ctx->ctxvals_pos; ++ ++ /* tesla state */ ++ dd_emit(ctx, 1, 0); /* 00000001 UNK0F90 */ ++ dd_emit(ctx, 1, 0); /* 00000001 UNK135C */ ++ ++ /* SRC_TIC state */ ++ dd_emit(ctx, 1, 0); /* 00000007 SRC_TILE_MODE_Z */ ++ dd_emit(ctx, 1, 2); /* 00000007 SRC_TILE_MODE_Y */ ++ dd_emit(ctx, 1, 1); /* 00000001 SRC_LINEAR #1 */ ++ dd_emit(ctx, 1, 0); /* 000000ff SRC_ADDRESS_HIGH */ ++ dd_emit(ctx, 1, 0); /* 00000001 SRC_SRGB */ ++ if (dev_priv->chipset >= 0x94) ++ dd_emit(ctx, 1, 0); /* 00000003 eng2d UNK0258 */ ++ dd_emit(ctx, 1, 1); /* 00000fff SRC_DEPTH */ ++ dd_emit(ctx, 1, 0x100); /* 0000ffff SRC_HEIGHT */ ++ ++ /* turing state */ ++ dd_emit(ctx, 1, 0); /* 0000000f TEXTURES_LOG2 */ ++ dd_emit(ctx, 1, 0); /* 0000000f SAMPLERS_LOG2 */ ++ dd_emit(ctx, 1, 0); /* 000000ff CB_DEF_ADDRESS_HIGH */ ++ dd_emit(ctx, 1, 0); /* ffffffff CB_DEF_ADDRESS_LOW */ ++ dd_emit(ctx, 1, 0); /* ffffffff SHARED_SIZE */ ++ dd_emit(ctx, 1, 2); /* ffffffff REG_MODE */ ++ dd_emit(ctx, 1, 1); /* 0000ffff BLOCK_ALLOC_THREADS */ ++ dd_emit(ctx, 1, 1); /* 00000001 LANES32 */ ++ dd_emit(ctx, 1, 0); /* 000000ff UNK370 */ ++ dd_emit(ctx, 1, 0); /* 000000ff USER_PARAM_UNK */ ++ dd_emit(ctx, 1, 0); /* 000000ff USER_PARAM_COUNT */ ++ dd_emit(ctx, 1, 1); /* 000000ff UNK384 bits 8-15 */ ++ dd_emit(ctx, 1, 0x3fffff); /* 003fffff TIC_LIMIT */ ++ dd_emit(ctx, 1, 0x1fff); /* 000fffff TSC_LIMIT */ ++ dd_emit(ctx, 1, 0); /* 0000ffff CB_ADDR_INDEX */ ++ dd_emit(ctx, 1, 1); /* 000007ff BLOCKDIM_X */ ++ dd_emit(ctx, 1, 1); /* 000007ff BLOCKDIM_XMY */ ++ dd_emit(ctx, 1, 0); /* 00000001 BLOCKDIM_XMY_OVERFLOW */ ++ dd_emit(ctx, 1, 1); /* 0003ffff BLOCKDIM_XMYMZ */ ++ dd_emit(ctx, 1, 1); /* 000007ff BLOCKDIM_Y */ ++ dd_emit(ctx, 1, 1); /* 0000007f BLOCKDIM_Z */ ++ dd_emit(ctx, 1, 4); /* 000000ff CP_REG_ALLOC_TEMP */ ++ dd_emit(ctx, 1, 1); /* 00000001 BLOCKDIM_DIRTY */ ++ if (IS_NVA3F(dev_priv->chipset)) ++ dd_emit(ctx, 1, 0); /* 00000003 UNK03E8 */ ++ dd_emit(ctx, 1, 1); /* 0000007f BLOCK_ALLOC_HALFWARPS */ ++ dd_emit(ctx, 1, 1); /* 00000007 LOCAL_WARPS_NO_CLAMP */ ++ dd_emit(ctx, 1, 7); /* 00000007 LOCAL_WARPS_LOG_ALLOC */ ++ dd_emit(ctx, 1, 1); /* 00000007 STACK_WARPS_NO_CLAMP */ ++ dd_emit(ctx, 1, 7); /* 00000007 STACK_WARPS_LOG_ALLOC */ ++ dd_emit(ctx, 1, 1); /* 00001fff BLOCK_ALLOC_REGSLOTS_PACKED */ ++ dd_emit(ctx, 1, 1); /* 00001fff BLOCK_ALLOC_REGSLOTS_STRIDED */ ++ dd_emit(ctx, 1, 1); /* 000007ff BLOCK_ALLOC_THREADS */ ++ ++ /* compat 2d state */ ++ if (dev_priv->chipset == 0x50) { ++ dd_emit(ctx, 4, 0); /* 0000ffff clip X, Y, W, H */ ++ ++ dd_emit(ctx, 1, 1); /* ffffffff chroma COLOR_FORMAT */ ++ ++ dd_emit(ctx, 1, 1); /* ffffffff pattern COLOR_FORMAT */ ++ dd_emit(ctx, 1, 0); /* ffffffff pattern SHAPE */ ++ dd_emit(ctx, 1, 1); /* ffffffff pattern PATTERN_SELECT */ ++ ++ dd_emit(ctx, 1, 0xa); /* ffffffff surf2d SRC_FORMAT */ ++ dd_emit(ctx, 1, 0); /* ffffffff surf2d DMA_SRC */ ++ dd_emit(ctx, 1, 0); /* 000000ff surf2d SRC_ADDRESS_HIGH */ ++ dd_emit(ctx, 1, 0); /* ffffffff surf2d SRC_ADDRESS_LOW */ ++ dd_emit(ctx, 1, 0x40); /* 0000ffff surf2d SRC_PITCH */ ++ dd_emit(ctx, 1, 0); /* 0000000f surf2d SRC_TILE_MODE_Z */ ++ dd_emit(ctx, 1, 2); /* 0000000f surf2d SRC_TILE_MODE_Y */ ++ dd_emit(ctx, 1, 0x100); /* ffffffff surf2d SRC_HEIGHT */ ++ dd_emit(ctx, 1, 1); /* 00000001 surf2d SRC_LINEAR */ ++ dd_emit(ctx, 1, 0x100); /* ffffffff surf2d SRC_WIDTH */ ++ ++ dd_emit(ctx, 1, 0); /* 0000ffff gdirect CLIP_B_X */ ++ dd_emit(ctx, 1, 0); /* 0000ffff gdirect CLIP_B_Y */ ++ dd_emit(ctx, 1, 0); /* 0000ffff gdirect CLIP_C_X */ ++ dd_emit(ctx, 1, 0); /* 0000ffff gdirect CLIP_C_Y */ ++ dd_emit(ctx, 1, 0); /* 0000ffff gdirect CLIP_D_X */ ++ dd_emit(ctx, 1, 0); /* 0000ffff gdirect CLIP_D_Y */ ++ dd_emit(ctx, 1, 1); /* ffffffff gdirect COLOR_FORMAT */ ++ dd_emit(ctx, 1, 0); /* ffffffff gdirect OPERATION */ ++ dd_emit(ctx, 1, 0); /* 0000ffff gdirect POINT_X */ ++ dd_emit(ctx, 1, 0); /* 0000ffff gdirect POINT_Y */ ++ ++ dd_emit(ctx, 1, 0); /* 0000ffff blit SRC_Y */ ++ dd_emit(ctx, 1, 0); /* ffffffff blit OPERATION */ ++ ++ dd_emit(ctx, 1, 0); /* ffffffff ifc OPERATION */ ++ ++ dd_emit(ctx, 1, 0); /* ffffffff iifc INDEX_FORMAT */ ++ dd_emit(ctx, 1, 0); /* ffffffff iifc LUT_OFFSET */ ++ dd_emit(ctx, 1, 4); /* ffffffff iifc COLOR_FORMAT */ ++ dd_emit(ctx, 1, 0); /* ffffffff iifc OPERATION */ ++ } ++ ++ /* m2mf state */ ++ dd_emit(ctx, 1, 0); /* ffffffff m2mf LINE_COUNT */ ++ dd_emit(ctx, 1, 0); /* ffffffff m2mf LINE_LENGTH_IN */ ++ dd_emit(ctx, 2, 0); /* ffffffff m2mf OFFSET_IN, OFFSET_OUT */ ++ dd_emit(ctx, 1, 1); /* ffffffff m2mf TILING_DEPTH_OUT */ ++ dd_emit(ctx, 1, 0x100); /* ffffffff m2mf TILING_HEIGHT_OUT */ ++ dd_emit(ctx, 1, 0); /* ffffffff m2mf TILING_POSITION_OUT_Z */ ++ dd_emit(ctx, 1, 1); /* 00000001 m2mf LINEAR_OUT */ ++ dd_emit(ctx, 2, 0); /* 0000ffff m2mf TILING_POSITION_OUT_X, Y */ ++ dd_emit(ctx, 1, 0x100); /* ffffffff m2mf TILING_PITCH_OUT */ ++ dd_emit(ctx, 1, 1); /* ffffffff m2mf TILING_DEPTH_IN */ ++ dd_emit(ctx, 1, 0x100); /* ffffffff m2mf TILING_HEIGHT_IN */ ++ dd_emit(ctx, 1, 0); /* ffffffff m2mf TILING_POSITION_IN_Z */ ++ dd_emit(ctx, 1, 1); /* 00000001 m2mf LINEAR_IN */ ++ dd_emit(ctx, 2, 0); /* 0000ffff m2mf TILING_POSITION_IN_X, Y */ ++ dd_emit(ctx, 1, 0x100); /* ffffffff m2mf TILING_PITCH_IN */ ++ ++ /* more compat 2d state */ ++ if (dev_priv->chipset == 0x50) { ++ dd_emit(ctx, 1, 1); /* ffffffff line COLOR_FORMAT */ ++ dd_emit(ctx, 1, 0); /* ffffffff line OPERATION */ ++ ++ dd_emit(ctx, 1, 1); /* ffffffff triangle COLOR_FORMAT */ ++ dd_emit(ctx, 1, 0); /* ffffffff triangle OPERATION */ ++ ++ dd_emit(ctx, 1, 0); /* 0000000f sifm TILE_MODE_Z */ ++ dd_emit(ctx, 1, 2); /* 0000000f sifm TILE_MODE_Y */ ++ dd_emit(ctx, 1, 0); /* 000000ff sifm FORMAT_FILTER */ ++ dd_emit(ctx, 1, 1); /* 000000ff sifm FORMAT_ORIGIN */ ++ dd_emit(ctx, 1, 0); /* 0000ffff sifm SRC_PITCH */ ++ dd_emit(ctx, 1, 1); /* 00000001 sifm SRC_LINEAR */ ++ dd_emit(ctx, 1, 0); /* 000000ff sifm SRC_OFFSET_HIGH */ ++ dd_emit(ctx, 1, 0); /* ffffffff sifm SRC_OFFSET */ ++ dd_emit(ctx, 1, 0); /* 0000ffff sifm SRC_HEIGHT */ ++ dd_emit(ctx, 1, 0); /* 0000ffff sifm SRC_WIDTH */ ++ dd_emit(ctx, 1, 3); /* ffffffff sifm COLOR_FORMAT */ ++ dd_emit(ctx, 1, 0); /* ffffffff sifm OPERATION */ ++ ++ dd_emit(ctx, 1, 0); /* ffffffff sifc OPERATION */ ++ } ++ ++ /* tesla state */ ++ dd_emit(ctx, 1, 0); /* 0000000f GP_TEXTURES_LOG2 */ ++ dd_emit(ctx, 1, 0); /* 0000000f GP_SAMPLERS_LOG2 */ ++ dd_emit(ctx, 1, 0); /* 000000ff */ ++ dd_emit(ctx, 1, 0); /* ffffffff */ ++ dd_emit(ctx, 1, 4); /* 000000ff UNK12B0_0 */ ++ dd_emit(ctx, 1, 0x70); /* 000000ff UNK12B0_1 */ ++ dd_emit(ctx, 1, 0x80); /* 000000ff UNK12B0_3 */ ++ dd_emit(ctx, 1, 0); /* 000000ff UNK12B0_2 */ ++ dd_emit(ctx, 1, 0); /* 0000000f FP_TEXTURES_LOG2 */ ++ dd_emit(ctx, 1, 0); /* 0000000f FP_SAMPLERS_LOG2 */ ++ if (IS_NVA3F(dev_priv->chipset)) { ++ dd_emit(ctx, 1, 0); /* ffffffff */ ++ dd_emit(ctx, 1, 0); /* 0000007f MULTISAMPLE_SAMPLES_LOG2 */ ++ } else { ++ dd_emit(ctx, 1, 0); /* 0000000f MULTISAMPLE_SAMPLES_LOG2 */ ++ } ++ dd_emit(ctx, 1, 0xc); /* 000000ff SEMANTIC_COLOR.BFC0_ID */ ++ if (dev_priv->chipset != 0x50) ++ dd_emit(ctx, 1, 0); /* 00000001 SEMANTIC_COLOR.CLMP_EN */ ++ dd_emit(ctx, 1, 8); /* 000000ff SEMANTIC_COLOR.COLR_NR */ ++ dd_emit(ctx, 1, 0x14); /* 000000ff SEMANTIC_COLOR.FFC0_ID */ ++ if (dev_priv->chipset == 0x50) { ++ dd_emit(ctx, 1, 0); /* 000000ff SEMANTIC_LAYER */ ++ dd_emit(ctx, 1, 0); /* 00000001 */ ++ } else { ++ dd_emit(ctx, 1, 0); /* 00000001 SEMANTIC_PTSZ.ENABLE */ ++ dd_emit(ctx, 1, 0x29); /* 000000ff SEMANTIC_PTSZ.PTSZ_ID */ ++ dd_emit(ctx, 1, 0x27); /* 000000ff SEMANTIC_PRIM */ ++ dd_emit(ctx, 1, 0x26); /* 000000ff SEMANTIC_LAYER */ ++ dd_emit(ctx, 1, 8); /* 0000000f SMENATIC_CLIP.CLIP_HIGH */ ++ dd_emit(ctx, 1, 4); /* 000000ff SEMANTIC_CLIP.CLIP_LO */ ++ dd_emit(ctx, 1, 0x27); /* 000000ff UNK0FD4 */ ++ dd_emit(ctx, 1, 0); /* 00000001 UNK1900 */ ++ } ++ dd_emit(ctx, 1, 0); /* 00000007 RT_CONTROL_MAP0 */ ++ dd_emit(ctx, 1, 1); /* 00000007 RT_CONTROL_MAP1 */ ++ dd_emit(ctx, 1, 2); /* 00000007 RT_CONTROL_MAP2 */ ++ dd_emit(ctx, 1, 3); /* 00000007 RT_CONTROL_MAP3 */ ++ dd_emit(ctx, 1, 4); /* 00000007 RT_CONTROL_MAP4 */ ++ dd_emit(ctx, 1, 5); /* 00000007 RT_CONTROL_MAP5 */ ++ dd_emit(ctx, 1, 6); /* 00000007 RT_CONTROL_MAP6 */ ++ dd_emit(ctx, 1, 7); /* 00000007 RT_CONTROL_MAP7 */ ++ dd_emit(ctx, 1, 1); /* 0000000f RT_CONTROL_COUNT */ ++ dd_emit(ctx, 8, 0); /* 00000001 RT_HORIZ_UNK */ ++ dd_emit(ctx, 8, 0); /* ffffffff RT_ADDRESS_LOW */ ++ dd_emit(ctx, 1, 0xcf); /* 000000ff RT_FORMAT */ ++ dd_emit(ctx, 7, 0); /* 000000ff RT_FORMAT */ ++ if (dev_priv->chipset != 0x50) ++ dd_emit(ctx, 3, 0); /* 1, 1, 1 */ ++ else ++ dd_emit(ctx, 2, 0); /* 1, 1 */ ++ dd_emit(ctx, 1, 0); /* ffffffff GP_ENABLE */ ++ dd_emit(ctx, 1, 0x80); /* 0000ffff GP_VERTEX_OUTPUT_COUNT*/ ++ dd_emit(ctx, 1, 4); /* 000000ff GP_REG_ALLOC_RESULT */ ++ dd_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */ ++ if (IS_NVA3F(dev_priv->chipset)) { ++ dd_emit(ctx, 1, 3); /* 00000003 */ ++ dd_emit(ctx, 1, 0); /* 00000001 UNK1418. Alone. */ ++ } ++ if (dev_priv->chipset != 0x50) ++ dd_emit(ctx, 1, 3); /* 00000003 UNK15AC */ ++ dd_emit(ctx, 1, 1); /* ffffffff RASTERIZE_ENABLE */ ++ dd_emit(ctx, 1, 0); /* 00000001 FP_CONTROL.EXPORTS_Z */ ++ if (dev_priv->chipset != 0x50) ++ dd_emit(ctx, 1, 0); /* 00000001 FP_CONTROL.MULTIPLE_RESULTS */ ++ dd_emit(ctx, 1, 0x12); /* 000000ff FP_INTERPOLANT_CTRL.COUNT */ ++ dd_emit(ctx, 1, 0x10); /* 000000ff FP_INTERPOLANT_CTRL.COUNT_NONFLAT */ ++ dd_emit(ctx, 1, 0xc); /* 000000ff FP_INTERPOLANT_CTRL.OFFSET */ ++ dd_emit(ctx, 1, 1); /* 00000001 FP_INTERPOLANT_CTRL.UMASK.W */ ++ dd_emit(ctx, 1, 0); /* 00000001 FP_INTERPOLANT_CTRL.UMASK.X */ ++ dd_emit(ctx, 1, 0); /* 00000001 FP_INTERPOLANT_CTRL.UMASK.Y */ ++ dd_emit(ctx, 1, 0); /* 00000001 FP_INTERPOLANT_CTRL.UMASK.Z */ ++ dd_emit(ctx, 1, 4); /* 000000ff FP_RESULT_COUNT */ ++ dd_emit(ctx, 1, 2); /* ffffffff REG_MODE */ ++ dd_emit(ctx, 1, 4); /* 000000ff FP_REG_ALLOC_TEMP */ ++ if (dev_priv->chipset >= 0xa0) ++ dd_emit(ctx, 1, 0); /* ffffffff */ ++ dd_emit(ctx, 1, 0); /* 00000001 GP_BUILTIN_RESULT_EN.LAYER_IDX */ ++ dd_emit(ctx, 1, 0); /* ffffffff STRMOUT_ENABLE */ ++ dd_emit(ctx, 1, 0x3fffff); /* 003fffff TIC_LIMIT */ ++ dd_emit(ctx, 1, 0x1fff); /* 000fffff TSC_LIMIT */ ++ dd_emit(ctx, 1, 0); /* 00000001 VERTEX_TWO_SIDE_ENABLE*/ ++ if (dev_priv->chipset != 0x50) ++ dd_emit(ctx, 8, 0); /* 00000001 */ ++ if (dev_priv->chipset >= 0xa0) { ++ dd_emit(ctx, 1, 1); /* 00000007 VTX_ATTR_DEFINE.COMP */ ++ dd_emit(ctx, 1, 1); /* 00000007 VTX_ATTR_DEFINE.SIZE */ ++ dd_emit(ctx, 1, 2); /* 00000007 VTX_ATTR_DEFINE.TYPE */ ++ dd_emit(ctx, 1, 0); /* 000000ff VTX_ATTR_DEFINE.ATTR */ ++ } ++ dd_emit(ctx, 1, 4); /* 0000007f VP_RESULT_MAP_SIZE */ ++ dd_emit(ctx, 1, 0x14); /* 0000001f ZETA_FORMAT */ ++ dd_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */ ++ dd_emit(ctx, 1, 0); /* 0000000f VP_TEXTURES_LOG2 */ ++ dd_emit(ctx, 1, 0); /* 0000000f VP_SAMPLERS_LOG2 */ ++ if (IS_NVA3F(dev_priv->chipset)) ++ dd_emit(ctx, 1, 0); /* 00000001 */ ++ dd_emit(ctx, 1, 2); /* 00000003 POLYGON_MODE_BACK */ ++ if (dev_priv->chipset >= 0xa0) ++ dd_emit(ctx, 1, 0); /* 00000003 VTX_ATTR_DEFINE.SIZE - 1 */ ++ dd_emit(ctx, 1, 0); /* 0000ffff CB_ADDR_INDEX */ ++ if (dev_priv->chipset >= 0xa0) ++ dd_emit(ctx, 1, 0); /* 00000003 */ ++ dd_emit(ctx, 1, 0); /* 00000001 CULL_FACE_ENABLE */ ++ dd_emit(ctx, 1, 1); /* 00000003 CULL_FACE */ ++ dd_emit(ctx, 1, 0); /* 00000001 FRONT_FACE */ ++ dd_emit(ctx, 1, 2); /* 00000003 POLYGON_MODE_FRONT */ ++ dd_emit(ctx, 1, 0x1000); /* 00007fff UNK141C */ ++ if (dev_priv->chipset != 0x50) { ++ dd_emit(ctx, 1, 0xe00); /* 7fff */ ++ dd_emit(ctx, 1, 0x1000); /* 7fff */ ++ dd_emit(ctx, 1, 0x1e00); /* 7fff */ ++ } ++ dd_emit(ctx, 1, 0); /* 00000001 BEGIN_END_ACTIVE */ ++ dd_emit(ctx, 1, 1); /* 00000001 POLYGON_MODE_??? */ ++ dd_emit(ctx, 1, 1); /* 000000ff GP_REG_ALLOC_TEMP / 4 rounded up */ ++ dd_emit(ctx, 1, 1); /* 000000ff FP_REG_ALLOC_TEMP... without /4? */ ++ dd_emit(ctx, 1, 1); /* 000000ff VP_REG_ALLOC_TEMP / 4 rounded up */ ++ dd_emit(ctx, 1, 1); /* 00000001 */ ++ dd_emit(ctx, 1, 0); /* 00000001 */ ++ dd_emit(ctx, 1, 0); /* 00000001 VTX_ATTR_MASK_UNK0 nonempty */ ++ dd_emit(ctx, 1, 0); /* 00000001 VTX_ATTR_MASK_UNK1 nonempty */ ++ dd_emit(ctx, 1, 0x200); /* 0003ffff GP_VERTEX_OUTPUT_COUNT*GP_REG_ALLOC_RESULT */ ++ if (IS_NVA3F(dev_priv->chipset)) ++ dd_emit(ctx, 1, 0x200); ++ dd_emit(ctx, 1, 0); /* 00000001 */ ++ if (dev_priv->chipset < 0xa0) { ++ dd_emit(ctx, 1, 1); /* 00000001 */ ++ dd_emit(ctx, 1, 0x70); /* 000000ff */ ++ dd_emit(ctx, 1, 0x80); /* 000000ff */ ++ dd_emit(ctx, 1, 0); /* 000000ff */ ++ dd_emit(ctx, 1, 0); /* 00000001 */ ++ dd_emit(ctx, 1, 1); /* 00000001 */ ++ dd_emit(ctx, 1, 0x70); /* 000000ff */ ++ dd_emit(ctx, 1, 0x80); /* 000000ff */ ++ dd_emit(ctx, 1, 0); /* 000000ff */ ++ } else { ++ dd_emit(ctx, 1, 1); /* 00000001 */ ++ dd_emit(ctx, 1, 0xf0); /* 000000ff */ ++ dd_emit(ctx, 1, 0xff); /* 000000ff */ ++ dd_emit(ctx, 1, 0); /* 000000ff */ ++ dd_emit(ctx, 1, 0); /* 00000001 */ ++ dd_emit(ctx, 1, 1); /* 00000001 */ ++ dd_emit(ctx, 1, 0xf0); /* 000000ff */ ++ dd_emit(ctx, 1, 0xff); /* 000000ff */ ++ dd_emit(ctx, 1, 0); /* 000000ff */ ++ dd_emit(ctx, 1, 9); /* 0000003f UNK114C.COMP,SIZE */ ++ } ++ ++ /* eng2d state */ ++ dd_emit(ctx, 1, 0); /* 00000001 eng2d COLOR_KEY_ENABLE */ ++ dd_emit(ctx, 1, 0); /* 00000007 eng2d COLOR_KEY_FORMAT */ ++ dd_emit(ctx, 1, 1); /* ffffffff eng2d DST_DEPTH */ ++ dd_emit(ctx, 1, 0xcf); /* 000000ff eng2d DST_FORMAT */ ++ dd_emit(ctx, 1, 0); /* ffffffff eng2d DST_LAYER */ ++ dd_emit(ctx, 1, 1); /* 00000001 eng2d DST_LINEAR */ ++ dd_emit(ctx, 1, 0); /* 00000007 eng2d PATTERN_COLOR_FORMAT */ ++ dd_emit(ctx, 1, 0); /* 00000007 eng2d OPERATION */ ++ dd_emit(ctx, 1, 0); /* 00000003 eng2d PATTERN_SELECT */ ++ dd_emit(ctx, 1, 0xcf); /* 000000ff eng2d SIFC_FORMAT */ ++ dd_emit(ctx, 1, 0); /* 00000001 eng2d SIFC_BITMAP_ENABLE */ ++ dd_emit(ctx, 1, 2); /* 00000003 eng2d SIFC_BITMAP_UNK808 */ ++ dd_emit(ctx, 1, 0); /* ffffffff eng2d BLIT_DU_DX_FRACT */ ++ dd_emit(ctx, 1, 1); /* ffffffff eng2d BLIT_DU_DX_INT */ ++ dd_emit(ctx, 1, 0); /* ffffffff eng2d BLIT_DV_DY_FRACT */ ++ dd_emit(ctx, 1, 1); /* ffffffff eng2d BLIT_DV_DY_INT */ ++ dd_emit(ctx, 1, 0); /* 00000001 eng2d BLIT_CONTROL_FILTER */ ++ dd_emit(ctx, 1, 0xcf); /* 000000ff eng2d DRAW_COLOR_FORMAT */ ++ dd_emit(ctx, 1, 0xcf); /* 000000ff eng2d SRC_FORMAT */ ++ dd_emit(ctx, 1, 1); /* 00000001 eng2d SRC_LINEAR #2 */ ++ ++ num = ctx->ctxvals_pos - base; ++ ctx->ctxvals_pos = base; ++ if (IS_NVA3F(dev_priv->chipset)) ++ cp_ctx(ctx, 0x404800, num); ++ else ++ cp_ctx(ctx, 0x405400, num); ++} ++ + /* + * xfer areas. These are a pain. + * +@@ -990,28 +1130,33 @@ nv50_graph_construct_mmio(struct nouveau_grctx *ctx) + * without the help of ctxprog. + */ + +-static inline void ++static void + xf_emit(struct nouveau_grctx *ctx, int num, uint32_t val) { + int i; + if (val && ctx->mode == NOUVEAU_GRCTX_VALS) + for (i = 0; i < num; i++) +- nv_wo32(ctx->dev, ctx->data, ctx->ctxvals_pos + (i << 3), val); ++ nv_wo32(ctx->data, 4 * (ctx->ctxvals_pos + (i << 3)), val); + ctx->ctxvals_pos += num << 3; + } + + /* Gene declarations... */ + ++static void nv50_graph_construct_gene_dispatch(struct nouveau_grctx *ctx); + static void nv50_graph_construct_gene_m2mf(struct nouveau_grctx *ctx); +-static void nv50_graph_construct_gene_unk1(struct nouveau_grctx *ctx); +-static void nv50_graph_construct_gene_unk2(struct nouveau_grctx *ctx); +-static void nv50_graph_construct_gene_unk3(struct nouveau_grctx *ctx); +-static void nv50_graph_construct_gene_unk4(struct nouveau_grctx *ctx); +-static void nv50_graph_construct_gene_unk5(struct nouveau_grctx *ctx); +-static void nv50_graph_construct_gene_unk6(struct nouveau_grctx *ctx); +-static void nv50_graph_construct_gene_unk7(struct nouveau_grctx *ctx); +-static void nv50_graph_construct_gene_unk8(struct nouveau_grctx *ctx); +-static void nv50_graph_construct_gene_unk9(struct nouveau_grctx *ctx); +-static void nv50_graph_construct_gene_unk10(struct nouveau_grctx *ctx); ++static void nv50_graph_construct_gene_ccache(struct nouveau_grctx *ctx); ++static void nv50_graph_construct_gene_unk10xx(struct nouveau_grctx *ctx); ++static void nv50_graph_construct_gene_unk14xx(struct nouveau_grctx *ctx); ++static void nv50_graph_construct_gene_zcull(struct nouveau_grctx *ctx); ++static void nv50_graph_construct_gene_clipid(struct nouveau_grctx *ctx); ++static void nv50_graph_construct_gene_unk24xx(struct nouveau_grctx *ctx); ++static void nv50_graph_construct_gene_vfetch(struct nouveau_grctx *ctx); ++static void nv50_graph_construct_gene_eng2d(struct nouveau_grctx *ctx); ++static void nv50_graph_construct_gene_csched(struct nouveau_grctx *ctx); ++static void nv50_graph_construct_gene_unk1cxx(struct nouveau_grctx *ctx); ++static void nv50_graph_construct_gene_strmout(struct nouveau_grctx *ctx); ++static void nv50_graph_construct_gene_unk34xx(struct nouveau_grctx *ctx); ++static void nv50_graph_construct_gene_ropm1(struct nouveau_grctx *ctx); ++static void nv50_graph_construct_gene_ropm2(struct nouveau_grctx *ctx); + static void nv50_graph_construct_gene_ropc(struct nouveau_grctx *ctx); + static void nv50_graph_construct_xfer_tp(struct nouveau_grctx *ctx); + +@@ -1030,102 +1175,32 @@ nv50_graph_construct_xfer1(struct nouveau_grctx *ctx) + if (dev_priv->chipset < 0xa0) { + /* Strand 0 */ + ctx->ctxvals_pos = offset; +- switch (dev_priv->chipset) { +- case 0x50: +- xf_emit(ctx, 0x99, 0); +- break; +- case 0x84: +- case 0x86: +- xf_emit(ctx, 0x384, 0); +- break; +- case 0x92: +- case 0x94: +- case 0x96: +- case 0x98: +- xf_emit(ctx, 0x380, 0); +- break; +- } +- nv50_graph_construct_gene_m2mf (ctx); +- switch (dev_priv->chipset) { +- case 0x50: +- case 0x84: +- case 0x86: +- case 0x98: +- xf_emit(ctx, 0x4c4, 0); +- break; +- case 0x92: +- case 0x94: +- case 0x96: +- xf_emit(ctx, 0x984, 0); +- break; +- } +- nv50_graph_construct_gene_unk5(ctx); +- if (dev_priv->chipset == 0x50) +- xf_emit(ctx, 0xa, 0); +- else +- xf_emit(ctx, 0xb, 0); +- nv50_graph_construct_gene_unk4(ctx); +- nv50_graph_construct_gene_unk3(ctx); ++ nv50_graph_construct_gene_dispatch(ctx); ++ nv50_graph_construct_gene_m2mf(ctx); ++ nv50_graph_construct_gene_unk24xx(ctx); ++ nv50_graph_construct_gene_clipid(ctx); ++ nv50_graph_construct_gene_zcull(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 1 */ + ctx->ctxvals_pos = offset + 0x1; +- nv50_graph_construct_gene_unk6(ctx); +- nv50_graph_construct_gene_unk7(ctx); +- nv50_graph_construct_gene_unk8(ctx); +- switch (dev_priv->chipset) { +- case 0x50: +- case 0x92: +- xf_emit(ctx, 0xfb, 0); +- break; +- case 0x84: +- xf_emit(ctx, 0xd3, 0); +- break; +- case 0x94: +- case 0x96: +- xf_emit(ctx, 0xab, 0); +- break; +- case 0x86: +- case 0x98: +- xf_emit(ctx, 0x6b, 0); +- break; +- } +- xf_emit(ctx, 2, 0x4e3bfdf); +- xf_emit(ctx, 4, 0); +- xf_emit(ctx, 1, 0x0fac6881); +- xf_emit(ctx, 0xb, 0); +- xf_emit(ctx, 2, 0x4e3bfdf); ++ nv50_graph_construct_gene_vfetch(ctx); ++ nv50_graph_construct_gene_eng2d(ctx); ++ nv50_graph_construct_gene_csched(ctx); ++ nv50_graph_construct_gene_ropm1(ctx); ++ nv50_graph_construct_gene_ropm2(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 2 */ + ctx->ctxvals_pos = offset + 0x2; +- switch (dev_priv->chipset) { +- case 0x50: +- case 0x92: +- xf_emit(ctx, 0xa80, 0); +- break; +- case 0x84: +- xf_emit(ctx, 0xa7e, 0); +- break; +- case 0x94: +- case 0x96: +- xf_emit(ctx, 0xa7c, 0); +- break; +- case 0x86: +- case 0x98: +- xf_emit(ctx, 0xa7a, 0); +- break; +- } +- xf_emit(ctx, 1, 0x3fffff); +- xf_emit(ctx, 2, 0); +- xf_emit(ctx, 1, 0x1fff); +- xf_emit(ctx, 0xe, 0); +- nv50_graph_construct_gene_unk9(ctx); +- nv50_graph_construct_gene_unk2(ctx); +- nv50_graph_construct_gene_unk1(ctx); +- nv50_graph_construct_gene_unk10(ctx); ++ nv50_graph_construct_gene_ccache(ctx); ++ nv50_graph_construct_gene_unk1cxx(ctx); ++ nv50_graph_construct_gene_strmout(ctx); ++ nv50_graph_construct_gene_unk14xx(ctx); ++ nv50_graph_construct_gene_unk10xx(ctx); ++ nv50_graph_construct_gene_unk34xx(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + +@@ -1150,86 +1225,46 @@ nv50_graph_construct_xfer1(struct nouveau_grctx *ctx) + } else { + /* Strand 0 */ + ctx->ctxvals_pos = offset; +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) +- xf_emit(ctx, 0x385, 0); +- else +- xf_emit(ctx, 0x384, 0); ++ nv50_graph_construct_gene_dispatch(ctx); + nv50_graph_construct_gene_m2mf(ctx); +- xf_emit(ctx, 0x950, 0); +- nv50_graph_construct_gene_unk10(ctx); +- xf_emit(ctx, 1, 0x0fac6881); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) { +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 3, 0); +- } +- nv50_graph_construct_gene_unk8(ctx); +- if (dev_priv->chipset == 0xa0) +- xf_emit(ctx, 0x189, 0); +- else if (dev_priv->chipset == 0xa3) +- xf_emit(ctx, 0xd5, 0); +- else if (dev_priv->chipset == 0xa5) +- xf_emit(ctx, 0x99, 0); +- else if (dev_priv->chipset == 0xaa) +- xf_emit(ctx, 0x65, 0); +- else +- xf_emit(ctx, 0x6d, 0); +- nv50_graph_construct_gene_unk9(ctx); ++ nv50_graph_construct_gene_unk34xx(ctx); ++ nv50_graph_construct_gene_csched(ctx); ++ nv50_graph_construct_gene_unk1cxx(ctx); ++ nv50_graph_construct_gene_strmout(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 1 */ + ctx->ctxvals_pos = offset + 1; +- nv50_graph_construct_gene_unk1(ctx); ++ nv50_graph_construct_gene_unk10xx(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 2 */ + ctx->ctxvals_pos = offset + 2; +- if (dev_priv->chipset == 0xa0) { +- nv50_graph_construct_gene_unk2(ctx); +- } +- xf_emit(ctx, 0x36, 0); +- nv50_graph_construct_gene_unk5(ctx); ++ if (dev_priv->chipset == 0xa0) ++ nv50_graph_construct_gene_unk14xx(ctx); ++ nv50_graph_construct_gene_unk24xx(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 3 */ + ctx->ctxvals_pos = offset + 3; +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 1); +- nv50_graph_construct_gene_unk6(ctx); ++ nv50_graph_construct_gene_vfetch(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 4 */ + ctx->ctxvals_pos = offset + 4; +- if (dev_priv->chipset == 0xa0) +- xf_emit(ctx, 0xa80, 0); +- else if (dev_priv->chipset == 0xa3) +- xf_emit(ctx, 0xa7c, 0); +- else +- xf_emit(ctx, 0xa7a, 0); +- xf_emit(ctx, 1, 0x3fffff); +- xf_emit(ctx, 2, 0); +- xf_emit(ctx, 1, 0x1fff); ++ nv50_graph_construct_gene_ccache(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + + /* Strand 5 */ + ctx->ctxvals_pos = offset + 5; +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 0x0fac6881); +- xf_emit(ctx, 0xb, 0); +- xf_emit(ctx, 2, 0x4e3bfdf); +- xf_emit(ctx, 3, 0); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) +- xf_emit(ctx, 1, 0x11); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 2, 0x4e3bfdf); +- xf_emit(ctx, 2, 0); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) +- xf_emit(ctx, 1, 0x11); +- xf_emit(ctx, 1, 0); ++ nv50_graph_construct_gene_ropm2(ctx); ++ nv50_graph_construct_gene_ropm1(ctx); ++ /* per-ROP context */ + for (i = 0; i < 8; i++) + if (units & (1<<(i+16))) + nv50_graph_construct_gene_ropc(ctx); +@@ -1238,10 +1273,9 @@ nv50_graph_construct_xfer1(struct nouveau_grctx *ctx) + + /* Strand 6 */ + ctx->ctxvals_pos = offset + 6; +- nv50_graph_construct_gene_unk3(ctx); +- xf_emit(ctx, 0xb, 0); +- nv50_graph_construct_gene_unk4(ctx); +- nv50_graph_construct_gene_unk7(ctx); ++ nv50_graph_construct_gene_zcull(ctx); ++ nv50_graph_construct_gene_clipid(ctx); ++ nv50_graph_construct_gene_eng2d(ctx); + if (units & (1 << 0)) + nv50_graph_construct_xfer_tp(ctx); + if (units & (1 << 1)) +@@ -1269,7 +1303,7 @@ nv50_graph_construct_xfer1(struct nouveau_grctx *ctx) + if (units & (1 << 9)) + nv50_graph_construct_xfer_tp(ctx); + } else { +- nv50_graph_construct_gene_unk2(ctx); ++ nv50_graph_construct_gene_unk14xx(ctx); + } + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; +@@ -1290,9 +1324,70 @@ nv50_graph_construct_xfer1(struct nouveau_grctx *ctx) + */ + + static void ++nv50_graph_construct_gene_dispatch(struct nouveau_grctx *ctx) ++{ ++ /* start of strand 0 */ ++ struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; ++ /* SEEK */ ++ if (dev_priv->chipset == 0x50) ++ xf_emit(ctx, 5, 0); ++ else if (!IS_NVA3F(dev_priv->chipset)) ++ xf_emit(ctx, 6, 0); ++ else ++ xf_emit(ctx, 4, 0); ++ /* SEEK */ ++ /* the PGRAPH's internal FIFO */ ++ if (dev_priv->chipset == 0x50) ++ xf_emit(ctx, 8*3, 0); ++ else ++ xf_emit(ctx, 0x100*3, 0); ++ /* and another bonus slot?!? */ ++ xf_emit(ctx, 3, 0); ++ /* and YET ANOTHER bonus slot? */ ++ if (IS_NVA3F(dev_priv->chipset)) ++ xf_emit(ctx, 3, 0); ++ /* SEEK */ ++ /* CTX_SWITCH: caches of gr objects bound to subchannels. 8 values, last used index */ ++ xf_emit(ctx, 9, 0); ++ /* SEEK */ ++ xf_emit(ctx, 9, 0); ++ /* SEEK */ ++ xf_emit(ctx, 9, 0); ++ /* SEEK */ ++ xf_emit(ctx, 9, 0); ++ /* SEEK */ ++ if (dev_priv->chipset < 0x90) ++ xf_emit(ctx, 4, 0); ++ /* SEEK */ ++ xf_emit(ctx, 2, 0); ++ /* SEEK */ ++ xf_emit(ctx, 6*2, 0); ++ xf_emit(ctx, 2, 0); ++ /* SEEK */ ++ xf_emit(ctx, 2, 0); ++ /* SEEK */ ++ xf_emit(ctx, 6*2, 0); ++ xf_emit(ctx, 2, 0); ++ /* SEEK */ ++ if (dev_priv->chipset == 0x50) ++ xf_emit(ctx, 0x1c, 0); ++ else if (dev_priv->chipset < 0xa0) ++ xf_emit(ctx, 0x1e, 0); ++ else ++ xf_emit(ctx, 0x22, 0); ++ /* SEEK */ ++ xf_emit(ctx, 0x15, 0); ++} ++ ++static void + nv50_graph_construct_gene_m2mf(struct nouveau_grctx *ctx) + { +- /* m2mf state */ ++ /* Strand 0, right after dispatch */ ++ struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; ++ int smallm2mf = 0; ++ if (dev_priv->chipset < 0x92 || dev_priv->chipset == 0x98) ++ smallm2mf = 1; ++ /* SEEK */ + xf_emit (ctx, 1, 0); /* DMA_NOTIFY instance >> 4 */ + xf_emit (ctx, 1, 0); /* DMA_BUFFER_IN instance >> 4 */ + xf_emit (ctx, 1, 0); /* DMA_BUFFER_OUT instance >> 4 */ +@@ -1319,427 +1414,975 @@ nv50_graph_construct_gene_m2mf(struct nouveau_grctx *ctx) + xf_emit (ctx, 1, 0); /* TILING_POSITION_OUT */ + xf_emit (ctx, 1, 0); /* OFFSET_IN_HIGH */ + xf_emit (ctx, 1, 0); /* OFFSET_OUT_HIGH */ ++ /* SEEK */ ++ if (smallm2mf) ++ xf_emit(ctx, 0x40, 0); /* 20 * ffffffff, 3ffff */ ++ else ++ xf_emit(ctx, 0x100, 0); /* 80 * ffffffff, 3ffff */ ++ xf_emit(ctx, 4, 0); /* 1f/7f, 0, 1f/7f, 0 [1f for smallm2mf, 7f otherwise] */ ++ /* SEEK */ ++ if (smallm2mf) ++ xf_emit(ctx, 0x400, 0); /* ffffffff */ ++ else ++ xf_emit(ctx, 0x800, 0); /* ffffffff */ ++ xf_emit(ctx, 4, 0); /* ff/1ff, 0, 0, 0 [ff for smallm2mf, 1ff otherwise] */ ++ /* SEEK */ ++ xf_emit(ctx, 0x40, 0); /* 20 * bits ffffffff, 3ffff */ ++ xf_emit(ctx, 0x6, 0); /* 1f, 0, 1f, 0, 1f, 0 */ + } + + static void +-nv50_graph_construct_gene_unk1(struct nouveau_grctx *ctx) ++nv50_graph_construct_gene_ccache(struct nouveau_grctx *ctx) + { + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; +- /* end of area 2 on pre-NVA0, area 1 on NVAx */ +- xf_emit(ctx, 2, 4); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 0x80); +- xf_emit(ctx, 1, 4); +- xf_emit(ctx, 1, 0x80c14); +- xf_emit(ctx, 1, 0); +- if (dev_priv->chipset == 0x50) +- xf_emit(ctx, 1, 0x3ff); +- else +- xf_emit(ctx, 1, 0x7ff); ++ xf_emit(ctx, 2, 0); /* RO */ ++ xf_emit(ctx, 0x800, 0); /* ffffffff */ + switch (dev_priv->chipset) { + case 0x50: +- case 0x86: +- case 0x98: +- case 0xaa: +- case 0xac: +- xf_emit(ctx, 0x542, 0); ++ case 0x92: ++ case 0xa0: ++ xf_emit(ctx, 0x2b, 0); + break; + case 0x84: +- case 0x92: ++ xf_emit(ctx, 0x29, 0); ++ break; + case 0x94: + case 0x96: +- xf_emit(ctx, 0x942, 0); +- break; +- case 0xa0: + case 0xa3: +- xf_emit(ctx, 0x2042, 0); ++ xf_emit(ctx, 0x27, 0); + break; ++ case 0x86: ++ case 0x98: + case 0xa5: + case 0xa8: +- xf_emit(ctx, 0x842, 0); ++ case 0xaa: ++ case 0xac: ++ case 0xaf: ++ xf_emit(ctx, 0x25, 0); + break; + } +- xf_emit(ctx, 2, 4); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 0x80); +- xf_emit(ctx, 1, 4); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 0x27); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 0x26); +- xf_emit(ctx, 3, 0); ++ /* CB bindings, 0x80 of them. first word is address >> 8, second is ++ * size >> 4 | valid << 24 */ ++ xf_emit(ctx, 0x100, 0); /* ffffffff CB_DEF */ ++ xf_emit(ctx, 1, 0); /* 0000007f CB_ADDR_BUFFER */ ++ xf_emit(ctx, 1, 0); /* 0 */ ++ xf_emit(ctx, 0x30, 0); /* ff SET_PROGRAM_CB */ ++ xf_emit(ctx, 1, 0); /* 3f last SET_PROGRAM_CB */ ++ xf_emit(ctx, 4, 0); /* RO */ ++ xf_emit(ctx, 0x100, 0); /* ffffffff */ ++ xf_emit(ctx, 8, 0); /* 1f, 0, 0, ... */ ++ xf_emit(ctx, 8, 0); /* ffffffff */ ++ xf_emit(ctx, 4, 0); /* ffffffff */ ++ xf_emit(ctx, 1, 0); /* 3 */ ++ xf_emit(ctx, 1, 0); /* ffffffff */ ++ xf_emit(ctx, 1, 0); /* 0000ffff DMA_CODE_CB */ ++ xf_emit(ctx, 1, 0); /* 0000ffff DMA_TIC */ ++ xf_emit(ctx, 1, 0); /* 0000ffff DMA_TSC */ ++ xf_emit(ctx, 1, 0); /* 00000001 LINKED_TSC */ ++ xf_emit(ctx, 1, 0); /* 000000ff TIC_ADDRESS_HIGH */ ++ xf_emit(ctx, 1, 0); /* ffffffff TIC_ADDRESS_LOW */ ++ xf_emit(ctx, 1, 0x3fffff); /* 003fffff TIC_LIMIT */ ++ xf_emit(ctx, 1, 0); /* 000000ff TSC_ADDRESS_HIGH */ ++ xf_emit(ctx, 1, 0); /* ffffffff TSC_ADDRESS_LOW */ ++ xf_emit(ctx, 1, 0x1fff); /* 000fffff TSC_LIMIT */ ++ xf_emit(ctx, 1, 0); /* 000000ff VP_ADDRESS_HIGH */ ++ xf_emit(ctx, 1, 0); /* ffffffff VP_ADDRESS_LOW */ ++ xf_emit(ctx, 1, 0); /* 00ffffff VP_START_ID */ ++ xf_emit(ctx, 1, 0); /* 000000ff CB_DEF_ADDRESS_HIGH */ ++ xf_emit(ctx, 1, 0); /* ffffffff CB_DEF_ADDRESS_LOW */ ++ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 000000ff GP_ADDRESS_HIGH */ ++ xf_emit(ctx, 1, 0); /* ffffffff GP_ADDRESS_LOW */ ++ xf_emit(ctx, 1, 0); /* 00ffffff GP_START_ID */ ++ xf_emit(ctx, 1, 0); /* 000000ff FP_ADDRESS_HIGH */ ++ xf_emit(ctx, 1, 0); /* ffffffff FP_ADDRESS_LOW */ ++ xf_emit(ctx, 1, 0); /* 00ffffff FP_START_ID */ + } + + static void +-nv50_graph_construct_gene_unk10(struct nouveau_grctx *ctx) ++nv50_graph_construct_gene_unk10xx(struct nouveau_grctx *ctx) + { ++ struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; ++ int i; + /* end of area 2 on pre-NVA0, area 1 on NVAx */ +- xf_emit(ctx, 0x10, 0x04000000); +- xf_emit(ctx, 0x24, 0); +- xf_emit(ctx, 2, 0x04e3bfdf); +- xf_emit(ctx, 2, 0); +- xf_emit(ctx, 1, 0x1fe21); ++ xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */ ++ xf_emit(ctx, 1, 4); /* 0000007f VP_RESULT_MAP_SIZE */ ++ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ ++ xf_emit(ctx, 1, 0x80); /* 0000ffff GP_VERTEX_OUTPUT_COUNT */ ++ xf_emit(ctx, 1, 4); /* 000000ff GP_REG_ALLOC_RESULT */ ++ xf_emit(ctx, 1, 0x80c14); /* 01ffffff SEMANTIC_COLOR */ ++ xf_emit(ctx, 1, 0); /* 00000001 VERTEX_TWO_SIDE_ENABLE */ ++ if (dev_priv->chipset == 0x50) ++ xf_emit(ctx, 1, 0x3ff); ++ else ++ xf_emit(ctx, 1, 0x7ff); /* 000007ff */ ++ xf_emit(ctx, 1, 0); /* 111/113 */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ ++ for (i = 0; i < 8; i++) { ++ switch (dev_priv->chipset) { ++ case 0x50: ++ case 0x86: ++ case 0x98: ++ case 0xaa: ++ case 0xac: ++ xf_emit(ctx, 0xa0, 0); /* ffffffff */ ++ break; ++ case 0x84: ++ case 0x92: ++ case 0x94: ++ case 0x96: ++ xf_emit(ctx, 0x120, 0); ++ break; ++ case 0xa5: ++ case 0xa8: ++ xf_emit(ctx, 0x100, 0); /* ffffffff */ ++ break; ++ case 0xa0: ++ case 0xa3: ++ case 0xaf: ++ xf_emit(ctx, 0x400, 0); /* ffffffff */ ++ break; ++ } ++ xf_emit(ctx, 4, 0); /* 3f, 0, 0, 0 */ ++ xf_emit(ctx, 4, 0); /* ffffffff */ ++ } ++ xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */ ++ xf_emit(ctx, 1, 4); /* 0000007f VP_RESULT_MAP_SIZE */ ++ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ ++ xf_emit(ctx, 1, 0x80); /* 0000ffff GP_VERTEX_OUTPUT_COUNT */ ++ xf_emit(ctx, 1, 4); /* 000000ff GP_REG_ALLOC_TEMP */ ++ xf_emit(ctx, 1, 1); /* 00000001 RASTERIZE_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1900 */ ++ xf_emit(ctx, 1, 0x27); /* 000000ff UNK0FD4 */ ++ xf_emit(ctx, 1, 0); /* 0001ffff GP_BUILTIN_RESULT_EN */ ++ xf_emit(ctx, 1, 0x26); /* 000000ff SEMANTIC_LAYER */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ ++} ++ ++static void ++nv50_graph_construct_gene_unk34xx(struct nouveau_grctx *ctx) ++{ ++ struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; ++ /* end of area 2 on pre-NVA0, area 1 on NVAx */ ++ xf_emit(ctx, 1, 0); /* 00000001 VIEWPORT_CLIP_RECTS_EN */ ++ xf_emit(ctx, 1, 0); /* 00000003 VIEWPORT_CLIP_MODE */ ++ xf_emit(ctx, 0x10, 0x04000000); /* 07ffffff VIEWPORT_CLIP_HORIZ*8, VIEWPORT_CLIP_VERT*8 */ ++ xf_emit(ctx, 1, 0); /* 00000001 POLYGON_STIPPLE_ENABLE */ ++ xf_emit(ctx, 0x20, 0); /* ffffffff POLYGON_STIPPLE */ ++ xf_emit(ctx, 2, 0); /* 00007fff WINDOW_OFFSET_XY */ ++ xf_emit(ctx, 1, 0); /* ffff0ff3 */ ++ xf_emit(ctx, 1, 0x04e3bfdf); /* ffffffff UNK0D64 */ ++ xf_emit(ctx, 1, 0x04e3bfdf); /* ffffffff UNK0DF4 */ ++ xf_emit(ctx, 1, 0); /* 00000003 WINDOW_ORIGIN */ ++ xf_emit(ctx, 1, 0); /* 00000007 */ ++ xf_emit(ctx, 1, 0x1fe21); /* 0001ffff tesla UNK0FAC */ ++ if (dev_priv->chipset >= 0xa0) ++ xf_emit(ctx, 1, 0x0fac6881); ++ if (IS_NVA3F(dev_priv->chipset)) { ++ xf_emit(ctx, 1, 1); ++ xf_emit(ctx, 3, 0); ++ } + } + + static void +-nv50_graph_construct_gene_unk2(struct nouveau_grctx *ctx) ++nv50_graph_construct_gene_unk14xx(struct nouveau_grctx *ctx) + { + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; + /* middle of area 2 on pre-NVA0, beginning of area 2 on NVA0, area 7 on >NVA0 */ + if (dev_priv->chipset != 0x50) { +- xf_emit(ctx, 5, 0); +- xf_emit(ctx, 1, 0x80c14); +- xf_emit(ctx, 2, 0); +- xf_emit(ctx, 1, 0x804); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 2, 4); +- xf_emit(ctx, 1, 0x8100c12); ++ xf_emit(ctx, 5, 0); /* ffffffff */ ++ xf_emit(ctx, 1, 0x80c14); /* 01ffffff SEMANTIC_COLOR */ ++ xf_emit(ctx, 1, 0); /* 00000001 */ ++ xf_emit(ctx, 1, 0); /* 000003ff */ ++ xf_emit(ctx, 1, 0x804); /* 00000fff SEMANTIC_CLIP */ ++ xf_emit(ctx, 1, 0); /* 00000001 */ ++ xf_emit(ctx, 2, 4); /* 7f, ff */ ++ xf_emit(ctx, 1, 0x8100c12); /* 1fffffff FP_INTERPOLANT_CTRL */ + } +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 2, 4); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 0x10); +- if (dev_priv->chipset == 0x50) +- xf_emit(ctx, 3, 0); +- else +- xf_emit(ctx, 4, 0); +- xf_emit(ctx, 1, 0x804); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 0x1a); ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ ++ xf_emit(ctx, 1, 4); /* 0000007f VP_RESULT_MAP_SIZE */ ++ xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */ ++ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ ++ xf_emit(ctx, 1, 0x10); /* 7f/ff VIEW_VOLUME_CLIP_CTRL */ ++ xf_emit(ctx, 1, 0); /* 000000ff VP_CLIP_DISTANCE_ENABLE */ + if (dev_priv->chipset != 0x50) +- xf_emit(ctx, 1, 0x7f); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 0x80c14); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 0x8100c12); +- xf_emit(ctx, 2, 4); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 0x10); +- xf_emit(ctx, 3, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 0x8100c12); +- xf_emit(ctx, 6, 0); +- if (dev_priv->chipset == 0x50) +- xf_emit(ctx, 1, 0x3ff); +- else +- xf_emit(ctx, 1, 0x7ff); +- xf_emit(ctx, 1, 0x80c14); +- xf_emit(ctx, 0x38, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 2, 0); +- xf_emit(ctx, 1, 0x10); +- xf_emit(ctx, 0x38, 0); +- xf_emit(ctx, 2, 0x88); +- xf_emit(ctx, 2, 0); +- xf_emit(ctx, 1, 4); +- xf_emit(ctx, 0x16, 0); +- xf_emit(ctx, 1, 0x26); +- xf_emit(ctx, 2, 0); +- xf_emit(ctx, 1, 0x3f800000); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) +- xf_emit(ctx, 4, 0); +- else +- xf_emit(ctx, 3, 0); +- xf_emit(ctx, 1, 0x1a); +- xf_emit(ctx, 1, 0x10); ++ xf_emit(ctx, 1, 0); /* 3ff */ ++ xf_emit(ctx, 1, 0); /* 000000ff tesla UNK1940 */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK0D7C */ ++ xf_emit(ctx, 1, 0x804); /* 00000fff SEMANTIC_CLIP */ ++ xf_emit(ctx, 1, 1); /* 00000001 VIEWPORT_TRANSFORM_EN */ ++ xf_emit(ctx, 1, 0x1a); /* 0000001f POLYGON_MODE */ + if (dev_priv->chipset != 0x50) +- xf_emit(ctx, 0x28, 0); ++ xf_emit(ctx, 1, 0x7f); /* 000000ff tesla UNK0FFC */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ ++ xf_emit(ctx, 1, 1); /* 00000001 SHADE_MODEL */ ++ xf_emit(ctx, 1, 0x80c14); /* 01ffffff SEMANTIC_COLOR */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1900 */ ++ xf_emit(ctx, 1, 0x8100c12); /* 1fffffff FP_INTERPOLANT_CTRL */ ++ xf_emit(ctx, 1, 4); /* 0000007f VP_RESULT_MAP_SIZE */ ++ xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */ ++ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ ++ xf_emit(ctx, 1, 0x10); /* 7f/ff VIEW_VOLUME_CLIP_CTRL */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK0D7C */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK0F8C */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ ++ xf_emit(ctx, 1, 1); /* 00000001 VIEWPORT_TRANSFORM_EN */ ++ xf_emit(ctx, 1, 0x8100c12); /* 1fffffff FP_INTERPOLANT_CTRL */ ++ xf_emit(ctx, 4, 0); /* ffffffff NOPERSPECTIVE_BITMAP */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1900 */ ++ xf_emit(ctx, 1, 0); /* 0000000f */ ++ if (dev_priv->chipset == 0x50) ++ xf_emit(ctx, 1, 0x3ff); /* 000003ff tesla UNK0D68 */ + else +- xf_emit(ctx, 0x25, 0); +- xf_emit(ctx, 1, 0x52); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 0x26); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 2, 4); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 0x1a); +- xf_emit(ctx, 2, 0); +- xf_emit(ctx, 1, 0x00ffff00); +- xf_emit(ctx, 1, 0); ++ xf_emit(ctx, 1, 0x7ff); /* 000007ff tesla UNK0D68 */ ++ xf_emit(ctx, 1, 0x80c14); /* 01ffffff SEMANTIC_COLOR */ ++ xf_emit(ctx, 1, 0); /* 00000001 VERTEX_TWO_SIDE_ENABLE */ ++ xf_emit(ctx, 0x30, 0); /* ffffffff VIEWPORT_SCALE: X0, Y0, Z0, X1, Y1, ... */ ++ xf_emit(ctx, 3, 0); /* f, 0, 0 */ ++ xf_emit(ctx, 3, 0); /* ffffffff last VIEWPORT_SCALE? */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ ++ xf_emit(ctx, 1, 1); /* 00000001 VIEWPORT_TRANSFORM_EN */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1900 */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1924 */ ++ xf_emit(ctx, 1, 0x10); /* 000000ff VIEW_VOLUME_CLIP_CTRL */ ++ xf_emit(ctx, 1, 0); /* 00000001 */ ++ xf_emit(ctx, 0x30, 0); /* ffffffff VIEWPORT_TRANSLATE */ ++ xf_emit(ctx, 3, 0); /* f, 0, 0 */ ++ xf_emit(ctx, 3, 0); /* ffffffff */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ ++ xf_emit(ctx, 2, 0x88); /* 000001ff tesla UNK19D8 */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1924 */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ ++ xf_emit(ctx, 1, 4); /* 0000000f CULL_MODE */ ++ xf_emit(ctx, 2, 0); /* 07ffffff SCREEN_SCISSOR */ ++ xf_emit(ctx, 2, 0); /* 00007fff WINDOW_OFFSET_XY */ ++ xf_emit(ctx, 1, 0); /* 00000003 WINDOW_ORIGIN */ ++ xf_emit(ctx, 0x10, 0); /* 00000001 SCISSOR_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 0001ffff GP_BUILTIN_RESULT_EN */ ++ xf_emit(ctx, 1, 0x26); /* 000000ff SEMANTIC_LAYER */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1900 */ ++ xf_emit(ctx, 1, 0); /* 0000000f */ ++ xf_emit(ctx, 1, 0x3f800000); /* ffffffff LINE_WIDTH */ ++ xf_emit(ctx, 1, 0); /* 00000001 LINE_STIPPLE_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000001 LINE_SMOOTH_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000007 MULTISAMPLE_SAMPLES_LOG2 */ ++ if (IS_NVA3F(dev_priv->chipset)) ++ xf_emit(ctx, 1, 0); /* 00000001 */ ++ xf_emit(ctx, 1, 0x1a); /* 0000001f POLYGON_MODE */ ++ xf_emit(ctx, 1, 0x10); /* 000000ff VIEW_VOLUME_CLIP_CTRL */ ++ if (dev_priv->chipset != 0x50) { ++ xf_emit(ctx, 1, 0); /* ffffffff */ ++ xf_emit(ctx, 1, 0); /* 00000001 */ ++ xf_emit(ctx, 1, 0); /* 000003ff */ ++ } ++ xf_emit(ctx, 0x20, 0); /* 10xbits ffffffff, 3fffff. SCISSOR_* */ ++ xf_emit(ctx, 1, 0); /* f */ ++ xf_emit(ctx, 1, 0); /* 0? */ ++ xf_emit(ctx, 1, 0); /* ffffffff */ ++ xf_emit(ctx, 1, 0); /* 003fffff */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ ++ xf_emit(ctx, 1, 0x52); /* 000001ff SEMANTIC_PTSZ */ ++ xf_emit(ctx, 1, 0); /* 0001ffff GP_BUILTIN_RESULT_EN */ ++ xf_emit(ctx, 1, 0x26); /* 000000ff SEMANTIC_LAYER */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1900 */ ++ xf_emit(ctx, 1, 4); /* 0000007f VP_RESULT_MAP_SIZE */ ++ xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */ ++ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ ++ xf_emit(ctx, 1, 0x1a); /* 0000001f POLYGON_MODE */ ++ xf_emit(ctx, 1, 0); /* 00000001 LINE_SMOOTH_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000001 LINE_STIPPLE_ENABLE */ ++ xf_emit(ctx, 1, 0x00ffff00); /* 00ffffff LINE_STIPPLE_PATTERN */ ++ xf_emit(ctx, 1, 0); /* 0000000f */ + } + + static void +-nv50_graph_construct_gene_unk3(struct nouveau_grctx *ctx) ++nv50_graph_construct_gene_zcull(struct nouveau_grctx *ctx) + { + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; +- /* end of area 0 on pre-NVA0, beginning of area 6 on NVAx */ +- xf_emit(ctx, 1, 0x3f); +- xf_emit(ctx, 0xa, 0); +- xf_emit(ctx, 1, 2); +- xf_emit(ctx, 2, 0x04000000); +- xf_emit(ctx, 8, 0); +- xf_emit(ctx, 1, 4); +- xf_emit(ctx, 3, 0); +- xf_emit(ctx, 1, 4); +- if (dev_priv->chipset == 0x50) +- xf_emit(ctx, 0x10, 0); +- else +- xf_emit(ctx, 0x11, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 0x1001); +- xf_emit(ctx, 4, 0xffff); +- xf_emit(ctx, 0x20, 0); +- xf_emit(ctx, 0x10, 0x3f800000); +- xf_emit(ctx, 1, 0x10); +- if (dev_priv->chipset == 0x50) +- xf_emit(ctx, 1, 0); +- else +- xf_emit(ctx, 2, 0); +- xf_emit(ctx, 1, 3); +- xf_emit(ctx, 2, 0); ++ /* end of strand 0 on pre-NVA0, beginning of strand 6 on NVAx */ ++ /* SEEK */ ++ xf_emit(ctx, 1, 0x3f); /* 0000003f UNK1590 */ ++ xf_emit(ctx, 1, 0); /* 00000001 ALPHA_TEST_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000007 MULTISAMPLE_SAMPLES_LOG2 */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */ ++ xf_emit(ctx, 1, 0); /* 00000007 STENCIL_BACK_FUNC_FUNC */ ++ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_FUNC_MASK */ ++ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_FUNC_REF */ ++ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_MASK */ ++ xf_emit(ctx, 3, 0); /* 00000007 STENCIL_BACK_OP_FAIL, ZFAIL, ZPASS */ ++ xf_emit(ctx, 1, 2); /* 00000003 tesla UNK143C */ ++ xf_emit(ctx, 2, 0x04000000); /* 07ffffff tesla UNK0D6C */ ++ xf_emit(ctx, 1, 0); /* ffff0ff3 */ ++ xf_emit(ctx, 1, 0); /* 00000001 CLIPID_ENABLE */ ++ xf_emit(ctx, 2, 0); /* ffffffff DEPTH_BOUNDS */ ++ xf_emit(ctx, 1, 0); /* 00000001 */ ++ xf_emit(ctx, 1, 0); /* 00000007 DEPTH_TEST_FUNC */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE_ENABLE */ ++ xf_emit(ctx, 1, 4); /* 0000000f CULL_MODE */ ++ xf_emit(ctx, 1, 0); /* 0000ffff */ ++ xf_emit(ctx, 1, 0); /* 00000001 UNK0FB0 */ ++ xf_emit(ctx, 1, 0); /* 00000001 POLYGON_STIPPLE_ENABLE */ ++ xf_emit(ctx, 1, 4); /* 00000007 FP_CONTROL */ ++ xf_emit(ctx, 1, 0); /* ffffffff */ ++ xf_emit(ctx, 1, 0); /* 0001ffff GP_BUILTIN_RESULT_EN */ ++ xf_emit(ctx, 1, 0); /* 000000ff CLEAR_STENCIL */ ++ xf_emit(ctx, 1, 0); /* 00000007 STENCIL_FRONT_FUNC_FUNC */ ++ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_FUNC_MASK */ ++ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_FUNC_REF */ ++ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_MASK */ ++ xf_emit(ctx, 3, 0); /* 00000007 STENCIL_FRONT_OP_FAIL, ZFAIL, ZPASS */ ++ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_BACK_ENABLE */ ++ xf_emit(ctx, 1, 0); /* ffffffff CLEAR_DEPTH */ ++ xf_emit(ctx, 1, 0); /* 00000007 */ ++ if (dev_priv->chipset != 0x50) ++ xf_emit(ctx, 1, 0); /* 00000003 tesla UNK1108 */ ++ xf_emit(ctx, 1, 0); /* 00000001 SAMPLECNT_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */ ++ xf_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */ ++ xf_emit(ctx, 1, 0x1001); /* 00001fff ZETA_ARRAY_MODE */ ++ /* SEEK */ ++ xf_emit(ctx, 4, 0xffff); /* 0000ffff MSAA_MASK */ ++ xf_emit(ctx, 0x10, 0); /* 00000001 SCISSOR_ENABLE */ ++ xf_emit(ctx, 0x10, 0); /* ffffffff DEPTH_RANGE_NEAR */ ++ xf_emit(ctx, 0x10, 0x3f800000); /* ffffffff DEPTH_RANGE_FAR */ ++ xf_emit(ctx, 1, 0x10); /* 7f/ff/3ff VIEW_VOLUME_CLIP_CTRL */ ++ xf_emit(ctx, 1, 0); /* 00000001 VIEWPORT_CLIP_RECTS_EN */ ++ xf_emit(ctx, 1, 3); /* 00000003 FP_CTRL_UNK196C */ ++ xf_emit(ctx, 1, 0); /* 00000003 tesla UNK1968 */ ++ if (dev_priv->chipset != 0x50) ++ xf_emit(ctx, 1, 0); /* 0fffffff tesla UNK1104 */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK151C */ + } + + static void +-nv50_graph_construct_gene_unk4(struct nouveau_grctx *ctx) ++nv50_graph_construct_gene_clipid(struct nouveau_grctx *ctx) + { +- /* middle of area 0 on pre-NVA0, middle of area 6 on NVAx */ +- xf_emit(ctx, 2, 0x04000000); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 0x80); +- xf_emit(ctx, 3, 0); +- xf_emit(ctx, 1, 0x80); +- xf_emit(ctx, 1, 0); ++ /* middle of strand 0 on pre-NVA0 [after 24xx], middle of area 6 on NVAx */ ++ /* SEEK */ ++ xf_emit(ctx, 1, 0); /* 00000007 UNK0FB4 */ ++ /* SEEK */ ++ xf_emit(ctx, 4, 0); /* 07ffffff CLIPID_REGION_HORIZ */ ++ xf_emit(ctx, 4, 0); /* 07ffffff CLIPID_REGION_VERT */ ++ xf_emit(ctx, 2, 0); /* 07ffffff SCREEN_SCISSOR */ ++ xf_emit(ctx, 2, 0x04000000); /* 07ffffff UNK1508 */ ++ xf_emit(ctx, 1, 0); /* 00000001 CLIPID_ENABLE */ ++ xf_emit(ctx, 1, 0x80); /* 00003fff CLIPID_WIDTH */ ++ xf_emit(ctx, 1, 0); /* 000000ff CLIPID_ID */ ++ xf_emit(ctx, 1, 0); /* 000000ff CLIPID_ADDRESS_HIGH */ ++ xf_emit(ctx, 1, 0); /* ffffffff CLIPID_ADDRESS_LOW */ ++ xf_emit(ctx, 1, 0x80); /* 00003fff CLIPID_HEIGHT */ ++ xf_emit(ctx, 1, 0); /* 0000ffff DMA_CLIPID */ + } + + static void +-nv50_graph_construct_gene_unk5(struct nouveau_grctx *ctx) ++nv50_graph_construct_gene_unk24xx(struct nouveau_grctx *ctx) + { + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; +- /* middle of area 0 on pre-NVA0 [after m2mf], end of area 2 on NVAx */ +- xf_emit(ctx, 2, 4); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) +- xf_emit(ctx, 0x1c4d, 0); ++ int i; ++ /* middle of strand 0 on pre-NVA0 [after m2mf], end of strand 2 on NVAx */ ++ /* SEEK */ ++ xf_emit(ctx, 0x33, 0); ++ /* SEEK */ ++ xf_emit(ctx, 2, 0); ++ /* SEEK */ ++ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ ++ xf_emit(ctx, 1, 4); /* 0000007f VP_RESULT_MAP_SIZE */ ++ xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */ ++ /* SEEK */ ++ if (IS_NVA3F(dev_priv->chipset)) { ++ xf_emit(ctx, 4, 0); /* RO */ ++ xf_emit(ctx, 0xe10, 0); /* 190 * 9: 8*ffffffff, 7ff */ ++ xf_emit(ctx, 1, 0); /* 1ff */ ++ xf_emit(ctx, 8, 0); /* 0? */ ++ xf_emit(ctx, 9, 0); /* ffffffff, 7ff */ ++ ++ xf_emit(ctx, 4, 0); /* RO */ ++ xf_emit(ctx, 0xe10, 0); /* 190 * 9: 8*ffffffff, 7ff */ ++ xf_emit(ctx, 1, 0); /* 1ff */ ++ xf_emit(ctx, 8, 0); /* 0? */ ++ xf_emit(ctx, 9, 0); /* ffffffff, 7ff */ ++ } + else +- xf_emit(ctx, 0x1c4b, 0); +- xf_emit(ctx, 2, 4); +- xf_emit(ctx, 1, 0x8100c12); ++ { ++ xf_emit(ctx, 0xc, 0); /* RO */ ++ /* SEEK */ ++ xf_emit(ctx, 0xe10, 0); /* 190 * 9: 8*ffffffff, 7ff */ ++ xf_emit(ctx, 1, 0); /* 1ff */ ++ xf_emit(ctx, 8, 0); /* 0? */ ++ ++ /* SEEK */ ++ xf_emit(ctx, 0xc, 0); /* RO */ ++ /* SEEK */ ++ xf_emit(ctx, 0xe10, 0); /* 190 * 9: 8*ffffffff, 7ff */ ++ xf_emit(ctx, 1, 0); /* 1ff */ ++ xf_emit(ctx, 8, 0); /* 0? */ ++ } ++ /* SEEK */ ++ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ ++ xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */ ++ xf_emit(ctx, 1, 4); /* 0000007f VP_RESULT_MAP_SIZE */ ++ xf_emit(ctx, 1, 0x8100c12); /* 1fffffff FP_INTERPOLANT_CTRL */ + if (dev_priv->chipset != 0x50) +- xf_emit(ctx, 1, 3); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 0x8100c12); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 0x80c14); +- xf_emit(ctx, 1, 1); ++ xf_emit(ctx, 1, 3); /* 00000003 tesla UNK1100 */ ++ /* SEEK */ ++ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ ++ xf_emit(ctx, 1, 0x8100c12); /* 1fffffff FP_INTERPOLANT_CTRL */ ++ xf_emit(ctx, 1, 0); /* 0000000f VP_GP_BUILTIN_ATTR_EN */ ++ xf_emit(ctx, 1, 0x80c14); /* 01ffffff SEMANTIC_COLOR */ ++ xf_emit(ctx, 1, 1); /* 00000001 */ ++ /* SEEK */ + if (dev_priv->chipset >= 0xa0) +- xf_emit(ctx, 2, 4); +- xf_emit(ctx, 1, 0x80c14); +- xf_emit(ctx, 2, 0); +- xf_emit(ctx, 1, 0x8100c12); +- xf_emit(ctx, 1, 0x27); +- xf_emit(ctx, 2, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 0x3c1, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 0x16, 0); +- xf_emit(ctx, 1, 0x8100c12); +- xf_emit(ctx, 1, 0); ++ xf_emit(ctx, 2, 4); /* 000000ff */ ++ xf_emit(ctx, 1, 0x80c14); /* 01ffffff SEMANTIC_COLOR */ ++ xf_emit(ctx, 1, 0); /* 00000001 VERTEX_TWO_SIDE_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000001 POINT_SPRITE_ENABLE */ ++ xf_emit(ctx, 1, 0x8100c12); /* 1fffffff FP_INTERPOLANT_CTRL */ ++ xf_emit(ctx, 1, 0x27); /* 000000ff SEMANTIC_PRIM_ID */ ++ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 0000000f */ ++ xf_emit(ctx, 1, 1); /* 00000001 */ ++ for (i = 0; i < 10; i++) { ++ /* SEEK */ ++ xf_emit(ctx, 0x40, 0); /* ffffffff */ ++ xf_emit(ctx, 0x10, 0); /* 3, 0, 0.... */ ++ xf_emit(ctx, 0x10, 0); /* ffffffff */ ++ } ++ /* SEEK */ ++ xf_emit(ctx, 1, 0); /* 00000001 POINT_SPRITE_CTRL */ ++ xf_emit(ctx, 1, 1); /* 00000001 */ ++ xf_emit(ctx, 1, 0); /* ffffffff */ ++ xf_emit(ctx, 4, 0); /* ffffffff NOPERSPECTIVE_BITMAP */ ++ xf_emit(ctx, 0x10, 0); /* 00ffffff POINT_COORD_REPLACE_MAP */ ++ xf_emit(ctx, 1, 0); /* 00000003 WINDOW_ORIGIN */ ++ xf_emit(ctx, 1, 0x8100c12); /* 1fffffff FP_INTERPOLANT_CTRL */ ++ if (dev_priv->chipset != 0x50) ++ xf_emit(ctx, 1, 0); /* 000003ff */ + } + + static void +-nv50_graph_construct_gene_unk6(struct nouveau_grctx *ctx) ++nv50_graph_construct_gene_vfetch(struct nouveau_grctx *ctx) + { + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; +- /* beginning of area 1 on pre-NVA0 [after m2mf], area 3 on NVAx */ +- xf_emit(ctx, 4, 0); +- xf_emit(ctx, 1, 0xf); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) +- xf_emit(ctx, 8, 0); +- else +- xf_emit(ctx, 4, 0); +- xf_emit(ctx, 1, 0x20); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) +- xf_emit(ctx, 0x11, 0); ++ int acnt = 0x10, rep, i; ++ /* beginning of strand 1 on pre-NVA0, strand 3 on NVAx */ ++ if (IS_NVA3F(dev_priv->chipset)) ++ acnt = 0x20; ++ /* SEEK */ ++ if (dev_priv->chipset >= 0xa0) { ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK13A4 */ ++ xf_emit(ctx, 1, 1); /* 00000fff tesla UNK1318 */ ++ } ++ xf_emit(ctx, 1, 0); /* ffffffff VERTEX_BUFFER_FIRST */ ++ xf_emit(ctx, 1, 0); /* 00000001 PRIMITIVE_RESTART_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000001 UNK0DE8 */ ++ xf_emit(ctx, 1, 0); /* ffffffff PRIMITIVE_RESTART_INDEX */ ++ xf_emit(ctx, 1, 0xf); /* ffffffff VP_ATTR_EN */ ++ xf_emit(ctx, (acnt/8)-1, 0); /* ffffffff VP_ATTR_EN */ ++ xf_emit(ctx, acnt/8, 0); /* ffffffff VTX_ATR_MASK_UNK0DD0 */ ++ xf_emit(ctx, 1, 0); /* 0000000f VP_GP_BUILTIN_ATTR_EN */ ++ xf_emit(ctx, 1, 0x20); /* 0000ffff tesla UNK129C */ ++ xf_emit(ctx, 1, 0); /* 000000ff turing UNK370??? */ ++ xf_emit(ctx, 1, 0); /* 0000ffff turing USER_PARAM_COUNT */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ ++ /* SEEK */ ++ if (IS_NVA3F(dev_priv->chipset)) ++ xf_emit(ctx, 0xb, 0); /* RO */ + else if (dev_priv->chipset >= 0xa0) +- xf_emit(ctx, 0xf, 0); ++ xf_emit(ctx, 0x9, 0); /* RO */ + else +- xf_emit(ctx, 0xe, 0); +- xf_emit(ctx, 1, 0x1a); +- xf_emit(ctx, 0xd, 0); +- xf_emit(ctx, 2, 4); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 4); +- xf_emit(ctx, 1, 8); +- xf_emit(ctx, 1, 0); ++ xf_emit(ctx, 0x8, 0); /* RO */ ++ /* SEEK */ ++ xf_emit(ctx, 1, 0); /* 00000001 EDGE_FLAG */ ++ xf_emit(ctx, 1, 0); /* 00000001 PROVOKING_VERTEX_LAST */ ++ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ ++ xf_emit(ctx, 1, 0x1a); /* 0000001f POLYGON_MODE */ ++ /* SEEK */ ++ xf_emit(ctx, 0xc, 0); /* RO */ ++ /* SEEK */ ++ xf_emit(ctx, 1, 0); /* 7f/ff */ ++ xf_emit(ctx, 1, 4); /* 7f/ff VP_REG_ALLOC_RESULT */ ++ xf_emit(ctx, 1, 4); /* 7f/ff VP_RESULT_MAP_SIZE */ ++ xf_emit(ctx, 1, 0); /* 0000000f VP_GP_BUILTIN_ATTR_EN */ ++ xf_emit(ctx, 1, 4); /* 000001ff UNK1A28 */ ++ xf_emit(ctx, 1, 8); /* 000001ff UNK0DF0 */ ++ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ + if (dev_priv->chipset == 0x50) +- xf_emit(ctx, 1, 0x3ff); ++ xf_emit(ctx, 1, 0x3ff); /* 3ff tesla UNK0D68 */ + else +- xf_emit(ctx, 1, 0x7ff); ++ xf_emit(ctx, 1, 0x7ff); /* 7ff tesla UNK0D68 */ + if (dev_priv->chipset == 0xa8) +- xf_emit(ctx, 1, 0x1e00); +- xf_emit(ctx, 0xc, 0); +- xf_emit(ctx, 1, 0xf); +- if (dev_priv->chipset == 0x50) +- xf_emit(ctx, 0x125, 0); +- else if (dev_priv->chipset < 0xa0) +- xf_emit(ctx, 0x126, 0); +- else if (dev_priv->chipset == 0xa0 || dev_priv->chipset >= 0xaa) +- xf_emit(ctx, 0x124, 0); ++ xf_emit(ctx, 1, 0x1e00); /* 7fff */ ++ /* SEEK */ ++ xf_emit(ctx, 0xc, 0); /* RO or close */ ++ /* SEEK */ ++ xf_emit(ctx, 1, 0xf); /* ffffffff VP_ATTR_EN */ ++ xf_emit(ctx, (acnt/8)-1, 0); /* ffffffff VP_ATTR_EN */ ++ xf_emit(ctx, 1, 0); /* 0000000f VP_GP_BUILTIN_ATTR_EN */ ++ if (dev_priv->chipset > 0x50 && dev_priv->chipset < 0xa0) ++ xf_emit(ctx, 2, 0); /* ffffffff */ + else +- xf_emit(ctx, 0x1f7, 0); +- xf_emit(ctx, 1, 0xf); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) +- xf_emit(ctx, 3, 0); ++ xf_emit(ctx, 1, 0); /* ffffffff */ ++ xf_emit(ctx, 1, 0); /* 00000003 tesla UNK0FD8 */ ++ /* SEEK */ ++ if (IS_NVA3F(dev_priv->chipset)) { ++ xf_emit(ctx, 0x10, 0); /* 0? */ ++ xf_emit(ctx, 2, 0); /* weird... */ ++ xf_emit(ctx, 2, 0); /* RO */ ++ } else { ++ xf_emit(ctx, 8, 0); /* 0? */ ++ xf_emit(ctx, 1, 0); /* weird... */ ++ xf_emit(ctx, 2, 0); /* RO */ ++ } ++ /* SEEK */ ++ xf_emit(ctx, 1, 0); /* ffffffff VB_ELEMENT_BASE */ ++ xf_emit(ctx, 1, 0); /* ffffffff UNK1438 */ ++ xf_emit(ctx, acnt, 0); /* 1 tesla UNK1000 */ ++ if (dev_priv->chipset >= 0xa0) ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1118? */ ++ /* SEEK */ ++ xf_emit(ctx, acnt, 0); /* ffffffff VERTEX_ARRAY_UNK90C */ ++ xf_emit(ctx, 1, 0); /* f/1f */ ++ /* SEEK */ ++ xf_emit(ctx, acnt, 0); /* ffffffff VERTEX_ARRAY_UNK90C */ ++ xf_emit(ctx, 1, 0); /* f/1f */ ++ /* SEEK */ ++ xf_emit(ctx, acnt, 0); /* RO */ ++ xf_emit(ctx, 2, 0); /* RO */ ++ /* SEEK */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK111C? */ ++ xf_emit(ctx, 1, 0); /* RO */ ++ /* SEEK */ ++ xf_emit(ctx, 1, 0); /* 000000ff UNK15F4_ADDRESS_HIGH */ ++ xf_emit(ctx, 1, 0); /* ffffffff UNK15F4_ADDRESS_LOW */ ++ xf_emit(ctx, 1, 0); /* 000000ff UNK0F84_ADDRESS_HIGH */ ++ xf_emit(ctx, 1, 0); /* ffffffff UNK0F84_ADDRESS_LOW */ ++ /* SEEK */ ++ xf_emit(ctx, acnt, 0); /* 00003fff VERTEX_ARRAY_ATTRIB_OFFSET */ ++ xf_emit(ctx, 3, 0); /* f/1f */ ++ /* SEEK */ ++ xf_emit(ctx, acnt, 0); /* 00000fff VERTEX_ARRAY_STRIDE */ ++ xf_emit(ctx, 3, 0); /* f/1f */ ++ /* SEEK */ ++ xf_emit(ctx, acnt, 0); /* ffffffff VERTEX_ARRAY_LOW */ ++ xf_emit(ctx, 3, 0); /* f/1f */ ++ /* SEEK */ ++ xf_emit(ctx, acnt, 0); /* 000000ff VERTEX_ARRAY_HIGH */ ++ xf_emit(ctx, 3, 0); /* f/1f */ ++ /* SEEK */ ++ xf_emit(ctx, acnt, 0); /* ffffffff VERTEX_LIMIT_LOW */ ++ xf_emit(ctx, 3, 0); /* f/1f */ ++ /* SEEK */ ++ xf_emit(ctx, acnt, 0); /* 000000ff VERTEX_LIMIT_HIGH */ ++ xf_emit(ctx, 3, 0); /* f/1f */ ++ /* SEEK */ ++ if (IS_NVA3F(dev_priv->chipset)) { ++ xf_emit(ctx, acnt, 0); /* f */ ++ xf_emit(ctx, 3, 0); /* f/1f */ ++ } ++ /* SEEK */ ++ if (IS_NVA3F(dev_priv->chipset)) ++ xf_emit(ctx, 2, 0); /* RO */ ++ else ++ xf_emit(ctx, 5, 0); /* RO */ ++ /* SEEK */ ++ xf_emit(ctx, 1, 0); /* ffff DMA_VTXBUF */ ++ /* SEEK */ ++ if (dev_priv->chipset < 0xa0) { ++ xf_emit(ctx, 0x41, 0); /* RO */ ++ /* SEEK */ ++ xf_emit(ctx, 0x11, 0); /* RO */ ++ } else if (!IS_NVA3F(dev_priv->chipset)) ++ xf_emit(ctx, 0x50, 0); /* RO */ + else +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 1); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) +- xf_emit(ctx, 0xa1, 0); ++ xf_emit(ctx, 0x58, 0); /* RO */ ++ /* SEEK */ ++ xf_emit(ctx, 1, 0xf); /* ffffffff VP_ATTR_EN */ ++ xf_emit(ctx, (acnt/8)-1, 0); /* ffffffff VP_ATTR_EN */ ++ xf_emit(ctx, 1, 1); /* 1 UNK0DEC */ ++ /* SEEK */ ++ xf_emit(ctx, acnt*4, 0); /* ffffffff VTX_ATTR */ ++ xf_emit(ctx, 4, 0); /* f/1f, 0, 0, 0 */ ++ /* SEEK */ ++ if (IS_NVA3F(dev_priv->chipset)) ++ xf_emit(ctx, 0x1d, 0); /* RO */ + else +- xf_emit(ctx, 0x5a, 0); +- xf_emit(ctx, 1, 0xf); ++ xf_emit(ctx, 0x16, 0); /* RO */ ++ /* SEEK */ ++ xf_emit(ctx, 1, 0xf); /* ffffffff VP_ATTR_EN */ ++ xf_emit(ctx, (acnt/8)-1, 0); /* ffffffff VP_ATTR_EN */ ++ /* SEEK */ + if (dev_priv->chipset < 0xa0) +- xf_emit(ctx, 0x834, 0); +- else if (dev_priv->chipset == 0xa0) +- xf_emit(ctx, 0x1873, 0); +- else if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) +- xf_emit(ctx, 0x8ba, 0); ++ xf_emit(ctx, 8, 0); /* RO */ ++ else if (IS_NVA3F(dev_priv->chipset)) ++ xf_emit(ctx, 0xc, 0); /* RO */ ++ else ++ xf_emit(ctx, 7, 0); /* RO */ ++ /* SEEK */ ++ xf_emit(ctx, 0xa, 0); /* RO */ ++ if (dev_priv->chipset == 0xa0) ++ rep = 0xc; ++ else ++ rep = 4; ++ for (i = 0; i < rep; i++) { ++ /* SEEK */ ++ if (IS_NVA3F(dev_priv->chipset)) ++ xf_emit(ctx, 0x20, 0); /* ffffffff */ ++ xf_emit(ctx, 0x200, 0); /* ffffffff */ ++ xf_emit(ctx, 4, 0); /* 7f/ff, 0, 0, 0 */ ++ xf_emit(ctx, 4, 0); /* ffffffff */ ++ } ++ /* SEEK */ ++ xf_emit(ctx, 1, 0); /* 113/111 */ ++ xf_emit(ctx, 1, 0xf); /* ffffffff VP_ATTR_EN */ ++ xf_emit(ctx, (acnt/8)-1, 0); /* ffffffff VP_ATTR_EN */ ++ xf_emit(ctx, acnt/8, 0); /* ffffffff VTX_ATTR_MASK_UNK0DD0 */ ++ xf_emit(ctx, 1, 0); /* 0000000f VP_GP_BUILTIN_ATTR_EN */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ ++ /* SEEK */ ++ if (IS_NVA3F(dev_priv->chipset)) ++ xf_emit(ctx, 7, 0); /* weird... */ + else +- xf_emit(ctx, 0x833, 0); +- xf_emit(ctx, 1, 0xf); +- xf_emit(ctx, 0xf, 0); ++ xf_emit(ctx, 5, 0); /* weird... */ + } + + static void +-nv50_graph_construct_gene_unk7(struct nouveau_grctx *ctx) ++nv50_graph_construct_gene_eng2d(struct nouveau_grctx *ctx) + { + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; +- /* middle of area 1 on pre-NVA0 [after m2mf], middle of area 6 on NVAx */ +- xf_emit(ctx, 2, 0); +- if (dev_priv->chipset == 0x50) +- xf_emit(ctx, 2, 1); +- else +- xf_emit(ctx, 2, 0); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 2, 0x100); +- xf_emit(ctx, 1, 0x11); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 8); +- xf_emit(ctx, 5, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 3, 1); +- xf_emit(ctx, 1, 0xcf); +- xf_emit(ctx, 1, 2); +- xf_emit(ctx, 6, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 3, 1); +- xf_emit(ctx, 4, 0); +- xf_emit(ctx, 1, 4); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 0x15); +- xf_emit(ctx, 3, 0); +- xf_emit(ctx, 1, 0x4444480); +- xf_emit(ctx, 0x37, 0); ++ /* middle of strand 1 on pre-NVA0 [after vfetch], middle of strand 6 on NVAx */ ++ /* SEEK */ ++ xf_emit(ctx, 2, 0); /* 0001ffff CLIP_X, CLIP_Y */ ++ xf_emit(ctx, 2, 0); /* 0000ffff CLIP_W, CLIP_H */ ++ xf_emit(ctx, 1, 0); /* 00000001 CLIP_ENABLE */ ++ if (dev_priv->chipset < 0xa0) { ++ /* this is useless on everything but the original NV50, ++ * guess they forgot to nuke it. Or just didn't bother. */ ++ xf_emit(ctx, 2, 0); /* 0000ffff IFC_CLIP_X, Y */ ++ xf_emit(ctx, 2, 1); /* 0000ffff IFC_CLIP_W, H */ ++ xf_emit(ctx, 1, 0); /* 00000001 IFC_CLIP_ENABLE */ ++ } ++ xf_emit(ctx, 1, 1); /* 00000001 DST_LINEAR */ ++ xf_emit(ctx, 1, 0x100); /* 0001ffff DST_WIDTH */ ++ xf_emit(ctx, 1, 0x100); /* 0001ffff DST_HEIGHT */ ++ xf_emit(ctx, 1, 0x11); /* 3f[NV50]/7f[NV84+] DST_FORMAT */ ++ xf_emit(ctx, 1, 0); /* 0001ffff DRAW_POINT_X */ ++ xf_emit(ctx, 1, 8); /* 0000000f DRAW_UNK58C */ ++ xf_emit(ctx, 1, 0); /* 000fffff SIFC_DST_X_FRACT */ ++ xf_emit(ctx, 1, 0); /* 0001ffff SIFC_DST_X_INT */ ++ xf_emit(ctx, 1, 0); /* 000fffff SIFC_DST_Y_FRACT */ ++ xf_emit(ctx, 1, 0); /* 0001ffff SIFC_DST_Y_INT */ ++ xf_emit(ctx, 1, 0); /* 000fffff SIFC_DX_DU_FRACT */ ++ xf_emit(ctx, 1, 1); /* 0001ffff SIFC_DX_DU_INT */ ++ xf_emit(ctx, 1, 0); /* 000fffff SIFC_DY_DV_FRACT */ ++ xf_emit(ctx, 1, 1); /* 0001ffff SIFC_DY_DV_INT */ ++ xf_emit(ctx, 1, 1); /* 0000ffff SIFC_WIDTH */ ++ xf_emit(ctx, 1, 1); /* 0000ffff SIFC_HEIGHT */ ++ xf_emit(ctx, 1, 0xcf); /* 000000ff SIFC_FORMAT */ ++ xf_emit(ctx, 1, 2); /* 00000003 SIFC_BITMAP_UNK808 */ ++ xf_emit(ctx, 1, 0); /* 00000003 SIFC_BITMAP_LINE_PACK_MODE */ ++ xf_emit(ctx, 1, 0); /* 00000001 SIFC_BITMAP_LSB_FIRST */ ++ xf_emit(ctx, 1, 0); /* 00000001 SIFC_BITMAP_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 0000ffff BLIT_DST_X */ ++ xf_emit(ctx, 1, 0); /* 0000ffff BLIT_DST_Y */ ++ xf_emit(ctx, 1, 0); /* 000fffff BLIT_DU_DX_FRACT */ ++ xf_emit(ctx, 1, 1); /* 0001ffff BLIT_DU_DX_INT */ ++ xf_emit(ctx, 1, 0); /* 000fffff BLIT_DV_DY_FRACT */ ++ xf_emit(ctx, 1, 1); /* 0001ffff BLIT_DV_DY_INT */ ++ xf_emit(ctx, 1, 1); /* 0000ffff BLIT_DST_W */ ++ xf_emit(ctx, 1, 1); /* 0000ffff BLIT_DST_H */ ++ xf_emit(ctx, 1, 0); /* 000fffff BLIT_SRC_X_FRACT */ ++ xf_emit(ctx, 1, 0); /* 0001ffff BLIT_SRC_X_INT */ ++ xf_emit(ctx, 1, 0); /* 000fffff BLIT_SRC_Y_FRACT */ ++ xf_emit(ctx, 1, 0); /* 00000001 UNK888 */ ++ xf_emit(ctx, 1, 4); /* 0000003f UNK884 */ ++ xf_emit(ctx, 1, 0); /* 00000007 UNK880 */ ++ xf_emit(ctx, 1, 1); /* 0000001f tesla UNK0FB8 */ ++ xf_emit(ctx, 1, 0x15); /* 000000ff tesla UNK128C */ ++ xf_emit(ctx, 2, 0); /* 00000007, ffff0ff3 */ ++ xf_emit(ctx, 1, 0); /* 00000001 UNK260 */ ++ xf_emit(ctx, 1, 0x4444480); /* 1fffffff UNK870 */ ++ /* SEEK */ ++ xf_emit(ctx, 0x10, 0); ++ /* SEEK */ ++ xf_emit(ctx, 0x27, 0); + } + + static void +-nv50_graph_construct_gene_unk8(struct nouveau_grctx *ctx) ++nv50_graph_construct_gene_csched(struct nouveau_grctx *ctx) + { +- /* middle of area 1 on pre-NVA0 [after m2mf], middle of area 0 on NVAx */ +- xf_emit(ctx, 4, 0); +- xf_emit(ctx, 1, 0x8100c12); +- xf_emit(ctx, 4, 0); +- xf_emit(ctx, 1, 0x100); +- xf_emit(ctx, 2, 0); +- xf_emit(ctx, 1, 0x10001); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 0x10001); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 0x10001); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 4); +- xf_emit(ctx, 1, 2); ++ struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; ++ /* middle of strand 1 on pre-NVA0 [after eng2d], middle of strand 0 on NVAx */ ++ /* SEEK */ ++ xf_emit(ctx, 2, 0); /* 00007fff WINDOW_OFFSET_XY... what is it doing here??? */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1924 */ ++ xf_emit(ctx, 1, 0); /* 00000003 WINDOW_ORIGIN */ ++ xf_emit(ctx, 1, 0x8100c12); /* 1fffffff FP_INTERPOLANT_CTRL */ ++ xf_emit(ctx, 1, 0); /* 000003ff */ ++ /* SEEK */ ++ xf_emit(ctx, 1, 0); /* ffffffff turing UNK364 */ ++ xf_emit(ctx, 1, 0); /* 0000000f turing UNK36C */ ++ xf_emit(ctx, 1, 0); /* 0000ffff USER_PARAM_COUNT */ ++ xf_emit(ctx, 1, 0x100); /* 00ffffff turing UNK384 */ ++ xf_emit(ctx, 1, 0); /* 0000000f turing UNK2A0 */ ++ xf_emit(ctx, 1, 0); /* 0000ffff GRIDID */ ++ xf_emit(ctx, 1, 0x10001); /* ffffffff GRIDDIM_XY */ ++ xf_emit(ctx, 1, 0); /* ffffffff */ ++ xf_emit(ctx, 1, 0x10001); /* ffffffff BLOCKDIM_XY */ ++ xf_emit(ctx, 1, 1); /* 0000ffff BLOCKDIM_Z */ ++ xf_emit(ctx, 1, 0x10001); /* 00ffffff BLOCK_ALLOC */ ++ xf_emit(ctx, 1, 1); /* 00000001 LANES32 */ ++ xf_emit(ctx, 1, 4); /* 000000ff FP_REG_ALLOC_TEMP */ ++ xf_emit(ctx, 1, 2); /* 00000003 REG_MODE */ ++ /* SEEK */ ++ xf_emit(ctx, 0x40, 0); /* ffffffff USER_PARAM */ ++ switch (dev_priv->chipset) { ++ case 0x50: ++ case 0x92: ++ xf_emit(ctx, 8, 0); /* 7, 0, 0, 0, ... */ ++ xf_emit(ctx, 0x80, 0); /* fff */ ++ xf_emit(ctx, 2, 0); /* ff, fff */ ++ xf_emit(ctx, 0x10*2, 0); /* ffffffff, 1f */ ++ break; ++ case 0x84: ++ xf_emit(ctx, 8, 0); /* 7, 0, 0, 0, ... */ ++ xf_emit(ctx, 0x60, 0); /* fff */ ++ xf_emit(ctx, 2, 0); /* ff, fff */ ++ xf_emit(ctx, 0xc*2, 0); /* ffffffff, 1f */ ++ break; ++ case 0x94: ++ case 0x96: ++ xf_emit(ctx, 8, 0); /* 7, 0, 0, 0, ... */ ++ xf_emit(ctx, 0x40, 0); /* fff */ ++ xf_emit(ctx, 2, 0); /* ff, fff */ ++ xf_emit(ctx, 8*2, 0); /* ffffffff, 1f */ ++ break; ++ case 0x86: ++ case 0x98: ++ xf_emit(ctx, 4, 0); /* f, 0, 0, 0 */ ++ xf_emit(ctx, 0x10, 0); /* fff */ ++ xf_emit(ctx, 2, 0); /* ff, fff */ ++ xf_emit(ctx, 2*2, 0); /* ffffffff, 1f */ ++ break; ++ case 0xa0: ++ xf_emit(ctx, 8, 0); /* 7, 0, 0, 0, ... */ ++ xf_emit(ctx, 0xf0, 0); /* fff */ ++ xf_emit(ctx, 2, 0); /* ff, fff */ ++ xf_emit(ctx, 0x1e*2, 0); /* ffffffff, 1f */ ++ break; ++ case 0xa3: ++ xf_emit(ctx, 8, 0); /* 7, 0, 0, 0, ... */ ++ xf_emit(ctx, 0x60, 0); /* fff */ ++ xf_emit(ctx, 2, 0); /* ff, fff */ ++ xf_emit(ctx, 0xc*2, 0); /* ffffffff, 1f */ ++ break; ++ case 0xa5: ++ case 0xaf: ++ xf_emit(ctx, 8, 0); /* 7, 0, 0, 0, ... */ ++ xf_emit(ctx, 0x30, 0); /* fff */ ++ xf_emit(ctx, 2, 0); /* ff, fff */ ++ xf_emit(ctx, 6*2, 0); /* ffffffff, 1f */ ++ break; ++ case 0xaa: ++ xf_emit(ctx, 0x12, 0); ++ break; ++ case 0xa8: ++ case 0xac: ++ xf_emit(ctx, 4, 0); /* f, 0, 0, 0 */ ++ xf_emit(ctx, 0x10, 0); /* fff */ ++ xf_emit(ctx, 2, 0); /* ff, fff */ ++ xf_emit(ctx, 2*2, 0); /* ffffffff, 1f */ ++ break; ++ } ++ xf_emit(ctx, 1, 0); /* 0000000f */ ++ xf_emit(ctx, 1, 0); /* 00000000 */ ++ xf_emit(ctx, 1, 0); /* ffffffff */ ++ xf_emit(ctx, 1, 0); /* 0000001f */ ++ xf_emit(ctx, 4, 0); /* ffffffff */ ++ xf_emit(ctx, 1, 0); /* 00000003 turing UNK35C */ ++ xf_emit(ctx, 1, 0); /* ffffffff */ ++ xf_emit(ctx, 4, 0); /* ffffffff */ ++ xf_emit(ctx, 1, 0); /* 00000003 turing UNK35C */ ++ xf_emit(ctx, 1, 0); /* ffffffff */ ++ xf_emit(ctx, 1, 0); /* 000000ff */ + } + + static void +-nv50_graph_construct_gene_unk9(struct nouveau_grctx *ctx) ++nv50_graph_construct_gene_unk1cxx(struct nouveau_grctx *ctx) + { + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; +- /* middle of area 2 on pre-NVA0 [after m2mf], end of area 0 on NVAx */ +- xf_emit(ctx, 1, 0x3f800000); +- xf_emit(ctx, 6, 0); +- xf_emit(ctx, 1, 4); +- xf_emit(ctx, 1, 0x1a); +- xf_emit(ctx, 2, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 0x12, 0); +- xf_emit(ctx, 1, 0x00ffff00); +- xf_emit(ctx, 6, 0); +- xf_emit(ctx, 1, 0xf); +- xf_emit(ctx, 7, 0); +- xf_emit(ctx, 1, 0x0fac6881); +- xf_emit(ctx, 1, 0x11); +- xf_emit(ctx, 0xf, 0); +- xf_emit(ctx, 1, 4); +- xf_emit(ctx, 2, 0); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) +- xf_emit(ctx, 1, 3); ++ xf_emit(ctx, 2, 0); /* 00007fff WINDOW_OFFSET_XY */ ++ xf_emit(ctx, 1, 0x3f800000); /* ffffffff LINE_WIDTH */ ++ xf_emit(ctx, 1, 0); /* 00000001 LINE_SMOOTH_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1658 */ ++ xf_emit(ctx, 1, 0); /* 00000001 POLYGON_SMOOTH_ENABLE */ ++ xf_emit(ctx, 3, 0); /* 00000001 POLYGON_OFFSET_*_ENABLE */ ++ xf_emit(ctx, 1, 4); /* 0000000f CULL_MODE */ ++ xf_emit(ctx, 1, 0x1a); /* 0000001f POLYGON_MODE */ ++ xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */ ++ xf_emit(ctx, 1, 0); /* 00000001 POINT_SPRITE_ENABLE */ ++ xf_emit(ctx, 1, 1); /* 00000001 tesla UNK165C */ ++ xf_emit(ctx, 0x10, 0); /* 00000001 SCISSOR_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */ ++ xf_emit(ctx, 1, 0); /* 00000001 LINE_STIPPLE_ENABLE */ ++ xf_emit(ctx, 1, 0x00ffff00); /* 00ffffff LINE_STIPPLE_PATTERN */ ++ xf_emit(ctx, 1, 0); /* ffffffff POLYGON_OFFSET_UNITS */ ++ xf_emit(ctx, 1, 0); /* ffffffff POLYGON_OFFSET_FACTOR */ ++ xf_emit(ctx, 1, 0); /* 00000003 tesla UNK1668 */ ++ xf_emit(ctx, 2, 0); /* 07ffffff SCREEN_SCISSOR */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1900 */ ++ xf_emit(ctx, 1, 0xf); /* 0000000f COLOR_MASK */ ++ xf_emit(ctx, 7, 0); /* 0000000f COLOR_MASK */ ++ xf_emit(ctx, 1, 0x0fac6881); /* 0fffffff RT_CONTROL */ ++ xf_emit(ctx, 1, 0x11); /* 0000007f RT_FORMAT */ ++ xf_emit(ctx, 7, 0); /* 0000007f RT_FORMAT */ ++ xf_emit(ctx, 8, 0); /* 00000001 RT_HORIZ_LINEAR */ ++ xf_emit(ctx, 1, 4); /* 00000007 FP_CONTROL */ ++ xf_emit(ctx, 1, 0); /* 00000001 ALPHA_TEST_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000007 ALPHA_TEST_FUNC */ ++ if (IS_NVA3F(dev_priv->chipset)) ++ xf_emit(ctx, 1, 3); /* 00000003 UNK16B4 */ + else if (dev_priv->chipset >= 0xa0) +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 2, 0); +- xf_emit(ctx, 1, 2); +- xf_emit(ctx, 2, 0x04000000); +- xf_emit(ctx, 3, 0); +- xf_emit(ctx, 1, 5); +- xf_emit(ctx, 1, 0x52); +- if (dev_priv->chipset == 0x50) { +- xf_emit(ctx, 0x13, 0); +- } else { +- xf_emit(ctx, 4, 0); +- xf_emit(ctx, 1, 1); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) +- xf_emit(ctx, 0x11, 0); +- else +- xf_emit(ctx, 0x10, 0); ++ xf_emit(ctx, 1, 1); /* 00000001 UNK16B4 */ ++ xf_emit(ctx, 1, 0); /* 00000003 MULTISAMPLE_CTRL */ ++ xf_emit(ctx, 1, 0); /* 00000003 tesla UNK0F90 */ ++ xf_emit(ctx, 1, 2); /* 00000003 tesla UNK143C */ ++ xf_emit(ctx, 2, 0x04000000); /* 07ffffff tesla UNK0D6C */ ++ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_MASK */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000001 SAMPLECNT_ENABLE */ ++ xf_emit(ctx, 1, 5); /* 0000000f UNK1408 */ ++ xf_emit(ctx, 1, 0x52); /* 000001ff SEMANTIC_PTSZ */ ++ xf_emit(ctx, 1, 0); /* ffffffff POINT_SIZE */ ++ xf_emit(ctx, 1, 0); /* 00000001 */ ++ xf_emit(ctx, 1, 0); /* 00000007 tesla UNK0FB4 */ ++ if (dev_priv->chipset != 0x50) { ++ xf_emit(ctx, 1, 0); /* 3ff */ ++ xf_emit(ctx, 1, 1); /* 00000001 tesla UNK1110 */ + } +- xf_emit(ctx, 0x10, 0x3f800000); +- xf_emit(ctx, 1, 0x10); +- xf_emit(ctx, 0x26, 0); +- xf_emit(ctx, 1, 0x8100c12); +- xf_emit(ctx, 1, 5); +- xf_emit(ctx, 2, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 4, 0xffff); ++ if (IS_NVA3F(dev_priv->chipset)) ++ xf_emit(ctx, 1, 0); /* 00000003 tesla UNK1928 */ ++ xf_emit(ctx, 0x10, 0); /* ffffffff DEPTH_RANGE_NEAR */ ++ xf_emit(ctx, 0x10, 0x3f800000); /* ffffffff DEPTH_RANGE_FAR */ ++ xf_emit(ctx, 1, 0x10); /* 000000ff VIEW_VOLUME_CLIP_CTRL */ ++ xf_emit(ctx, 0x20, 0); /* 07ffffff VIEWPORT_HORIZ, then VIEWPORT_VERT. (W&0x3fff)<<13 | (X&0x1fff). */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK187C */ ++ xf_emit(ctx, 1, 0); /* 00000003 WINDOW_ORIGIN */ ++ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_BACK_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_MASK */ ++ xf_emit(ctx, 1, 0x8100c12); /* 1fffffff FP_INTERPOLANT_CTRL */ ++ xf_emit(ctx, 1, 5); /* 0000000f tesla UNK1220 */ ++ xf_emit(ctx, 1, 0); /* 00000007 MULTISAMPLE_SAMPLES_LOG2 */ ++ xf_emit(ctx, 1, 0); /* 000000ff tesla UNK1A20 */ ++ xf_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000001 VERTEX_TWO_SIDE_ENABLE */ ++ xf_emit(ctx, 4, 0xffff); /* 0000ffff MSAA_MASK */ + if (dev_priv->chipset != 0x50) +- xf_emit(ctx, 1, 3); ++ xf_emit(ctx, 1, 3); /* 00000003 tesla UNK1100 */ + if (dev_priv->chipset < 0xa0) +- xf_emit(ctx, 0x1f, 0); +- else if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) +- xf_emit(ctx, 0xc, 0); +- else +- xf_emit(ctx, 3, 0); +- xf_emit(ctx, 1, 0x00ffff00); +- xf_emit(ctx, 1, 0x1a); ++ xf_emit(ctx, 0x1c, 0); /* RO */ ++ else if (IS_NVA3F(dev_priv->chipset)) ++ xf_emit(ctx, 0x9, 0); ++ xf_emit(ctx, 1, 0); /* 00000001 UNK1534 */ ++ xf_emit(ctx, 1, 0); /* 00000001 LINE_SMOOTH_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000001 LINE_STIPPLE_ENABLE */ ++ xf_emit(ctx, 1, 0x00ffff00); /* 00ffffff LINE_STIPPLE_PATTERN */ ++ xf_emit(ctx, 1, 0x1a); /* 0000001f POLYGON_MODE */ ++ xf_emit(ctx, 1, 0); /* 00000003 WINDOW_ORIGIN */ + if (dev_priv->chipset != 0x50) { +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 3); ++ xf_emit(ctx, 1, 3); /* 00000003 tesla UNK1100 */ ++ xf_emit(ctx, 1, 0); /* 3ff */ + } ++ /* XXX: the following block could belong either to unk1cxx, or ++ * to STRMOUT. Rather hard to tell. */ + if (dev_priv->chipset < 0xa0) +- xf_emit(ctx, 0x26, 0); ++ xf_emit(ctx, 0x25, 0); + else +- xf_emit(ctx, 0x3c, 0); +- xf_emit(ctx, 1, 0x102); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 4, 4); +- if (dev_priv->chipset >= 0xa0) +- xf_emit(ctx, 8, 0); +- xf_emit(ctx, 2, 4); +- xf_emit(ctx, 1, 0); ++ xf_emit(ctx, 0x3b, 0); ++} ++ ++static void ++nv50_graph_construct_gene_strmout(struct nouveau_grctx *ctx) ++{ ++ struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; ++ xf_emit(ctx, 1, 0x102); /* 0000ffff STRMOUT_BUFFER_CTRL */ ++ xf_emit(ctx, 1, 0); /* ffffffff STRMOUT_PRIMITIVE_COUNT */ ++ xf_emit(ctx, 4, 4); /* 000000ff STRMOUT_NUM_ATTRIBS */ ++ if (dev_priv->chipset >= 0xa0) { ++ xf_emit(ctx, 4, 0); /* ffffffff UNK1A8C */ ++ xf_emit(ctx, 4, 0); /* ffffffff UNK1780 */ ++ } ++ xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */ ++ xf_emit(ctx, 1, 4); /* 0000007f VP_RESULT_MAP_SIZE */ ++ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ + if (dev_priv->chipset == 0x50) +- xf_emit(ctx, 1, 0x3ff); ++ xf_emit(ctx, 1, 0x3ff); /* 000003ff tesla UNK0D68 */ + else +- xf_emit(ctx, 1, 0x7ff); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 0x102); +- xf_emit(ctx, 9, 0); +- xf_emit(ctx, 4, 4); +- xf_emit(ctx, 0x2c, 0); ++ xf_emit(ctx, 1, 0x7ff); /* 000007ff tesla UNK0D68 */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ ++ /* SEEK */ ++ xf_emit(ctx, 1, 0x102); /* 0000ffff STRMOUT_BUFFER_CTRL */ ++ xf_emit(ctx, 1, 0); /* ffffffff STRMOUT_PRIMITIVE_COUNT */ ++ xf_emit(ctx, 4, 0); /* 000000ff STRMOUT_ADDRESS_HIGH */ ++ xf_emit(ctx, 4, 0); /* ffffffff STRMOUT_ADDRESS_LOW */ ++ xf_emit(ctx, 4, 4); /* 000000ff STRMOUT_NUM_ATTRIBS */ ++ if (dev_priv->chipset >= 0xa0) { ++ xf_emit(ctx, 4, 0); /* ffffffff UNK1A8C */ ++ xf_emit(ctx, 4, 0); /* ffffffff UNK1780 */ ++ } ++ xf_emit(ctx, 1, 0); /* 0000ffff DMA_STRMOUT */ ++ xf_emit(ctx, 1, 0); /* 0000ffff DMA_QUERY */ ++ xf_emit(ctx, 1, 0); /* 000000ff QUERY_ADDRESS_HIGH */ ++ xf_emit(ctx, 2, 0); /* ffffffff QUERY_ADDRESS_LOW QUERY_COUNTER */ ++ xf_emit(ctx, 2, 0); /* ffffffff */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ ++ /* SEEK */ ++ xf_emit(ctx, 0x20, 0); /* ffffffff STRMOUT_MAP */ ++ xf_emit(ctx, 1, 0); /* 0000000f */ ++ xf_emit(ctx, 1, 0); /* 00000000? */ ++ xf_emit(ctx, 2, 0); /* ffffffff */ ++} ++ ++static void ++nv50_graph_construct_gene_ropm1(struct nouveau_grctx *ctx) ++{ ++ struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; ++ xf_emit(ctx, 1, 0x4e3bfdf); /* ffffffff UNK0D64 */ ++ xf_emit(ctx, 1, 0x4e3bfdf); /* ffffffff UNK0DF4 */ ++ xf_emit(ctx, 1, 0); /* 00000007 */ ++ xf_emit(ctx, 1, 0); /* 000003ff */ ++ if (IS_NVA3F(dev_priv->chipset)) ++ xf_emit(ctx, 1, 0x11); /* 000000ff tesla UNK1968 */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */ ++} ++ ++static void ++nv50_graph_construct_gene_ropm2(struct nouveau_grctx *ctx) ++{ ++ struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; ++ /* SEEK */ ++ xf_emit(ctx, 1, 0); /* 0000ffff DMA_QUERY */ ++ xf_emit(ctx, 1, 0x0fac6881); /* 0fffffff RT_CONTROL */ ++ xf_emit(ctx, 2, 0); /* ffffffff */ ++ xf_emit(ctx, 1, 0); /* 000000ff QUERY_ADDRESS_HIGH */ ++ xf_emit(ctx, 2, 0); /* ffffffff QUERY_ADDRESS_LOW, COUNTER */ ++ xf_emit(ctx, 1, 0); /* 00000001 SAMPLECNT_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 7 */ ++ /* SEEK */ ++ xf_emit(ctx, 1, 0); /* 0000ffff DMA_QUERY */ ++ xf_emit(ctx, 1, 0); /* 000000ff QUERY_ADDRESS_HIGH */ ++ xf_emit(ctx, 2, 0); /* ffffffff QUERY_ADDRESS_LOW, COUNTER */ ++ xf_emit(ctx, 1, 0x4e3bfdf); /* ffffffff UNK0D64 */ ++ xf_emit(ctx, 1, 0x4e3bfdf); /* ffffffff UNK0DF4 */ ++ xf_emit(ctx, 1, 0); /* 00000001 eng2d UNK260 */ ++ xf_emit(ctx, 1, 0); /* ff/3ff */ ++ xf_emit(ctx, 1, 0); /* 00000007 */ ++ if (IS_NVA3F(dev_priv->chipset)) ++ xf_emit(ctx, 1, 0x11); /* 000000ff tesla UNK1968 */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */ + } + + static void +@@ -1749,443 +2392,709 @@ nv50_graph_construct_gene_ropc(struct nouveau_grctx *ctx) + int magic2; + if (dev_priv->chipset == 0x50) { + magic2 = 0x00003e60; +- } else if (dev_priv->chipset <= 0xa0 || dev_priv->chipset >= 0xaa) { ++ } else if (!IS_NVA3F(dev_priv->chipset)) { + magic2 = 0x001ffe67; + } else { + magic2 = 0x00087e67; + } +- xf_emit(ctx, 8, 0); +- xf_emit(ctx, 1, 2); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, magic2); +- xf_emit(ctx, 4, 0); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 7, 0); +- if (dev_priv->chipset >= 0xa0 && dev_priv->chipset < 0xaa) +- xf_emit(ctx, 1, 0x15); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 0x10); +- xf_emit(ctx, 2, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 4, 0); ++ xf_emit(ctx, 1, 0); /* f/7 MUTISAMPLE_SAMPLES_LOG2 */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */ ++ xf_emit(ctx, 1, 0); /* 00000007 STENCIL_BACK_FUNC_FUNC */ ++ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_FUNC_MASK */ ++ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_MASK */ ++ xf_emit(ctx, 3, 0); /* 00000007 STENCIL_BACK_OP_FAIL, ZFAIL, ZPASS */ ++ xf_emit(ctx, 1, 2); /* 00000003 tesla UNK143C */ ++ xf_emit(ctx, 1, 0); /* ffff0ff3 */ ++ xf_emit(ctx, 1, magic2); /* 001fffff tesla UNK0F78 */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_BOUNDS_EN */ ++ xf_emit(ctx, 1, 0); /* 00000007 DEPTH_TEST_FUNC */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE_ENABLE */ ++ if (IS_NVA3F(dev_priv->chipset)) ++ xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */ ++ xf_emit(ctx, 1, 0); /* 00000007 STENCIL_FRONT_FUNC_FUNC */ ++ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_FUNC_MASK */ ++ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_MASK */ ++ xf_emit(ctx, 3, 0); /* 00000007 STENCIL_FRONT_OP_FAIL, ZFAIL, ZPASS */ ++ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */ ++ if (dev_priv->chipset >= 0xa0 && !IS_NVAAF(dev_priv->chipset)) ++ xf_emit(ctx, 1, 0x15); /* 000000ff */ ++ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_BACK_ENABLE */ ++ xf_emit(ctx, 1, 1); /* 00000001 tesla UNK15B4 */ ++ xf_emit(ctx, 1, 0x10); /* 3ff/ff VIEW_VOLUME_CLIP_CTRL */ ++ xf_emit(ctx, 1, 0); /* ffffffff CLEAR_DEPTH */ ++ xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */ ++ xf_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */ + if (dev_priv->chipset == 0x86 || dev_priv->chipset == 0x92 || dev_priv->chipset == 0x98 || dev_priv->chipset >= 0xa0) { +- xf_emit(ctx, 1, 4); +- xf_emit(ctx, 1, 0x400); +- xf_emit(ctx, 1, 0x300); +- xf_emit(ctx, 1, 0x1001); ++ xf_emit(ctx, 3, 0); /* ff, ffffffff, ffffffff */ ++ xf_emit(ctx, 1, 4); /* 7 */ ++ xf_emit(ctx, 1, 0x400); /* fffffff */ ++ xf_emit(ctx, 1, 0x300); /* ffff */ ++ xf_emit(ctx, 1, 0x1001); /* 1fff */ + if (dev_priv->chipset != 0xa0) { +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) +- xf_emit(ctx, 1, 0); ++ if (IS_NVA3F(dev_priv->chipset)) ++ xf_emit(ctx, 1, 0); /* 0000000f UNK15C8 */ + else +- xf_emit(ctx, 1, 0x15); ++ xf_emit(ctx, 1, 0x15); /* ff */ + } +- xf_emit(ctx, 3, 0); + } +- xf_emit(ctx, 2, 0); +- xf_emit(ctx, 1, 2); +- xf_emit(ctx, 8, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 0x10); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 0x13, 0); +- xf_emit(ctx, 1, 0x10); +- xf_emit(ctx, 0x10, 0); +- xf_emit(ctx, 0x10, 0x3f800000); +- xf_emit(ctx, 0x19, 0); +- xf_emit(ctx, 1, 0x10); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 0x3f); +- xf_emit(ctx, 6, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 1); ++ xf_emit(ctx, 1, 0); /* 00000007 MULTISAMPLE_SAMPLES_LOG2 */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */ ++ xf_emit(ctx, 1, 0); /* 00000007 STENCIL_BACK_FUNC_FUNC */ ++ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_FUNC_MASK */ ++ xf_emit(ctx, 1, 0); /* ffff0ff3 */ ++ xf_emit(ctx, 1, 2); /* 00000003 tesla UNK143C */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_BOUNDS_EN */ ++ xf_emit(ctx, 1, 0); /* 00000007 DEPTH_TEST_FUNC */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000007 STENCIL_FRONT_FUNC_FUNC */ ++ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_FUNC_MASK */ ++ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_BACK_ENABLE */ ++ xf_emit(ctx, 1, 1); /* 00000001 tesla UNK15B4 */ ++ xf_emit(ctx, 1, 0x10); /* 7f/ff VIEW_VOLUME_CLIP_CTRL */ ++ xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */ ++ xf_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1900 */ ++ xf_emit(ctx, 1, 0); /* 00000007 STENCIL_BACK_FUNC_FUNC */ ++ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_FUNC_MASK */ ++ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_FUNC_REF */ ++ xf_emit(ctx, 2, 0); /* ffffffff DEPTH_BOUNDS */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_BOUNDS_EN */ ++ xf_emit(ctx, 1, 0); /* 00000007 DEPTH_TEST_FUNC */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 0000000f */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK0FB0 */ ++ xf_emit(ctx, 1, 0); /* 00000007 STENCIL_FRONT_FUNC_FUNC */ ++ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_FUNC_MASK */ ++ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_FUNC_REF */ ++ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_BACK_ENABLE */ ++ xf_emit(ctx, 1, 0x10); /* 7f/ff VIEW_VOLUME_CLIP_CTRL */ ++ xf_emit(ctx, 0x10, 0); /* ffffffff DEPTH_RANGE_NEAR */ ++ xf_emit(ctx, 0x10, 0x3f800000); /* ffffffff DEPTH_RANGE_FAR */ ++ xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */ ++ xf_emit(ctx, 1, 0); /* 00000007 MULTISAMPLE_SAMPLES_LOG2 */ ++ xf_emit(ctx, 1, 0); /* 00000007 STENCIL_BACK_FUNC_FUNC */ ++ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_FUNC_MASK */ ++ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_FUNC_REF */ ++ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_MASK */ ++ xf_emit(ctx, 3, 0); /* 00000007 STENCIL_BACK_OP_FAIL, ZFAIL, ZPASS */ ++ xf_emit(ctx, 2, 0); /* ffffffff DEPTH_BOUNDS */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_BOUNDS_EN */ ++ xf_emit(ctx, 1, 0); /* 00000007 DEPTH_TEST_FUNC */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 000000ff CLEAR_STENCIL */ ++ xf_emit(ctx, 1, 0); /* 00000007 STENCIL_FRONT_FUNC_FUNC */ ++ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_FUNC_MASK */ ++ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_FUNC_REF */ ++ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_MASK */ ++ xf_emit(ctx, 3, 0); /* 00000007 STENCIL_FRONT_OP_FAIL, ZFAIL, ZPASS */ ++ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_BACK_ENABLE */ ++ xf_emit(ctx, 1, 0x10); /* 7f/ff VIEW_VOLUME_CLIP_CTRL */ ++ xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */ ++ xf_emit(ctx, 1, 0x3f); /* 0000003f UNK1590 */ ++ xf_emit(ctx, 1, 0); /* 00000007 MULTISAMPLE_SAMPLES_LOG2 */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */ ++ xf_emit(ctx, 2, 0); /* ffff0ff3, ffff */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK0FB0 */ ++ xf_emit(ctx, 1, 0); /* 0001ffff GP_BUILTIN_RESULT_EN */ ++ xf_emit(ctx, 1, 1); /* 00000001 tesla UNK15B4 */ ++ xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */ ++ xf_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */ ++ xf_emit(ctx, 1, 0); /* ffffffff CLEAR_DEPTH */ ++ xf_emit(ctx, 1, 1); /* 00000001 tesla UNK19CC */ + if (dev_priv->chipset >= 0xa0) { + xf_emit(ctx, 2, 0); + xf_emit(ctx, 1, 0x1001); + xf_emit(ctx, 0xb, 0); + } else { +- xf_emit(ctx, 0xc, 0); ++ xf_emit(ctx, 1, 0); /* 00000007 */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */ ++ xf_emit(ctx, 1, 0); /* 00000007 MULTISAMPLE_SAMPLES_LOG2 */ ++ xf_emit(ctx, 8, 0); /* 00000001 BLEND_ENABLE */ ++ xf_emit(ctx, 1, 0); /* ffff0ff3 */ + } +- xf_emit(ctx, 1, 0x11); +- xf_emit(ctx, 7, 0); +- xf_emit(ctx, 1, 0xf); +- xf_emit(ctx, 7, 0); +- xf_emit(ctx, 1, 0x11); +- if (dev_priv->chipset == 0x50) +- xf_emit(ctx, 4, 0); +- else +- xf_emit(ctx, 6, 0); +- xf_emit(ctx, 3, 1); +- xf_emit(ctx, 1, 2); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 2); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, magic2); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 0x0fac6881); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) { +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 0x18, 1); +- xf_emit(ctx, 8, 2); +- xf_emit(ctx, 8, 1); +- xf_emit(ctx, 8, 2); +- xf_emit(ctx, 8, 1); +- xf_emit(ctx, 3, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 5, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 0x16, 0); ++ xf_emit(ctx, 1, 0x11); /* 3f/7f RT_FORMAT */ ++ xf_emit(ctx, 7, 0); /* 3f/7f RT_FORMAT */ ++ xf_emit(ctx, 1, 0xf); /* 0000000f COLOR_MASK */ ++ xf_emit(ctx, 7, 0); /* 0000000f COLOR_MASK */ ++ xf_emit(ctx, 1, 0x11); /* 3f/7f */ ++ xf_emit(ctx, 1, 0); /* 00000001 LOGIC_OP_ENABLE */ ++ if (dev_priv->chipset != 0x50) { ++ xf_emit(ctx, 1, 0); /* 0000000f LOGIC_OP */ ++ xf_emit(ctx, 1, 0); /* 000000ff */ ++ } ++ xf_emit(ctx, 1, 0); /* 00000007 OPERATION */ ++ xf_emit(ctx, 1, 0); /* ff/3ff */ ++ xf_emit(ctx, 1, 0); /* 00000003 UNK0F90 */ ++ xf_emit(ctx, 2, 1); /* 00000007 BLEND_EQUATION_RGB, ALPHA */ ++ xf_emit(ctx, 1, 1); /* 00000001 UNK133C */ ++ xf_emit(ctx, 1, 2); /* 0000001f BLEND_FUNC_SRC_RGB */ ++ xf_emit(ctx, 1, 1); /* 0000001f BLEND_FUNC_DST_RGB */ ++ xf_emit(ctx, 1, 2); /* 0000001f BLEND_FUNC_SRC_ALPHA */ ++ xf_emit(ctx, 1, 1); /* 0000001f BLEND_FUNC_DST_ALPHA */ ++ xf_emit(ctx, 1, 0); /* 00000001 */ ++ xf_emit(ctx, 1, magic2); /* 001fffff tesla UNK0F78 */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */ ++ xf_emit(ctx, 1, 0x0fac6881); /* 0fffffff RT_CONTROL */ ++ if (IS_NVA3F(dev_priv->chipset)) { ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK12E4 */ ++ xf_emit(ctx, 8, 1); /* 00000007 IBLEND_EQUATION_RGB */ ++ xf_emit(ctx, 8, 1); /* 00000007 IBLEND_EQUATION_ALPHA */ ++ xf_emit(ctx, 8, 1); /* 00000001 IBLEND_UNK00 */ ++ xf_emit(ctx, 8, 2); /* 0000001f IBLEND_FUNC_SRC_RGB */ ++ xf_emit(ctx, 8, 1); /* 0000001f IBLEND_FUNC_DST_RGB */ ++ xf_emit(ctx, 8, 2); /* 0000001f IBLEND_FUNC_SRC_ALPHA */ ++ xf_emit(ctx, 8, 1); /* 0000001f IBLEND_FUNC_DST_ALPHA */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1140 */ ++ xf_emit(ctx, 2, 0); /* 00000001 */ ++ xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */ ++ xf_emit(ctx, 1, 0); /* 0000000f */ ++ xf_emit(ctx, 1, 0); /* 00000003 */ ++ xf_emit(ctx, 1, 0); /* ffffffff */ ++ xf_emit(ctx, 2, 0); /* 00000001 */ ++ xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */ ++ xf_emit(ctx, 1, 0); /* 00000001 */ ++ xf_emit(ctx, 1, 0); /* 000003ff */ ++ } else if (dev_priv->chipset >= 0xa0) { ++ xf_emit(ctx, 2, 0); /* 00000001 */ ++ xf_emit(ctx, 1, 0); /* 00000007 */ ++ xf_emit(ctx, 1, 0); /* 00000003 */ ++ xf_emit(ctx, 1, 0); /* ffffffff */ ++ xf_emit(ctx, 2, 0); /* 00000001 */ + } else { +- if (dev_priv->chipset >= 0xa0) +- xf_emit(ctx, 0x1b, 0); +- else +- xf_emit(ctx, 0x15, 0); ++ xf_emit(ctx, 1, 0); /* 00000007 MULTISAMPLE_SAMPLES_LOG2 */ ++ xf_emit(ctx, 1, 0); /* 00000003 tesla UNK1430 */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */ + } +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 2); +- xf_emit(ctx, 2, 1); +- xf_emit(ctx, 1, 2); +- xf_emit(ctx, 2, 1); ++ xf_emit(ctx, 4, 0); /* ffffffff CLEAR_COLOR */ ++ xf_emit(ctx, 4, 0); /* ffffffff BLEND_COLOR A R G B */ ++ xf_emit(ctx, 1, 0); /* 00000fff eng2d UNK2B0 */ + if (dev_priv->chipset >= 0xa0) +- xf_emit(ctx, 4, 0); +- else +- xf_emit(ctx, 3, 0); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) { +- xf_emit(ctx, 0x10, 1); +- xf_emit(ctx, 8, 2); +- xf_emit(ctx, 0x10, 1); +- xf_emit(ctx, 8, 2); +- xf_emit(ctx, 8, 1); +- xf_emit(ctx, 3, 0); ++ xf_emit(ctx, 2, 0); /* 00000001 */ ++ xf_emit(ctx, 1, 0); /* 000003ff */ ++ xf_emit(ctx, 8, 0); /* 00000001 BLEND_ENABLE */ ++ xf_emit(ctx, 1, 1); /* 00000001 UNK133C */ ++ xf_emit(ctx, 1, 2); /* 0000001f BLEND_FUNC_SRC_RGB */ ++ xf_emit(ctx, 1, 1); /* 0000001f BLEND_FUNC_DST_RGB */ ++ xf_emit(ctx, 1, 1); /* 00000007 BLEND_EQUATION_RGB */ ++ xf_emit(ctx, 1, 2); /* 0000001f BLEND_FUNC_SRC_ALPHA */ ++ xf_emit(ctx, 1, 1); /* 0000001f BLEND_FUNC_DST_ALPHA */ ++ xf_emit(ctx, 1, 1); /* 00000007 BLEND_EQUATION_ALPHA */ ++ xf_emit(ctx, 1, 0); /* 00000001 UNK19C0 */ ++ xf_emit(ctx, 1, 0); /* 00000001 LOGIC_OP_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 0000000f LOGIC_OP */ ++ if (dev_priv->chipset >= 0xa0) ++ xf_emit(ctx, 1, 0); /* 00000001 UNK12E4? NVA3+ only? */ ++ if (IS_NVA3F(dev_priv->chipset)) { ++ xf_emit(ctx, 8, 1); /* 00000001 IBLEND_UNK00 */ ++ xf_emit(ctx, 8, 1); /* 00000007 IBLEND_EQUATION_RGB */ ++ xf_emit(ctx, 8, 2); /* 0000001f IBLEND_FUNC_SRC_RGB */ ++ xf_emit(ctx, 8, 1); /* 0000001f IBLEND_FUNC_DST_RGB */ ++ xf_emit(ctx, 8, 1); /* 00000007 IBLEND_EQUATION_ALPHA */ ++ xf_emit(ctx, 8, 2); /* 0000001f IBLEND_FUNC_SRC_ALPHA */ ++ xf_emit(ctx, 8, 1); /* 0000001f IBLEND_FUNC_DST_ALPHA */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK15C4 */ ++ xf_emit(ctx, 1, 0); /* 00000001 */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1140 */ + } +- xf_emit(ctx, 1, 0x11); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 0x5b, 0); ++ xf_emit(ctx, 1, 0x11); /* 3f/7f DST_FORMAT */ ++ xf_emit(ctx, 1, 1); /* 00000001 DST_LINEAR */ ++ xf_emit(ctx, 1, 0); /* 00000007 PATTERN_COLOR_FORMAT */ ++ xf_emit(ctx, 2, 0); /* ffffffff PATTERN_MONO_COLOR */ ++ xf_emit(ctx, 1, 0); /* 00000001 PATTERN_MONO_FORMAT */ ++ xf_emit(ctx, 2, 0); /* ffffffff PATTERN_MONO_BITMAP */ ++ xf_emit(ctx, 1, 0); /* 00000003 PATTERN_SELECT */ ++ xf_emit(ctx, 1, 0); /* 000000ff ROP */ ++ xf_emit(ctx, 1, 0); /* ffffffff BETA1 */ ++ xf_emit(ctx, 1, 0); /* ffffffff BETA4 */ ++ xf_emit(ctx, 1, 0); /* 00000007 OPERATION */ ++ xf_emit(ctx, 0x50, 0); /* 10x ffffff, ffffff, ffffff, ffffff, 3 PATTERN */ + } + + static void +-nv50_graph_construct_xfer_tp_x1(struct nouveau_grctx *ctx) ++nv50_graph_construct_xfer_unk84xx(struct nouveau_grctx *ctx) + { + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; + int magic3; +- if (dev_priv->chipset == 0x50) ++ switch (dev_priv->chipset) { ++ case 0x50: + magic3 = 0x1000; +- else if (dev_priv->chipset == 0x86 || dev_priv->chipset == 0x98 || dev_priv->chipset >= 0xa8) ++ break; ++ case 0x86: ++ case 0x98: ++ case 0xa8: ++ case 0xaa: ++ case 0xac: ++ case 0xaf: + magic3 = 0x1e00; +- else ++ break; ++ default: + magic3 = 0; +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 4); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) +- xf_emit(ctx, 0x24, 0); ++ } ++ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ ++ xf_emit(ctx, 1, 4); /* 7f/ff[NVA0+] VP_REG_ALLOC_RESULT */ ++ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ ++ xf_emit(ctx, 1, 0); /* 111/113[NVA0+] */ ++ if (IS_NVA3F(dev_priv->chipset)) ++ xf_emit(ctx, 0x1f, 0); /* ffffffff */ + else if (dev_priv->chipset >= 0xa0) +- xf_emit(ctx, 0x14, 0); ++ xf_emit(ctx, 0x0f, 0); /* ffffffff */ + else +- xf_emit(ctx, 0x15, 0); +- xf_emit(ctx, 2, 4); ++ xf_emit(ctx, 0x10, 0); /* fffffff VP_RESULT_MAP_1 up */ ++ xf_emit(ctx, 2, 0); /* f/1f[NVA3], fffffff/ffffffff[NVA0+] */ ++ xf_emit(ctx, 1, 4); /* 7f/ff VP_REG_ALLOC_RESULT */ ++ xf_emit(ctx, 1, 4); /* 7f/ff VP_RESULT_MAP_SIZE */ + if (dev_priv->chipset >= 0xa0) +- xf_emit(ctx, 1, 0x03020100); ++ xf_emit(ctx, 1, 0x03020100); /* ffffffff */ + else +- xf_emit(ctx, 1, 0x00608080); +- xf_emit(ctx, 4, 0); +- xf_emit(ctx, 1, 4); +- xf_emit(ctx, 2, 0); +- xf_emit(ctx, 2, 4); +- xf_emit(ctx, 1, 0x80); ++ xf_emit(ctx, 1, 0x00608080); /* fffffff VP_RESULT_MAP_0 */ ++ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ ++ xf_emit(ctx, 2, 0); /* 111/113, 7f/ff */ ++ xf_emit(ctx, 1, 4); /* 7f/ff VP_RESULT_MAP_SIZE */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ ++ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ ++ xf_emit(ctx, 1, 4); /* 000000ff GP_REG_ALLOC_RESULT */ ++ xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */ ++ xf_emit(ctx, 1, 0x80); /* 0000ffff GP_VERTEX_OUTPUT_COUNT */ + if (magic3) +- xf_emit(ctx, 1, magic3); +- xf_emit(ctx, 1, 4); +- xf_emit(ctx, 0x24, 0); +- xf_emit(ctx, 1, 4); +- xf_emit(ctx, 1, 0x80); +- xf_emit(ctx, 1, 4); +- xf_emit(ctx, 1, 0x03020100); +- xf_emit(ctx, 1, 3); ++ xf_emit(ctx, 1, magic3); /* 00007fff tesla UNK141C */ ++ xf_emit(ctx, 1, 4); /* 7f/ff VP_RESULT_MAP_SIZE */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ ++ xf_emit(ctx, 1, 0); /* 111/113 */ ++ xf_emit(ctx, 0x1f, 0); /* ffffffff GP_RESULT_MAP_1 up */ ++ xf_emit(ctx, 1, 0); /* 0000001f */ ++ xf_emit(ctx, 1, 0); /* ffffffff */ ++ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ ++ xf_emit(ctx, 1, 4); /* 000000ff GP_REG_ALLOC_RESULT */ ++ xf_emit(ctx, 1, 0x80); /* 0000ffff GP_VERTEX_OUTPUT_COUNT */ ++ xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */ ++ xf_emit(ctx, 1, 0x03020100); /* ffffffff GP_RESULT_MAP_0 */ ++ xf_emit(ctx, 1, 3); /* 00000003 GP_OUTPUT_PRIMITIVE_TYPE */ + if (magic3) +- xf_emit(ctx, 1, magic3); +- xf_emit(ctx, 1, 4); +- xf_emit(ctx, 4, 0); +- xf_emit(ctx, 1, 4); +- xf_emit(ctx, 1, 3); +- xf_emit(ctx, 3, 0); +- xf_emit(ctx, 1, 4); ++ xf_emit(ctx, 1, magic3); /* 7fff tesla UNK141C */ ++ xf_emit(ctx, 1, 4); /* 7f/ff VP_RESULT_MAP_SIZE */ ++ xf_emit(ctx, 1, 0); /* 00000001 PROVOKING_VERTEX_LAST */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ ++ xf_emit(ctx, 1, 0); /* 111/113 */ ++ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ ++ xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */ ++ xf_emit(ctx, 1, 3); /* 00000003 GP_OUTPUT_PRIMITIVE_TYPE */ ++ xf_emit(ctx, 1, 0); /* 00000001 PROVOKING_VERTEX_LAST */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ ++ xf_emit(ctx, 1, 0); /* 00000003 tesla UNK13A0 */ ++ xf_emit(ctx, 1, 4); /* 7f/ff VP_REG_ALLOC_RESULT */ ++ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ ++ xf_emit(ctx, 1, 0); /* 111/113 */ + if (dev_priv->chipset == 0x94 || dev_priv->chipset == 0x96) +- xf_emit(ctx, 0x1024, 0); ++ xf_emit(ctx, 0x1020, 0); /* 4 x (0x400 x 0xffffffff, ff, 0, 0, 0, 4 x ffffffff) */ + else if (dev_priv->chipset < 0xa0) +- xf_emit(ctx, 0xa24, 0); +- else if (dev_priv->chipset == 0xa0 || dev_priv->chipset >= 0xaa) +- xf_emit(ctx, 0x214, 0); ++ xf_emit(ctx, 0xa20, 0); /* 4 x (0x280 x 0xffffffff, ff, 0, 0, 0, 4 x ffffffff) */ ++ else if (!IS_NVA3F(dev_priv->chipset)) ++ xf_emit(ctx, 0x210, 0); /* ffffffff */ + else +- xf_emit(ctx, 0x414, 0); +- xf_emit(ctx, 1, 4); +- xf_emit(ctx, 1, 3); +- xf_emit(ctx, 2, 0); ++ xf_emit(ctx, 0x410, 0); /* ffffffff */ ++ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ ++ xf_emit(ctx, 1, 4); /* 000000ff GP_RESULT_MAP_SIZE */ ++ xf_emit(ctx, 1, 3); /* 00000003 GP_OUTPUT_PRIMITIVE_TYPE */ ++ xf_emit(ctx, 1, 0); /* 00000001 PROVOKING_VERTEX_LAST */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ + } + + static void +-nv50_graph_construct_xfer_tp_x2(struct nouveau_grctx *ctx) ++nv50_graph_construct_xfer_tprop(struct nouveau_grctx *ctx) + { + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; + int magic1, magic2; + if (dev_priv->chipset == 0x50) { + magic1 = 0x3ff; + magic2 = 0x00003e60; +- } else if (dev_priv->chipset <= 0xa0 || dev_priv->chipset >= 0xaa) { ++ } else if (!IS_NVA3F(dev_priv->chipset)) { + magic1 = 0x7ff; + magic2 = 0x001ffe67; + } else { + magic1 = 0x7ff; + magic2 = 0x00087e67; + } +- xf_emit(ctx, 3, 0); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 0xc, 0); +- xf_emit(ctx, 1, 0xf); +- xf_emit(ctx, 0xb, 0); +- xf_emit(ctx, 1, 4); +- xf_emit(ctx, 4, 0xffff); +- xf_emit(ctx, 8, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 3, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 5, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 2, 0); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) { +- xf_emit(ctx, 1, 3); +- xf_emit(ctx, 1, 0); +- } else if (dev_priv->chipset >= 0xa0) +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 0xa, 0); +- xf_emit(ctx, 2, 1); +- xf_emit(ctx, 1, 2); +- xf_emit(ctx, 2, 1); +- xf_emit(ctx, 1, 2); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) { +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 0x18, 1); +- xf_emit(ctx, 8, 2); +- xf_emit(ctx, 8, 1); +- xf_emit(ctx, 8, 2); +- xf_emit(ctx, 8, 1); +- xf_emit(ctx, 1, 0); ++ xf_emit(ctx, 1, 0); /* 00000007 ALPHA_TEST_FUNC */ ++ xf_emit(ctx, 1, 0); /* ffffffff ALPHA_TEST_REF */ ++ xf_emit(ctx, 1, 0); /* 00000001 ALPHA_TEST_ENABLE */ ++ if (IS_NVA3F(dev_priv->chipset)) ++ xf_emit(ctx, 1, 1); /* 0000000f UNK16A0 */ ++ xf_emit(ctx, 1, 0); /* 7/f MULTISAMPLE_SAMPLES_LOG2 */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */ ++ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_BACK_MASK */ ++ xf_emit(ctx, 3, 0); /* 00000007 STENCIL_BACK_OP_FAIL, ZFAIL, ZPASS */ ++ xf_emit(ctx, 4, 0); /* ffffffff BLEND_COLOR */ ++ xf_emit(ctx, 1, 0); /* 00000001 UNK19C0 */ ++ xf_emit(ctx, 1, 0); /* 00000001 UNK0FDC */ ++ xf_emit(ctx, 1, 0xf); /* 0000000f COLOR_MASK */ ++ xf_emit(ctx, 7, 0); /* 0000000f COLOR_MASK */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000001 LOGIC_OP_ENABLE */ ++ xf_emit(ctx, 1, 0); /* ff[NV50]/3ff[NV84+] */ ++ xf_emit(ctx, 1, 4); /* 00000007 FP_CONTROL */ ++ xf_emit(ctx, 4, 0xffff); /* 0000ffff MSAA_MASK */ ++ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_MASK */ ++ xf_emit(ctx, 3, 0); /* 00000007 STENCIL_FRONT_OP_FAIL, ZFAIL, ZPASS */ ++ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_BACK_ENABLE */ ++ xf_emit(ctx, 2, 0); /* 00007fff WINDOW_OFFSET_XY */ ++ xf_emit(ctx, 1, 1); /* 00000001 tesla UNK19CC */ ++ xf_emit(ctx, 1, 0); /* 7 */ ++ xf_emit(ctx, 1, 0); /* 00000001 SAMPLECNT_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */ ++ xf_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */ ++ xf_emit(ctx, 1, 0); /* ffffffff COLOR_KEY */ ++ xf_emit(ctx, 1, 0); /* 00000001 COLOR_KEY_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000007 COLOR_KEY_FORMAT */ ++ xf_emit(ctx, 2, 0); /* ffffffff SIFC_BITMAP_COLOR */ ++ xf_emit(ctx, 1, 1); /* 00000001 SIFC_BITMAP_WRITE_BIT0_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000007 ALPHA_TEST_FUNC */ ++ xf_emit(ctx, 1, 0); /* 00000001 ALPHA_TEST_ENABLE */ ++ if (IS_NVA3F(dev_priv->chipset)) { ++ xf_emit(ctx, 1, 3); /* 00000003 tesla UNK16B4 */ ++ xf_emit(ctx, 1, 0); /* 00000003 */ ++ xf_emit(ctx, 1, 0); /* 00000003 tesla UNK1298 */ ++ } else if (dev_priv->chipset >= 0xa0) { ++ xf_emit(ctx, 1, 1); /* 00000001 tesla UNK16B4 */ ++ xf_emit(ctx, 1, 0); /* 00000003 */ ++ } else { ++ xf_emit(ctx, 1, 0); /* 00000003 MULTISAMPLE_CTRL */ + } +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 0x11); +- xf_emit(ctx, 7, 0); +- xf_emit(ctx, 1, 0x0fac6881); +- xf_emit(ctx, 2, 0); +- xf_emit(ctx, 1, 4); +- xf_emit(ctx, 3, 0); +- xf_emit(ctx, 1, 0x11); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 3, 0xcf); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 0xa, 0); +- xf_emit(ctx, 2, 1); +- xf_emit(ctx, 1, 2); +- xf_emit(ctx, 2, 1); +- xf_emit(ctx, 1, 2); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 8, 1); +- xf_emit(ctx, 1, 0x11); +- xf_emit(ctx, 7, 0); +- xf_emit(ctx, 1, 0x0fac6881); +- xf_emit(ctx, 1, 0xf); +- xf_emit(ctx, 7, 0); +- xf_emit(ctx, 1, magic2); +- xf_emit(ctx, 2, 0); +- xf_emit(ctx, 1, 0x11); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) +- xf_emit(ctx, 2, 1); +- else +- xf_emit(ctx, 1, 1); ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */ ++ xf_emit(ctx, 8, 0); /* 00000001 BLEND_ENABLE */ ++ xf_emit(ctx, 1, 1); /* 0000001f BLEND_FUNC_DST_ALPHA */ ++ xf_emit(ctx, 1, 1); /* 00000007 BLEND_EQUATION_ALPHA */ ++ xf_emit(ctx, 1, 2); /* 0000001f BLEND_FUNC_SRC_ALPHA */ ++ xf_emit(ctx, 1, 1); /* 0000001f BLEND_FUNC_DST_RGB */ ++ xf_emit(ctx, 1, 1); /* 00000007 BLEND_EQUATION_RGB */ ++ xf_emit(ctx, 1, 2); /* 0000001f BLEND_FUNC_SRC_RGB */ ++ if (IS_NVA3F(dev_priv->chipset)) { ++ xf_emit(ctx, 1, 0); /* 00000001 UNK12E4 */ ++ xf_emit(ctx, 8, 1); /* 00000007 IBLEND_EQUATION_RGB */ ++ xf_emit(ctx, 8, 1); /* 00000007 IBLEND_EQUATION_ALPHA */ ++ xf_emit(ctx, 8, 1); /* 00000001 IBLEND_UNK00 */ ++ xf_emit(ctx, 8, 2); /* 0000001f IBLEND_SRC_RGB */ ++ xf_emit(ctx, 8, 1); /* 0000001f IBLEND_DST_RGB */ ++ xf_emit(ctx, 8, 2); /* 0000001f IBLEND_SRC_ALPHA */ ++ xf_emit(ctx, 8, 1); /* 0000001f IBLEND_DST_ALPHA */ ++ xf_emit(ctx, 1, 0); /* 00000001 UNK1140 */ ++ } ++ xf_emit(ctx, 1, 1); /* 00000001 UNK133C */ ++ xf_emit(ctx, 1, 0); /* ffff0ff3 */ ++ xf_emit(ctx, 1, 0x11); /* 3f/7f RT_FORMAT */ ++ xf_emit(ctx, 7, 0); /* 3f/7f RT_FORMAT */ ++ xf_emit(ctx, 1, 0x0fac6881); /* 0fffffff RT_CONTROL */ ++ xf_emit(ctx, 1, 0); /* 00000001 LOGIC_OP_ENABLE */ ++ xf_emit(ctx, 1, 0); /* ff/3ff */ ++ xf_emit(ctx, 1, 4); /* 00000007 FP_CONTROL */ ++ xf_emit(ctx, 1, 0); /* 00000003 UNK0F90 */ ++ xf_emit(ctx, 1, 0); /* 00000001 FRAMEBUFFER_SRGB */ ++ xf_emit(ctx, 1, 0); /* 7 */ ++ xf_emit(ctx, 1, 0x11); /* 3f/7f DST_FORMAT */ ++ xf_emit(ctx, 1, 1); /* 00000001 DST_LINEAR */ ++ xf_emit(ctx, 1, 0); /* 00000007 OPERATION */ ++ xf_emit(ctx, 1, 0xcf); /* 000000ff SIFC_FORMAT */ ++ xf_emit(ctx, 1, 0xcf); /* 000000ff DRAW_COLOR_FORMAT */ ++ xf_emit(ctx, 1, 0xcf); /* 000000ff SRC_FORMAT */ ++ if (IS_NVA3F(dev_priv->chipset)) ++ xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */ ++ xf_emit(ctx, 1, 0); /* 7/f[NVA3] MULTISAMPLE_SAMPLES_LOG2 */ ++ xf_emit(ctx, 8, 0); /* 00000001 BLEND_ENABLE */ ++ xf_emit(ctx, 1, 1); /* 0000001f BLEND_FUNC_DST_ALPHA */ ++ xf_emit(ctx, 1, 1); /* 00000007 BLEND_EQUATION_ALPHA */ ++ xf_emit(ctx, 1, 2); /* 0000001f BLEND_FUNC_SRC_ALPHA */ ++ xf_emit(ctx, 1, 1); /* 0000001f BLEND_FUNC_DST_RGB */ ++ xf_emit(ctx, 1, 1); /* 00000007 BLEND_EQUATION_RGB */ ++ xf_emit(ctx, 1, 2); /* 0000001f BLEND_FUNC_SRC_RGB */ ++ xf_emit(ctx, 1, 1); /* 00000001 UNK133C */ ++ xf_emit(ctx, 1, 0); /* ffff0ff3 */ ++ xf_emit(ctx, 8, 1); /* 00000001 UNK19E0 */ ++ xf_emit(ctx, 1, 0x11); /* 3f/7f RT_FORMAT */ ++ xf_emit(ctx, 7, 0); /* 3f/7f RT_FORMAT */ ++ xf_emit(ctx, 1, 0x0fac6881); /* 0fffffff RT_CONTROL */ ++ xf_emit(ctx, 1, 0xf); /* 0000000f COLOR_MASK */ ++ xf_emit(ctx, 7, 0); /* 0000000f COLOR_MASK */ ++ xf_emit(ctx, 1, magic2); /* 001fffff tesla UNK0F78 */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_BOUNDS_EN */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */ ++ xf_emit(ctx, 1, 0x11); /* 3f/7f DST_FORMAT */ ++ xf_emit(ctx, 1, 1); /* 00000001 DST_LINEAR */ ++ if (IS_NVA3F(dev_priv->chipset)) ++ xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */ + if(dev_priv->chipset == 0x50) +- xf_emit(ctx, 1, 0); ++ xf_emit(ctx, 1, 0); /* ff */ + else +- xf_emit(ctx, 3, 0); +- xf_emit(ctx, 1, 4); +- xf_emit(ctx, 5, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 4, 0); +- xf_emit(ctx, 1, 0x11); +- xf_emit(ctx, 7, 0); +- xf_emit(ctx, 1, 0x0fac6881); +- xf_emit(ctx, 3, 0); +- xf_emit(ctx, 1, 0x11); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, magic1); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 2, 0); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 0x28, 0); +- xf_emit(ctx, 8, 8); +- xf_emit(ctx, 1, 0x11); +- xf_emit(ctx, 7, 0); +- xf_emit(ctx, 1, 0x0fac6881); +- xf_emit(ctx, 8, 0x400); +- xf_emit(ctx, 8, 0x300); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 0xf); +- xf_emit(ctx, 7, 0); +- xf_emit(ctx, 1, 0x20); +- xf_emit(ctx, 1, 0x11); +- xf_emit(ctx, 1, 0x100); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 2, 0); +- xf_emit(ctx, 1, 0x40); +- xf_emit(ctx, 1, 0x100); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 3); +- xf_emit(ctx, 4, 0); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, magic2); +- xf_emit(ctx, 3, 0); +- xf_emit(ctx, 1, 2); +- xf_emit(ctx, 1, 0x0fac6881); +- xf_emit(ctx, 9, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 4, 0); +- xf_emit(ctx, 1, 4); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 0x400); +- xf_emit(ctx, 1, 0x300); +- xf_emit(ctx, 1, 0x1001); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) +- xf_emit(ctx, 4, 0); +- else +- xf_emit(ctx, 3, 0); +- xf_emit(ctx, 1, 0x11); +- xf_emit(ctx, 7, 0); +- xf_emit(ctx, 1, 0x0fac6881); +- xf_emit(ctx, 1, 0xf); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) { +- xf_emit(ctx, 0x15, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 3, 0); +- } else +- xf_emit(ctx, 0x17, 0); ++ xf_emit(ctx, 3, 0); /* 1, 7, 3ff */ ++ xf_emit(ctx, 1, 4); /* 00000007 FP_CONTROL */ ++ xf_emit(ctx, 1, 0); /* 00000003 UNK0F90 */ ++ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000007 */ ++ xf_emit(ctx, 1, 0); /* 00000001 SAMPLECNT_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */ ++ xf_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */ ++ xf_emit(ctx, 1, 0); /* 7/f MULTISAMPLE_SAMPLES_LOG2 */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */ ++ xf_emit(ctx, 1, 0); /* ffff0ff3 */ ++ xf_emit(ctx, 1, 0x11); /* 3f/7f RT_FORMAT */ ++ xf_emit(ctx, 7, 0); /* 3f/7f RT_FORMAT */ ++ xf_emit(ctx, 1, 0x0fac6881); /* 0fffffff RT_CONTROL */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_BOUNDS_EN */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE_ENABLE */ ++ xf_emit(ctx, 1, 0x11); /* 3f/7f DST_FORMAT */ ++ xf_emit(ctx, 1, 1); /* 00000001 DST_LINEAR */ ++ xf_emit(ctx, 1, 0); /* 000fffff BLIT_DU_DX_FRACT */ ++ xf_emit(ctx, 1, 1); /* 0001ffff BLIT_DU_DX_INT */ ++ xf_emit(ctx, 1, 0); /* 000fffff BLIT_DV_DY_FRACT */ ++ xf_emit(ctx, 1, 1); /* 0001ffff BLIT_DV_DY_INT */ ++ xf_emit(ctx, 1, 0); /* ff/3ff */ ++ xf_emit(ctx, 1, magic1); /* 3ff/7ff tesla UNK0D68 */ ++ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */ ++ xf_emit(ctx, 1, 1); /* 00000001 tesla UNK15B4 */ ++ xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */ ++ xf_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000007 */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */ ++ if (IS_NVA3F(dev_priv->chipset)) ++ xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */ ++ xf_emit(ctx, 8, 0); /* 0000ffff DMA_COLOR */ ++ xf_emit(ctx, 1, 0); /* 0000ffff DMA_GLOBAL */ ++ xf_emit(ctx, 1, 0); /* 0000ffff DMA_LOCAL */ ++ xf_emit(ctx, 1, 0); /* 0000ffff DMA_STACK */ ++ xf_emit(ctx, 1, 0); /* ff/3ff */ ++ xf_emit(ctx, 1, 0); /* 0000ffff DMA_DST */ ++ xf_emit(ctx, 1, 0); /* 7 */ ++ xf_emit(ctx, 1, 0); /* 7/f MULTISAMPLE_SAMPLES_LOG2 */ ++ xf_emit(ctx, 1, 0); /* ffff0ff3 */ ++ xf_emit(ctx, 8, 0); /* 000000ff RT_ADDRESS_HIGH */ ++ xf_emit(ctx, 8, 0); /* ffffffff RT_LAYER_STRIDE */ ++ xf_emit(ctx, 8, 0); /* ffffffff RT_ADDRESS_LOW */ ++ xf_emit(ctx, 8, 8); /* 0000007f RT_TILE_MODE */ ++ xf_emit(ctx, 1, 0x11); /* 3f/7f RT_FORMAT */ ++ xf_emit(ctx, 7, 0); /* 3f/7f RT_FORMAT */ ++ xf_emit(ctx, 1, 0x0fac6881); /* 0fffffff RT_CONTROL */ ++ xf_emit(ctx, 8, 0x400); /* 0fffffff RT_HORIZ */ ++ xf_emit(ctx, 8, 0x300); /* 0000ffff RT_VERT */ ++ xf_emit(ctx, 1, 1); /* 00001fff RT_ARRAY_MODE */ ++ xf_emit(ctx, 1, 0xf); /* 0000000f COLOR_MASK */ ++ xf_emit(ctx, 7, 0); /* 0000000f COLOR_MASK */ ++ xf_emit(ctx, 1, 0x20); /* 00000fff DST_TILE_MODE */ ++ xf_emit(ctx, 1, 0x11); /* 3f/7f DST_FORMAT */ ++ xf_emit(ctx, 1, 0x100); /* 0001ffff DST_HEIGHT */ ++ xf_emit(ctx, 1, 0); /* 000007ff DST_LAYER */ ++ xf_emit(ctx, 1, 1); /* 00000001 DST_LINEAR */ ++ xf_emit(ctx, 1, 0); /* ffffffff DST_ADDRESS_LOW */ ++ xf_emit(ctx, 1, 0); /* 000000ff DST_ADDRESS_HIGH */ ++ xf_emit(ctx, 1, 0x40); /* 0007ffff DST_PITCH */ ++ xf_emit(ctx, 1, 0x100); /* 0001ffff DST_WIDTH */ ++ xf_emit(ctx, 1, 0); /* 0000ffff */ ++ xf_emit(ctx, 1, 3); /* 00000003 tesla UNK15AC */ ++ xf_emit(ctx, 1, 0); /* ff/3ff */ ++ xf_emit(ctx, 1, 0); /* 0001ffff GP_BUILTIN_RESULT_EN */ ++ xf_emit(ctx, 1, 0); /* 00000003 UNK0F90 */ ++ xf_emit(ctx, 1, 0); /* 00000007 */ ++ if (IS_NVA3F(dev_priv->chipset)) ++ xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */ ++ xf_emit(ctx, 1, magic2); /* 001fffff tesla UNK0F78 */ ++ xf_emit(ctx, 1, 0); /* 7/f MULTISAMPLE_SAMPLES_LOG2 */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */ ++ xf_emit(ctx, 1, 0); /* ffff0ff3 */ ++ xf_emit(ctx, 1, 2); /* 00000003 tesla UNK143C */ ++ xf_emit(ctx, 1, 0x0fac6881); /* 0fffffff RT_CONTROL */ ++ xf_emit(ctx, 1, 0); /* 0000ffff DMA_ZETA */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_BOUNDS_EN */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE_ENABLE */ ++ xf_emit(ctx, 2, 0); /* ffff, ff/3ff */ ++ xf_emit(ctx, 1, 0); /* 0001ffff GP_BUILTIN_RESULT_EN */ ++ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 000000ff STENCIL_FRONT_MASK */ ++ xf_emit(ctx, 1, 1); /* 00000001 tesla UNK15B4 */ ++ xf_emit(ctx, 1, 0); /* 00000007 */ ++ xf_emit(ctx, 1, 0); /* ffffffff ZETA_LAYER_STRIDE */ ++ xf_emit(ctx, 1, 0); /* 000000ff ZETA_ADDRESS_HIGH */ ++ xf_emit(ctx, 1, 0); /* ffffffff ZETA_ADDRESS_LOW */ ++ xf_emit(ctx, 1, 4); /* 00000007 ZETA_TILE_MODE */ ++ xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */ ++ xf_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */ ++ xf_emit(ctx, 1, 0x400); /* 0fffffff ZETA_HORIZ */ ++ xf_emit(ctx, 1, 0x300); /* 0000ffff ZETA_VERT */ ++ xf_emit(ctx, 1, 0x1001); /* 00001fff ZETA_ARRAY_MODE */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */ ++ xf_emit(ctx, 1, 0); /* 7/f MULTISAMPLE_SAMPLES_LOG2 */ ++ if (IS_NVA3F(dev_priv->chipset)) ++ xf_emit(ctx, 1, 0); /* 00000001 */ ++ xf_emit(ctx, 1, 0); /* ffff0ff3 */ ++ xf_emit(ctx, 1, 0x11); /* 3f/7f RT_FORMAT */ ++ xf_emit(ctx, 7, 0); /* 3f/7f RT_FORMAT */ ++ xf_emit(ctx, 1, 0x0fac6881); /* 0fffffff RT_CONTROL */ ++ xf_emit(ctx, 1, 0xf); /* 0000000f COLOR_MASK */ ++ xf_emit(ctx, 7, 0); /* 0000000f COLOR_MASK */ ++ xf_emit(ctx, 1, 0); /* ff/3ff */ ++ xf_emit(ctx, 8, 0); /* 00000001 BLEND_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000003 UNK0F90 */ ++ xf_emit(ctx, 1, 0); /* 00000001 FRAMEBUFFER_SRGB */ ++ xf_emit(ctx, 1, 0); /* 7 */ ++ xf_emit(ctx, 1, 0); /* 00000001 LOGIC_OP_ENABLE */ ++ if (IS_NVA3F(dev_priv->chipset)) { ++ xf_emit(ctx, 1, 0); /* 00000001 UNK1140 */ ++ xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */ ++ } ++ xf_emit(ctx, 1, 0); /* 7/f MULTISAMPLE_SAMPLES_LOG2 */ ++ xf_emit(ctx, 1, 0); /* 00000001 UNK1534 */ ++ xf_emit(ctx, 1, 0); /* ffff0ff3 */ + if (dev_priv->chipset >= 0xa0) +- xf_emit(ctx, 1, 0x0fac6881); +- xf_emit(ctx, 1, magic2); +- xf_emit(ctx, 3, 0); +- xf_emit(ctx, 1, 0x11); +- xf_emit(ctx, 2, 0); +- xf_emit(ctx, 1, 4); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 2, 1); +- xf_emit(ctx, 3, 0); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) +- xf_emit(ctx, 2, 1); +- else +- xf_emit(ctx, 1, 1); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) +- xf_emit(ctx, 2, 0); +- else if (dev_priv->chipset != 0x50) +- xf_emit(ctx, 1, 0); ++ xf_emit(ctx, 1, 0x0fac6881); /* fffffff */ ++ xf_emit(ctx, 1, magic2); /* 001fffff tesla UNK0F78 */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_BOUNDS_EN */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE_ENABLE */ ++ xf_emit(ctx, 1, 0x11); /* 3f/7f DST_FORMAT */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK0FB0 */ ++ xf_emit(ctx, 1, 0); /* ff/3ff */ ++ xf_emit(ctx, 1, 4); /* 00000007 FP_CONTROL */ ++ xf_emit(ctx, 1, 0); /* 00000001 STENCIL_FRONT_ENABLE */ ++ xf_emit(ctx, 1, 1); /* 00000001 tesla UNK15B4 */ ++ xf_emit(ctx, 1, 1); /* 00000001 tesla UNK19CC */ ++ xf_emit(ctx, 1, 0); /* 00000007 */ ++ xf_emit(ctx, 1, 0); /* 00000001 SAMPLECNT_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 0000000f ZETA_FORMAT */ ++ xf_emit(ctx, 1, 1); /* 00000001 ZETA_ENABLE */ ++ if (IS_NVA3F(dev_priv->chipset)) { ++ xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */ ++ xf_emit(ctx, 1, 0); /* 0000000f tesla UNK15C8 */ ++ } ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A3C */ ++ if (dev_priv->chipset >= 0xa0) { ++ xf_emit(ctx, 3, 0); /* 7/f, 1, ffff0ff3 */ ++ xf_emit(ctx, 1, 0xfac6881); /* fffffff */ ++ xf_emit(ctx, 4, 0); /* 1, 1, 1, 3ff */ ++ xf_emit(ctx, 1, 4); /* 7 */ ++ xf_emit(ctx, 1, 0); /* 1 */ ++ xf_emit(ctx, 2, 1); /* 1 */ ++ xf_emit(ctx, 2, 0); /* 7, f */ ++ xf_emit(ctx, 1, 1); /* 1 */ ++ xf_emit(ctx, 1, 0); /* 7/f */ ++ if (IS_NVA3F(dev_priv->chipset)) ++ xf_emit(ctx, 0x9, 0); /* 1 */ ++ else ++ xf_emit(ctx, 0x8, 0); /* 1 */ ++ xf_emit(ctx, 1, 0); /* ffff0ff3 */ ++ xf_emit(ctx, 8, 1); /* 1 */ ++ xf_emit(ctx, 1, 0x11); /* 7f */ ++ xf_emit(ctx, 7, 0); /* 7f */ ++ xf_emit(ctx, 1, 0xfac6881); /* fffffff */ ++ xf_emit(ctx, 1, 0xf); /* f */ ++ xf_emit(ctx, 7, 0); /* f */ ++ xf_emit(ctx, 1, 0x11); /* 7f */ ++ xf_emit(ctx, 1, 1); /* 1 */ ++ xf_emit(ctx, 5, 0); /* 1, 7, 3ff, 3, 7 */ ++ if (IS_NVA3F(dev_priv->chipset)) { ++ xf_emit(ctx, 1, 0); /* 00000001 UNK1140 */ ++ xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */ ++ } ++ } + } + + static void +-nv50_graph_construct_xfer_tp_x3(struct nouveau_grctx *ctx) ++nv50_graph_construct_xfer_tex(struct nouveau_grctx *ctx) + { + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; +- xf_emit(ctx, 3, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 1); ++ xf_emit(ctx, 2, 0); /* 1 LINKED_TSC. yes, 2. */ ++ if (dev_priv->chipset != 0x50) ++ xf_emit(ctx, 1, 0); /* 3 */ ++ xf_emit(ctx, 1, 1); /* 1ffff BLIT_DU_DX_INT */ ++ xf_emit(ctx, 1, 0); /* fffff BLIT_DU_DX_FRACT */ ++ xf_emit(ctx, 1, 1); /* 1ffff BLIT_DV_DY_INT */ ++ xf_emit(ctx, 1, 0); /* fffff BLIT_DV_DY_FRACT */ + if (dev_priv->chipset == 0x50) +- xf_emit(ctx, 2, 0); ++ xf_emit(ctx, 1, 0); /* 3 BLIT_CONTROL */ + else +- xf_emit(ctx, 3, 0); +- xf_emit(ctx, 1, 0x2a712488); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 0x4085c000); +- xf_emit(ctx, 1, 0x40); +- xf_emit(ctx, 1, 0x100); +- xf_emit(ctx, 1, 0x10100); +- xf_emit(ctx, 1, 0x02800000); ++ xf_emit(ctx, 2, 0); /* 3ff, 1 */ ++ xf_emit(ctx, 1, 0x2a712488); /* ffffffff SRC_TIC_0 */ ++ xf_emit(ctx, 1, 0); /* ffffffff SRC_TIC_1 */ ++ xf_emit(ctx, 1, 0x4085c000); /* ffffffff SRC_TIC_2 */ ++ xf_emit(ctx, 1, 0x40); /* ffffffff SRC_TIC_3 */ ++ xf_emit(ctx, 1, 0x100); /* ffffffff SRC_TIC_4 */ ++ xf_emit(ctx, 1, 0x10100); /* ffffffff SRC_TIC_5 */ ++ xf_emit(ctx, 1, 0x02800000); /* ffffffff SRC_TIC_6 */ ++ xf_emit(ctx, 1, 0); /* ffffffff SRC_TIC_7 */ ++ if (dev_priv->chipset == 0x50) { ++ xf_emit(ctx, 1, 0); /* 00000001 turing UNK358 */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A34? */ ++ xf_emit(ctx, 1, 0); /* 00000003 turing UNK37C tesla UNK1690 */ ++ xf_emit(ctx, 1, 0); /* 00000003 BLIT_CONTROL */ ++ xf_emit(ctx, 1, 0); /* 00000001 turing UNK32C tesla UNK0F94 */ ++ } else if (!IS_NVAAF(dev_priv->chipset)) { ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A34? */ ++ xf_emit(ctx, 1, 0); /* 00000003 */ ++ xf_emit(ctx, 1, 0); /* 000003ff */ ++ xf_emit(ctx, 1, 0); /* 00000003 */ ++ xf_emit(ctx, 1, 0); /* 000003ff */ ++ xf_emit(ctx, 1, 0); /* 00000003 tesla UNK1664 / turing UNK03E8 */ ++ xf_emit(ctx, 1, 0); /* 00000003 */ ++ xf_emit(ctx, 1, 0); /* 000003ff */ ++ } else { ++ xf_emit(ctx, 0x6, 0); ++ } ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A34 */ ++ xf_emit(ctx, 1, 0); /* 0000ffff DMA_TEXTURE */ ++ xf_emit(ctx, 1, 0); /* 0000ffff DMA_SRC */ + } + + static void +-nv50_graph_construct_xfer_tp_x4(struct nouveau_grctx *ctx) ++nv50_graph_construct_xfer_unk8cxx(struct nouveau_grctx *ctx) + { + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; +- xf_emit(ctx, 2, 0x04e3bfdf); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 0x00ffff00); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) +- xf_emit(ctx, 2, 1); +- else +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 2, 0); +- xf_emit(ctx, 1, 0x00ffff00); +- xf_emit(ctx, 8, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 0x30201000); +- xf_emit(ctx, 1, 0x70605040); +- xf_emit(ctx, 1, 0xb8a89888); +- xf_emit(ctx, 1, 0xf8e8d8c8); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 0x1a); +-} +- +-static void +-nv50_graph_construct_xfer_tp_x5(struct nouveau_grctx *ctx) +-{ +- struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; +- xf_emit(ctx, 3, 0); +- xf_emit(ctx, 1, 0xfac6881); +- xf_emit(ctx, 4, 0); +- xf_emit(ctx, 1, 4); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 2, 1); +- xf_emit(ctx, 2, 0); +- xf_emit(ctx, 1, 1); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) +- xf_emit(ctx, 0xb, 0); +- else +- xf_emit(ctx, 0xa, 0); +- xf_emit(ctx, 8, 1); +- xf_emit(ctx, 1, 0x11); +- xf_emit(ctx, 7, 0); +- xf_emit(ctx, 1, 0xfac6881); +- xf_emit(ctx, 1, 0xf); +- xf_emit(ctx, 7, 0); +- xf_emit(ctx, 1, 0x11); +- xf_emit(ctx, 1, 1); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) { +- xf_emit(ctx, 6, 0); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 6, 0); +- } else { +- xf_emit(ctx, 0xb, 0); +- } ++ xf_emit(ctx, 1, 0); /* 00000001 UNK1534 */ ++ xf_emit(ctx, 1, 0); /* 7/f MULTISAMPLE_SAMPLES_LOG2 */ ++ xf_emit(ctx, 2, 0); /* 7, ffff0ff3 */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE */ ++ xf_emit(ctx, 1, 0x04e3bfdf); /* ffffffff UNK0D64 */ ++ xf_emit(ctx, 1, 0x04e3bfdf); /* ffffffff UNK0DF4 */ ++ xf_emit(ctx, 1, 1); /* 00000001 UNK15B4 */ ++ xf_emit(ctx, 1, 0); /* 00000001 LINE_STIPPLE_ENABLE */ ++ xf_emit(ctx, 1, 0x00ffff00); /* 00ffffff LINE_STIPPLE_PATTERN */ ++ xf_emit(ctx, 1, 1); /* 00000001 tesla UNK0F98 */ ++ if (IS_NVA3F(dev_priv->chipset)) ++ xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */ ++ xf_emit(ctx, 1, 0); /* 00000003 tesla UNK1668 */ ++ xf_emit(ctx, 1, 0); /* 00000001 LINE_STIPPLE_ENABLE */ ++ xf_emit(ctx, 1, 0x00ffff00); /* 00ffffff LINE_STIPPLE_PATTERN */ ++ xf_emit(ctx, 1, 0); /* 00000001 POLYGON_SMOOTH_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000001 UNK1534 */ ++ xf_emit(ctx, 1, 0); /* 7/f MULTISAMPLE_SAMPLES_LOG2 */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1658 */ ++ xf_emit(ctx, 1, 0); /* 00000001 LINE_SMOOTH_ENABLE */ ++ xf_emit(ctx, 1, 0); /* ffff0ff3 */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_TEST_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000001 DEPTH_WRITE */ ++ xf_emit(ctx, 1, 1); /* 00000001 UNK15B4 */ ++ xf_emit(ctx, 1, 0); /* 00000001 POINT_SPRITE_ENABLE */ ++ xf_emit(ctx, 1, 1); /* 00000001 tesla UNK165C */ ++ xf_emit(ctx, 1, 0x30201000); /* ffffffff tesla UNK1670 */ ++ xf_emit(ctx, 1, 0x70605040); /* ffffffff tesla UNK1670 */ ++ xf_emit(ctx, 1, 0xb8a89888); /* ffffffff tesla UNK1670 */ ++ xf_emit(ctx, 1, 0xf8e8d8c8); /* ffffffff tesla UNK1670 */ ++ xf_emit(ctx, 1, 0); /* 00000001 VERTEX_TWO_SIDE_ENABLE */ ++ xf_emit(ctx, 1, 0x1a); /* 0000001f POLYGON_MODE */ + } + + static void +@@ -2193,108 +3102,136 @@ nv50_graph_construct_xfer_tp(struct nouveau_grctx *ctx) + { + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; + if (dev_priv->chipset < 0xa0) { +- nv50_graph_construct_xfer_tp_x1(ctx); +- nv50_graph_construct_xfer_tp_x2(ctx); +- nv50_graph_construct_xfer_tp_x3(ctx); +- if (dev_priv->chipset == 0x50) +- xf_emit(ctx, 0xf, 0); +- else +- xf_emit(ctx, 0x12, 0); +- nv50_graph_construct_xfer_tp_x4(ctx); ++ nv50_graph_construct_xfer_unk84xx(ctx); ++ nv50_graph_construct_xfer_tprop(ctx); ++ nv50_graph_construct_xfer_tex(ctx); ++ nv50_graph_construct_xfer_unk8cxx(ctx); + } else { +- nv50_graph_construct_xfer_tp_x3(ctx); +- if (dev_priv->chipset < 0xaa) +- xf_emit(ctx, 0xc, 0); +- else +- xf_emit(ctx, 0xa, 0); +- nv50_graph_construct_xfer_tp_x2(ctx); +- nv50_graph_construct_xfer_tp_x5(ctx); +- nv50_graph_construct_xfer_tp_x4(ctx); +- nv50_graph_construct_xfer_tp_x1(ctx); ++ nv50_graph_construct_xfer_tex(ctx); ++ nv50_graph_construct_xfer_tprop(ctx); ++ nv50_graph_construct_xfer_unk8cxx(ctx); ++ nv50_graph_construct_xfer_unk84xx(ctx); + } + } + + static void +-nv50_graph_construct_xfer_tp2(struct nouveau_grctx *ctx) ++nv50_graph_construct_xfer_mpc(struct nouveau_grctx *ctx) + { + struct drm_nouveau_private *dev_priv = ctx->dev->dev_private; +- int i, mpcnt; +- if (dev_priv->chipset == 0x98 || dev_priv->chipset == 0xaa) +- mpcnt = 1; +- else if (dev_priv->chipset < 0xa0 || dev_priv->chipset >= 0xa8) +- mpcnt = 2; +- else +- mpcnt = 3; ++ int i, mpcnt = 2; ++ switch (dev_priv->chipset) { ++ case 0x98: ++ case 0xaa: ++ mpcnt = 1; ++ break; ++ case 0x50: ++ case 0x84: ++ case 0x86: ++ case 0x92: ++ case 0x94: ++ case 0x96: ++ case 0xa8: ++ case 0xac: ++ mpcnt = 2; ++ break; ++ case 0xa0: ++ case 0xa3: ++ case 0xa5: ++ case 0xaf: ++ mpcnt = 3; ++ break; ++ } + for (i = 0; i < mpcnt; i++) { +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 0x80); +- xf_emit(ctx, 1, 0x80007004); +- xf_emit(ctx, 1, 0x04000400); ++ xf_emit(ctx, 1, 0); /* ff */ ++ xf_emit(ctx, 1, 0x80); /* ffffffff tesla UNK1404 */ ++ xf_emit(ctx, 1, 0x80007004); /* ffffffff tesla UNK12B0 */ ++ xf_emit(ctx, 1, 0x04000400); /* ffffffff */ + if (dev_priv->chipset >= 0xa0) +- xf_emit(ctx, 1, 0xc0); +- xf_emit(ctx, 1, 0x1000); +- xf_emit(ctx, 2, 0); +- if (dev_priv->chipset == 0x86 || dev_priv->chipset == 0x98 || dev_priv->chipset >= 0xa8) { +- xf_emit(ctx, 1, 0xe00); +- xf_emit(ctx, 1, 0x1e00); ++ xf_emit(ctx, 1, 0xc0); /* 00007fff tesla UNK152C */ ++ xf_emit(ctx, 1, 0x1000); /* 0000ffff tesla UNK0D60 */ ++ xf_emit(ctx, 1, 0); /* ff/3ff */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A30 */ ++ if (dev_priv->chipset == 0x86 || dev_priv->chipset == 0x98 || dev_priv->chipset == 0xa8 || IS_NVAAF(dev_priv->chipset)) { ++ xf_emit(ctx, 1, 0xe00); /* 7fff */ ++ xf_emit(ctx, 1, 0x1e00); /* 7fff */ + } +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 2, 0); ++ xf_emit(ctx, 1, 1); /* 000000ff VP_REG_ALLOC_TEMP */ ++ xf_emit(ctx, 1, 0); /* 00000001 LINKED_TSC */ ++ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ + if (dev_priv->chipset == 0x50) +- xf_emit(ctx, 2, 0x1000); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 4); +- xf_emit(ctx, 1, 2); +- if (dev_priv->chipset >= 0xaa) +- xf_emit(ctx, 0xb, 0); ++ xf_emit(ctx, 2, 0x1000); /* 7fff tesla UNK141C */ ++ xf_emit(ctx, 1, 1); /* 000000ff GP_REG_ALLOC_TEMP */ ++ xf_emit(ctx, 1, 0); /* 00000001 GP_ENABLE */ ++ xf_emit(ctx, 1, 4); /* 000000ff FP_REG_ALLOC_TEMP */ ++ xf_emit(ctx, 1, 2); /* 00000003 REG_MODE */ ++ if (IS_NVAAF(dev_priv->chipset)) ++ xf_emit(ctx, 0xb, 0); /* RO */ + else if (dev_priv->chipset >= 0xa0) +- xf_emit(ctx, 0xc, 0); ++ xf_emit(ctx, 0xc, 0); /* RO */ + else +- xf_emit(ctx, 0xa, 0); ++ xf_emit(ctx, 0xa, 0); /* RO */ + } +- xf_emit(ctx, 1, 0x08100c12); +- xf_emit(ctx, 1, 0); ++ xf_emit(ctx, 1, 0x08100c12); /* 1fffffff FP_INTERPOLANT_CTRL */ ++ xf_emit(ctx, 1, 0); /* ff/3ff */ + if (dev_priv->chipset >= 0xa0) { +- xf_emit(ctx, 1, 0x1fe21); ++ xf_emit(ctx, 1, 0x1fe21); /* 0003ffff tesla UNK0FAC */ + } +- xf_emit(ctx, 5, 0); +- xf_emit(ctx, 4, 0xffff); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 2, 0x10001); +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 0x1fe21); +- xf_emit(ctx, 1, 0); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) +- xf_emit(ctx, 1, 1); +- xf_emit(ctx, 4, 0); +- xf_emit(ctx, 1, 0x08100c12); +- xf_emit(ctx, 1, 4); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 2); +- xf_emit(ctx, 1, 0x11); +- xf_emit(ctx, 8, 0); +- xf_emit(ctx, 1, 0xfac6881); +- xf_emit(ctx, 1, 0); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) +- xf_emit(ctx, 1, 3); +- xf_emit(ctx, 3, 0); +- xf_emit(ctx, 1, 4); +- xf_emit(ctx, 9, 0); +- xf_emit(ctx, 1, 2); +- xf_emit(ctx, 2, 1); +- xf_emit(ctx, 1, 2); +- xf_emit(ctx, 3, 1); +- xf_emit(ctx, 1, 0); +- if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) { +- xf_emit(ctx, 8, 2); +- xf_emit(ctx, 0x10, 1); +- xf_emit(ctx, 8, 2); +- xf_emit(ctx, 0x18, 1); +- xf_emit(ctx, 3, 0); ++ xf_emit(ctx, 3, 0); /* 7fff, 0, 0 */ ++ xf_emit(ctx, 1, 0); /* 00000001 tesla UNK1534 */ ++ xf_emit(ctx, 1, 0); /* 7/f MULTISAMPLE_SAMPLES_LOG2 */ ++ xf_emit(ctx, 4, 0xffff); /* 0000ffff MSAA_MASK */ ++ xf_emit(ctx, 1, 1); /* 00000001 LANES32 */ ++ xf_emit(ctx, 1, 0x10001); /* 00ffffff BLOCK_ALLOC */ ++ xf_emit(ctx, 1, 0x10001); /* ffffffff BLOCKDIM_XY */ ++ xf_emit(ctx, 1, 1); /* 0000ffff BLOCKDIM_Z */ ++ xf_emit(ctx, 1, 0); /* ffffffff SHARED_SIZE */ ++ xf_emit(ctx, 1, 0x1fe21); /* 1ffff/3ffff[NVA0+] tesla UNk0FAC */ ++ xf_emit(ctx, 1, 0); /* ffffffff tesla UNK1A34 */ ++ if (IS_NVA3F(dev_priv->chipset)) ++ xf_emit(ctx, 1, 1); /* 0000001f tesla UNK169C */ ++ xf_emit(ctx, 1, 0); /* ff/3ff */ ++ xf_emit(ctx, 1, 0); /* 1 LINKED_TSC */ ++ xf_emit(ctx, 1, 0); /* ff FP_ADDRESS_HIGH */ ++ xf_emit(ctx, 1, 0); /* ffffffff FP_ADDRESS_LOW */ ++ xf_emit(ctx, 1, 0x08100c12); /* 1fffffff FP_INTERPOLANT_CTRL */ ++ xf_emit(ctx, 1, 4); /* 00000007 FP_CONTROL */ ++ xf_emit(ctx, 1, 0); /* 000000ff FRAG_COLOR_CLAMP_EN */ ++ xf_emit(ctx, 1, 2); /* 00000003 REG_MODE */ ++ xf_emit(ctx, 1, 0x11); /* 0000007f RT_FORMAT */ ++ xf_emit(ctx, 7, 0); /* 0000007f RT_FORMAT */ ++ xf_emit(ctx, 1, 0); /* 00000007 */ ++ xf_emit(ctx, 1, 0xfac6881); /* 0fffffff RT_CONTROL */ ++ xf_emit(ctx, 1, 0); /* 00000003 MULTISAMPLE_CTRL */ ++ if (IS_NVA3F(dev_priv->chipset)) ++ xf_emit(ctx, 1, 3); /* 00000003 tesla UNK16B4 */ ++ xf_emit(ctx, 1, 0); /* 00000001 ALPHA_TEST_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000007 ALPHA_TEST_FUNC */ ++ xf_emit(ctx, 1, 0); /* 00000001 FRAMEBUFFER_SRGB */ ++ xf_emit(ctx, 1, 4); /* ffffffff tesla UNK1400 */ ++ xf_emit(ctx, 8, 0); /* 00000001 BLEND_ENABLE */ ++ xf_emit(ctx, 1, 0); /* 00000001 LOGIC_OP_ENABLE */ ++ xf_emit(ctx, 1, 2); /* 0000001f BLEND_FUNC_SRC_RGB */ ++ xf_emit(ctx, 1, 1); /* 0000001f BLEND_FUNC_DST_RGB */ ++ xf_emit(ctx, 1, 1); /* 00000007 BLEND_EQUATION_RGB */ ++ xf_emit(ctx, 1, 2); /* 0000001f BLEND_FUNC_SRC_ALPHA */ ++ xf_emit(ctx, 1, 1); /* 0000001f BLEND_FUNC_DST_ALPHA */ ++ xf_emit(ctx, 1, 1); /* 00000007 BLEND_EQUATION_ALPHA */ ++ xf_emit(ctx, 1, 1); /* 00000001 UNK133C */ ++ if (IS_NVA3F(dev_priv->chipset)) { ++ xf_emit(ctx, 1, 0); /* 00000001 UNK12E4 */ ++ xf_emit(ctx, 8, 2); /* 0000001f IBLEND_FUNC_SRC_RGB */ ++ xf_emit(ctx, 8, 1); /* 0000001f IBLEND_FUNC_DST_RGB */ ++ xf_emit(ctx, 8, 1); /* 00000007 IBLEND_EQUATION_RGB */ ++ xf_emit(ctx, 8, 2); /* 0000001f IBLEND_FUNC_SRC_ALPHA */ ++ xf_emit(ctx, 8, 1); /* 0000001f IBLEND_FUNC_DST_ALPHA */ ++ xf_emit(ctx, 8, 1); /* 00000007 IBLEND_EQUATION_ALPHA */ ++ xf_emit(ctx, 8, 1); /* 00000001 IBLEND_UNK00 */ ++ xf_emit(ctx, 1, 0); /* 00000003 tesla UNK1928 */ ++ xf_emit(ctx, 1, 0); /* 00000001 UNK1140 */ + } +- xf_emit(ctx, 1, 4); ++ xf_emit(ctx, 1, 0); /* 00000003 tesla UNK0F90 */ ++ xf_emit(ctx, 1, 4); /* 000000ff FP_RESULT_COUNT */ ++ /* XXX: demagic this part some day */ + if (dev_priv->chipset == 0x50) + xf_emit(ctx, 0x3a0, 0); + else if (dev_priv->chipset < 0x94) +@@ -2303,9 +3240,9 @@ nv50_graph_construct_xfer_tp2(struct nouveau_grctx *ctx) + xf_emit(ctx, 0x39f, 0); + else + xf_emit(ctx, 0x3a3, 0); +- xf_emit(ctx, 1, 0x11); +- xf_emit(ctx, 1, 0); +- xf_emit(ctx, 1, 1); ++ xf_emit(ctx, 1, 0x11); /* 3f/7f DST_FORMAT */ ++ xf_emit(ctx, 1, 0); /* 7 OPERATION */ ++ xf_emit(ctx, 1, 1); /* 1 DST_LINEAR */ + xf_emit(ctx, 0x2d, 0); + } + +@@ -2323,52 +3260,56 @@ nv50_graph_construct_xfer2(struct nouveau_grctx *ctx) + if (dev_priv->chipset < 0xa0) { + for (i = 0; i < 8; i++) { + ctx->ctxvals_pos = offset + i; ++ /* that little bugger belongs to csched. No idea ++ * what it's doing here. */ + if (i == 0) +- xf_emit(ctx, 1, 0x08100c12); ++ xf_emit(ctx, 1, 0x08100c12); /* FP_INTERPOLANT_CTRL */ + if (units & (1 << i)) +- nv50_graph_construct_xfer_tp2(ctx); ++ nv50_graph_construct_xfer_mpc(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + } + } else { + /* Strand 0: TPs 0, 1 */ + ctx->ctxvals_pos = offset; +- xf_emit(ctx, 1, 0x08100c12); ++ /* that little bugger belongs to csched. No idea ++ * what it's doing here. */ ++ xf_emit(ctx, 1, 0x08100c12); /* FP_INTERPOLANT_CTRL */ + if (units & (1 << 0)) +- nv50_graph_construct_xfer_tp2(ctx); ++ nv50_graph_construct_xfer_mpc(ctx); + if (units & (1 << 1)) +- nv50_graph_construct_xfer_tp2(ctx); ++ nv50_graph_construct_xfer_mpc(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + +- /* Strand 0: TPs 2, 3 */ ++ /* Strand 1: TPs 2, 3 */ + ctx->ctxvals_pos = offset + 1; + if (units & (1 << 2)) +- nv50_graph_construct_xfer_tp2(ctx); ++ nv50_graph_construct_xfer_mpc(ctx); + if (units & (1 << 3)) +- nv50_graph_construct_xfer_tp2(ctx); ++ nv50_graph_construct_xfer_mpc(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + +- /* Strand 0: TPs 4, 5, 6 */ ++ /* Strand 2: TPs 4, 5, 6 */ + ctx->ctxvals_pos = offset + 2; + if (units & (1 << 4)) +- nv50_graph_construct_xfer_tp2(ctx); ++ nv50_graph_construct_xfer_mpc(ctx); + if (units & (1 << 5)) +- nv50_graph_construct_xfer_tp2(ctx); ++ nv50_graph_construct_xfer_mpc(ctx); + if (units & (1 << 6)) +- nv50_graph_construct_xfer_tp2(ctx); ++ nv50_graph_construct_xfer_mpc(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + +- /* Strand 0: TPs 7, 8, 9 */ ++ /* Strand 3: TPs 7, 8, 9 */ + ctx->ctxvals_pos = offset + 3; + if (units & (1 << 7)) +- nv50_graph_construct_xfer_tp2(ctx); ++ nv50_graph_construct_xfer_mpc(ctx); + if (units & (1 << 8)) +- nv50_graph_construct_xfer_tp2(ctx); ++ nv50_graph_construct_xfer_mpc(ctx); + if (units & (1 << 9)) +- nv50_graph_construct_xfer_tp2(ctx); ++ nv50_graph_construct_xfer_mpc(ctx); + if ((ctx->ctxvals_pos-offset)/8 > size) + size = (ctx->ctxvals_pos-offset)/8; + } diff --git a/drivers/gpu/drm/nouveau/nv50_instmem.c b/drivers/gpu/drm/nouveau/nv50_instmem.c -index 5f21df3..b7ad258 100644 +index 5f21df3..ac3de05 100644 --- a/drivers/gpu/drm/nouveau/nv50_instmem.c +++ b/drivers/gpu/drm/nouveau/nv50_instmem.c -@@ -35,8 +35,6 @@ struct nv50_instmem_priv { - struct nouveau_gpuobj_ref *pramin_pt; - struct nouveau_gpuobj_ref *pramin_bar; - struct nouveau_gpuobj_ref *fb_bar; +@@ -32,41 +32,87 @@ + 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; ++ struct nouveau_gpuobj *pramin_pt; ++ struct nouveau_gpuobj *pramin_bar; ++ struct nouveau_gpuobj *fb_bar; }; - #define NV50_INSTMEM_PAGE_SHIFT 12 -@@ -147,7 +145,7 @@ nv50_instmem_init(struct drm_device *dev) +-#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) ++static void ++nv50_channel_del(struct nouveau_channel **pchan) ++{ ++ struct nouveau_channel *chan; + +-/*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) ++ chan = *pchan; ++ *pchan = NULL; ++ if (!chan) ++ return; ++ ++ nouveau_gpuobj_ref(NULL, &chan->ramfc); ++ nouveau_gpuobj_ref(NULL, &chan->vm_pd); ++ if (chan->ramin_heap.fl_entry.next) ++ drm_mm_takedown(&chan->ramin_heap); ++ nouveau_gpuobj_ref(NULL, &chan->ramin); ++ kfree(chan); ++} ++ ++static int ++nv50_channel_new(struct drm_device *dev, u32 size, ++ struct nouveau_channel **pchan) ++{ ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ u32 pgd = (dev_priv->chipset == 0x50) ? 0x1400 : 0x0200; ++ u32 fc = (dev_priv->chipset == 0x50) ? 0x0000 : 0x4200; ++ struct nouveau_channel *chan; ++ int ret; ++ ++ chan = kzalloc(sizeof(*chan), GFP_KERNEL); ++ if (!chan) ++ return -ENOMEM; ++ chan->dev = dev; ++ ++ ret = nouveau_gpuobj_new(dev, NULL, size, 0x1000, 0, &chan->ramin); ++ if (ret) { ++ nv50_channel_del(&chan); ++ return ret; ++ } ++ ++ ret = drm_mm_init(&chan->ramin_heap, 0x6000, chan->ramin->size); ++ if (ret) { ++ nv50_channel_del(&chan); ++ return ret; ++ } ++ ++ ret = nouveau_gpuobj_new_fake(dev, chan->ramin->pinst == ~0 ? ~0 : ++ chan->ramin->pinst + pgd, ++ chan->ramin->vinst + pgd, ++ 0x4000, NVOBJ_FLAG_ZERO_ALLOC, ++ &chan->vm_pd); ++ if (ret) { ++ nv50_channel_del(&chan); ++ return ret; ++ } ++ ++ ret = nouveau_gpuobj_new_fake(dev, chan->ramin->pinst == ~0 ? ~0 : ++ chan->ramin->pinst + fc, ++ chan->ramin->vinst + fc, 0x100, ++ NVOBJ_FLAG_ZERO_ALLOC, &chan->ramfc); ++ if (ret) { ++ nv50_channel_del(&chan); ++ return ret; ++ } ++ ++ *pchan = chan; ++ return 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; +- uint32_t save_nv001700; +- uint64_t v; + struct nv50_instmem_priv *priv; ++ struct nouveau_channel *chan; + int ret, i; ++ u32 tmp; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) +@@ -77,215 +123,115 @@ nv50_instmem_init(struct drm_device *dev) + 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 = dev_priv->vram_size - 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 = dev_priv->vram_size; +- 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) ++ /* Global PRAMIN heap */ ++ ret = drm_mm_init(&dev_priv->ramin_heap, 0, dev_priv->ramin_size); ++ if (ret) { ++ NV_ERROR(dev, "Failed to init RAMIN heap\n"); + 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); ++ /* we need a channel to plug into the hw to control the BARs */ ++ ret = nv50_channel_new(dev, 128*1024, &dev_priv->fifos[0]); + if (ret) + return ret; ++ chan = dev_priv->fifos[127] = dev_priv->fifos[0]; + +- 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); ++ /* allocate page table for PRAMIN BAR */ ++ ret = nouveau_gpuobj_new(dev, chan, (dev_priv->ramin_size >> 12) * 8, ++ 0x1000, NVOBJ_FLAG_ZERO_ALLOC, ++ &priv->pramin_pt); if (ret) return ret; -- if (nouveau_mem_init_heap(&chan->ramin_heap, c_base, c_size - c_base)) -+ if (drm_mm_init(&chan->ramin_heap, c_base, c_size - c_base)) - return -ENOMEM; +- for (i = 0; i < c_vmpd; i += 4) +- BAR0_WI32(chan->ramin->gpuobj, i, 0); ++ nv_wo32(chan->vm_pd, 0x0000, priv->pramin_pt->vinst | 0x63); ++ nv_wo32(chan->vm_pd, 0x0004, 0); - /* RAMFC + zero channel's PRAMIN up to start of VM pagedir */ -@@ -262,23 +260,18 @@ nv50_instmem_init(struct drm_device *dev) +- /* VM page directory */ +- ret = nouveau_gpuobj_new_fake(dev, c_vmpd, c_offset + c_vmpd, +- 0x4000, 0, &chan->vm_pd, NULL); ++ /* DMA object for PRAMIN BAR */ ++ ret = nouveau_gpuobj_new(dev, chan, 6*4, 16, 0, &priv->pramin_bar); + 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); ++ nv_wo32(priv->pramin_bar, 0x00, 0x7fc00000); ++ nv_wo32(priv->pramin_bar, 0x04, dev_priv->ramin_size - 1); ++ nv_wo32(priv->pramin_bar, 0x08, 0x00000000); ++ nv_wo32(priv->pramin_bar, 0x0c, 0x00000000); ++ nv_wo32(priv->pramin_bar, 0x10, 0x00000000); ++ nv_wo32(priv->pramin_bar, 0x14, 0x00000000); ++ ++ /* map channel into PRAMIN, gpuobj didn't do it for us */ ++ ret = nv50_instmem_bind(dev, chan->ramin); + if (ret) + return ret; - /* 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; - } +- v = c_offset | 1; +- if (dev_priv->vram_sys_base) { +- v += dev_priv->vram_sys_base; +- v |= 0x30; +- } ++ /* poke regs... */ ++ nv_wr32(dev, 0x001704, 0x00000000 | (chan->ramin->vinst >> 12)); ++ nv_wr32(dev, 0x001704, 0x40000000 | (chan->ramin->vinst >> 12)); ++ nv_wr32(dev, 0x00170c, 0x80000000 | (priv->pramin_bar->cinst >> 4)); + +- i = 0; +- while (v < dev_priv->vram_sys_base + c_offset + c_size) { +- BAR0_WI32(priv->pramin_pt->gpuobj, i + 0, lower_32_bits(v)); +- BAR0_WI32(priv->pramin_pt->gpuobj, i + 4, upper_32_bits(v)); +- v += 0x1000; +- i += 8; ++ tmp = nv_ri32(dev, 0); ++ nv_wi32(dev, 0, ~tmp); ++ if (nv_ri32(dev, 0) != ~tmp) { ++ NV_ERROR(dev, "PRAMIN readback failed\n"); ++ return -EIO; } ++ nv_wi32(dev, 0, tmp); + +- while (i < pt_size) { +- BAR0_WI32(priv->pramin_pt->gpuobj, i + 0, 0x00000000); +- BAR0_WI32(priv->pramin_pt->gpuobj, i + 4, 0x00000000); +- i += 8; +- } ++ dev_priv->ramin_available = true; ++ ++ /* Determine VM layout */ ++ 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 = dev_priv->vram_size; ++ 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; + +- BAR0_WI32(chan->vm_pd, 0x00, priv->pramin_pt->instance | 0x63); +- BAR0_WI32(chan->vm_pd, 0x04, 0x00000000); ++ 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); + + /* 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]); ++ ret = nouveau_gpuobj_new(dev, NULL, NV50_VM_BLOCK / 0x10000 * 8, ++ 0, NVOBJ_FLAG_ZERO_ALLOC, ++ &chan->vm_vram_pt[i]); + if (ret) { +- NV_ERROR(dev, "Error creating VRAM page tables: %d\n", +- ret); ++ NV_ERROR(dev, "Error creating VRAM PGT: %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); ++ dev_priv->vm_vram_pt[i] = chan->vm_vram_pt[i]; + +- BAR0_WI32(chan->vm_pd, 0x10 + (i*8), +- chan->vm_vram_pt[i]->instance | 0x61); +- BAR0_WI32(chan->vm_pd, 0x14 + (i*8), 0); ++ nv_wo32(chan->vm_pd, 0x10 + (i*8), ++ chan->vm_vram_pt[i]->vinst | 0x61); ++ nv_wo32(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); ++ ret = nouveau_gpuobj_new(dev, chan, 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); ++ nv_wo32(priv->fb_bar, 0x00, 0x7fc00000); ++ nv_wo32(priv->fb_bar, 0x04, 0x40000000 + ++ pci_resource_len(dev->pdev, 1) - 1); ++ nv_wo32(priv->fb_bar, 0x08, 0x40000000); ++ nv_wo32(priv->fb_bar, 0x0c, 0x00000000); ++ nv_wo32(priv->fb_bar, 0x10, 0x00000000); ++ nv_wo32(priv->fb_bar, 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); ++ dev_priv->engine.instmem.flush(dev); + ++ nv_wr32(dev, 0x001708, 0x80000000 | (priv->fb_bar->cinst >> 4)); + 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 */ +- +- 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; -+ if (drm_mm_init(&dev_priv->ramin_heap, c_size, dev_priv->ramin_size - c_size)) { - NV_ERROR(dev, "Failed to init RAMIN heap\n"); +- 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; + } + +@@ -302,29 +248,24 @@ nv50_instmem_takedown(struct drm_device *dev) + if (!priv) + return; + ++ dev_priv->ramin_available = false; ++ + /* 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); ++ nouveau_gpuobj_ref(NULL, &priv->fb_bar); ++ nouveau_gpuobj_ref(NULL, &priv->pramin_bar); ++ nouveau_gpuobj_ref(NULL, &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; +- } ++ for (i = 0; i < dev_priv->vm_vram_pt_nr; i++) ++ nouveau_gpuobj_ref(NULL, &chan->vm_vram_pt[i]); + 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); ++ nv50_channel_del(&dev_priv->fifos[0]); ++ dev_priv->fifos[127] = NULL; } -@@ -321,7 +314,7 @@ nv50_instmem_takedown(struct drm_device *dev) - 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); -+ drm_mm_takedown(&chan->ramin_heap); + dev_priv->engine.instmem.priv = NULL; +@@ -336,14 +277,14 @@ 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; ++ struct nouveau_gpuobj *ramin = chan->ramin; + int i; + +- ramin->im_backing_suspend = vmalloc(ramin->im_pramin->size); ++ ramin->im_backing_suspend = vmalloc(ramin->size); + if (!ramin->im_backing_suspend) + return -ENOMEM; + +- for (i = 0; i < ramin->im_pramin->size; i += 4) ++ for (i = 0; i < ramin->size; i += 4) + ramin->im_backing_suspend[i/4] = nv_ri32(dev, i); + return 0; + } +@@ -354,23 +295,25 @@ 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; ++ struct nouveau_gpuobj *ramin = chan->ramin; + 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]); ++ dev_priv->ramin_available = false; ++ dev_priv->ramin_base = ~0; ++ for (i = 0; i < ramin->size; i += 4) ++ nv_wo32(ramin, i, ramin->im_backing_suspend[i/4]); ++ dev_priv->ramin_available = true; + 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_BAR_CFG_BASE, (chan->ramin->vinst >> 12)); + nv_wr32(dev, NV50_PUNK_UNK1710, 0); +- nv_wr32(dev, NV50_PUNK_BAR_CFG_BASE, (chan->ramin->instance >> 12) | ++ nv_wr32(dev, NV50_PUNK_BAR_CFG_BASE, (chan->ramin->vinst >> 12) | + NV50_PUNK_BAR_CFG_BASE_VALID); +- nv_wr32(dev, NV50_PUNK_BAR1_CTXDMA, (priv->fb_bar->instance >> 4) | ++ nv_wr32(dev, NV50_PUNK_BAR1_CTXDMA, (priv->fb_bar->cinst >> 4) | + NV50_PUNK_BAR1_CTXDMA_VALID); +- nv_wr32(dev, NV50_PUNK_BAR3_CTXDMA, (priv->pramin_bar->instance >> 4) | ++ nv_wr32(dev, NV50_PUNK_BAR3_CTXDMA, (priv->pramin_bar->cinst >> 4) | + NV50_PUNK_BAR3_CTXDMA_VALID); + + for (i = 0; i < 8; i++) +@@ -386,7 +329,7 @@ nv50_instmem_populate(struct drm_device *dev, struct nouveau_gpuobj *gpuobj, + if (gpuobj->im_backing) + return -EINVAL; + +- *sz = ALIGN(*sz, NV50_INSTMEM_PAGE_SIZE); ++ *sz = ALIGN(*sz, 4096); + if (*sz == 0) + return -EINVAL; + +@@ -404,9 +347,7 @@ nv50_instmem_populate(struct drm_device *dev, struct nouveau_gpuobj *gpuobj, + return ret; + } + +- gpuobj->im_backing_start = gpuobj->im_backing->bo.mem.mm_node->start; +- gpuobj->im_backing_start <<= PAGE_SHIFT; +- ++ gpuobj->vinst = gpuobj->im_backing->bo.mem.mm_node->start << PAGE_SHIFT; + return 0; + } + +@@ -429,23 +370,23 @@ 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; +- struct nouveau_gpuobj *pramin_pt = priv->pramin_pt->gpuobj; ++ struct nouveau_gpuobj *pramin_pt = priv->pramin_pt; + uint32_t pte, pte_end; + uint64_t vram; - dev_priv->fifos[0] = dev_priv->fifos[127] = NULL; - kfree(chan); -@@ -436,14 +429,14 @@ nv50_instmem_bind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj) if (!gpuobj->im_backing || !gpuobj->im_pramin || gpuobj->im_bound) return -EINVAL; @@ -6668,23 +19591,27 @@ index 5f21df3..b7ad258 100644 pte = (gpuobj->im_pramin->start >> 12) << 1; pte_end = ((gpuobj->im_pramin->size >> 12) << 1) + pte; - vram = gpuobj->im_backing_start; +- vram = gpuobj->im_backing_start; ++ vram = gpuobj->vinst; - NV_DEBUG(dev, "pramin=0x%llx, pte=%d, pte_end=%d\n", + NV_DEBUG(dev, "pramin=0x%lx, 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); +- NV_DEBUG(dev, "first vram page: 0x%08x\n", gpuobj->im_backing_start); ++ NV_DEBUG(dev, "first vram page: 0x%010llx\n", gpuobj->vinst); -@@ -453,27 +446,15 @@ nv50_instmem_bind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj) + vram |= 1; + if (dev_priv->vram_sys_base) { +@@ -453,27 +394,16 @@ nv50_instmem_bind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj) vram |= 0x30; } - dev_priv->engine.instmem.prepare_access(dev, true); while (pte < pte_end) { - nv_wo32(dev, pramin_pt, pte++, lower_32_bits(vram)); - nv_wo32(dev, pramin_pt, pte++, upper_32_bits(vram)); - vram += NV50_INSTMEM_PAGE_SIZE; - } +- nv_wo32(dev, pramin_pt, pte++, lower_32_bits(vram)); +- nv_wo32(dev, pramin_pt, pte++, upper_32_bits(vram)); +- vram += NV50_INSTMEM_PAGE_SIZE; +- } - dev_priv->engine.instmem.finish_access(dev); - - nv_wr32(dev, 0x100c80, 0x00040001); @@ -6692,7 +19619,11 @@ index 5f21df3..b7ad258 100644 - NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (1)\n"); - NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80)); - return -EBUSY; -- } ++ nv_wo32(pramin_pt, (pte * 4) + 0, lower_32_bits(vram)); ++ nv_wo32(pramin_pt, (pte * 4) + 4, upper_32_bits(vram)); ++ vram += 0x1000; ++ pte += 2; + } + dev_priv->engine.instmem.flush(dev); - nv_wr32(dev, 0x100c80, 0x00060001); @@ -6706,14 +19637,24 @@ index 5f21df3..b7ad258 100644 gpuobj->im_bound = 1; return 0; -@@ -492,36 +473,36 @@ nv50_instmem_unbind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj) +@@ -489,39 +419,44 @@ nv50_instmem_unbind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj) + if (gpuobj->im_bound == 0) + return -EINVAL; + ++ /* can happen during late takedown */ ++ if (unlikely(!dev_priv->ramin_available)) ++ return 0; ++ pte = (gpuobj->im_pramin->start >> 12) << 1; pte_end = ((gpuobj->im_pramin->size >> 12) << 1) + pte; - dev_priv->engine.instmem.prepare_access(dev, true); while (pte < pte_end) { - nv_wo32(dev, priv->pramin_pt->gpuobj, pte++, 0x00000000); - nv_wo32(dev, priv->pramin_pt->gpuobj, pte++, 0x00000000); +- nv_wo32(dev, priv->pramin_pt->gpuobj, pte++, 0x00000000); +- nv_wo32(dev, priv->pramin_pt->gpuobj, pte++, 0x00000000); ++ nv_wo32(priv->pramin_pt, (pte * 4) + 0, 0x00000000); ++ nv_wo32(priv->pramin_pt, (pte * 4) + 4, 0x00000000); ++ pte += 2; } - dev_priv->engine.instmem.finish_access(dev); + dev_priv->engine.instmem.flush(dev); @@ -6731,7 +19672,7 @@ index 5f21df3..b7ad258 100644 - - priv->last_access_wr = write; + nv_wr32(dev, 0x00330c, 0x00000001); -+ if (!nv_wait(0x00330c, 0x00000001, 0x00000000)) ++ if (!nv_wait(dev, 0x00330c, 0x00000002, 0x00000000)) + NV_ERROR(dev, "PRAMIN flush timeout\n"); } @@ -6748,7 +19689,7 @@ index 5f21df3..b7ad258 100644 - NV_ERROR(dev, "PRAMIN flush timeout\n"); - } + nv_wr32(dev, 0x070000, 0x00000001); -+ if (!nv_wait(0x070000, 0x00000001, 0x00000000)) ++ if (!nv_wait(dev, 0x070000, 0x00000002, 0x00000000)) + NV_ERROR(dev, "PRAMIN flush timeout\n"); } @@ -6756,11 +19697,11 @@ index 5f21df3..b7ad258 100644 +nv50_vm_flush(struct drm_device *dev, int engine) +{ + nv_wr32(dev, 0x100c80, (engine << 16) | 1); -+ if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) ++ if (!nv_wait(dev, 0x100c80, 0x00000001, 0x00000000)) + NV_ERROR(dev, "vm flush timeout: engine %d\n", engine); +} diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c -index 812778d..bcd4cf8 100644 +index 812778d..b4a5ecb 100644 --- a/drivers/gpu/drm/nouveau/nv50_sor.c +++ b/drivers/gpu/drm/nouveau/nv50_sor.c @@ -37,52 +37,32 @@ @@ -6848,7 +19789,24 @@ index 812778d..bcd4cf8 100644 nvenc->dcb->or != nv_encoder->dcb->or) continue; -@@ -133,8 +115,22 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode) +@@ -110,7 +92,7 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode) + } + + /* wait for it to be done */ +- if (!nv_wait(NV50_PDISPLAY_SOR_DPMS_CTRL(or), ++ if (!nv_wait(dev, 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, +@@ -126,15 +108,29 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode) + + 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), ++ if (!nv_wait(dev, 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))); } @@ -6962,8 +19920,462 @@ index 812778d..bcd4cf8 100644 + drm_mode_connector_attach_encoder(connector, encoder); return 0; } +diff --git a/drivers/gpu/drm/nouveau/nvc0_fb.c b/drivers/gpu/drm/nouveau/nvc0_fb.c +new file mode 100644 +index 0000000..26a9960 +--- /dev/null ++++ b/drivers/gpu/drm/nouveau/nvc0_fb.c +@@ -0,0 +1,38 @@ ++/* ++ * Copyright 2010 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" ++ ++int ++nvc0_fb_init(struct drm_device *dev) ++{ ++ return 0; ++} ++ ++void ++nvc0_fb_takedown(struct drm_device *dev) ++{ ++} +diff --git a/drivers/gpu/drm/nouveau/nvc0_fifo.c b/drivers/gpu/drm/nouveau/nvc0_fifo.c +new file mode 100644 +index 0000000..2cdb7c3 +--- /dev/null ++++ b/drivers/gpu/drm/nouveau/nvc0_fifo.c +@@ -0,0 +1,89 @@ ++/* ++ * Copyright 2010 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" ++ ++void ++nvc0_fifo_disable(struct drm_device *dev) ++{ ++} ++ ++void ++nvc0_fifo_enable(struct drm_device *dev) ++{ ++} ++ ++bool ++nvc0_fifo_reassign(struct drm_device *dev, bool enable) ++{ ++ return false; ++} ++ ++bool ++nvc0_fifo_cache_pull(struct drm_device *dev, bool enable) ++{ ++ return false; ++} ++ ++int ++nvc0_fifo_channel_id(struct drm_device *dev) ++{ ++ return 127; ++} ++ ++int ++nvc0_fifo_create_context(struct nouveau_channel *chan) ++{ ++ return 0; ++} ++ ++void ++nvc0_fifo_destroy_context(struct nouveau_channel *chan) ++{ ++} ++ ++int ++nvc0_fifo_load_context(struct nouveau_channel *chan) ++{ ++ return 0; ++} ++ ++int ++nvc0_fifo_unload_context(struct drm_device *dev) ++{ ++ return 0; ++} ++ ++void ++nvc0_fifo_takedown(struct drm_device *dev) ++{ ++} ++ ++int ++nvc0_fifo_init(struct drm_device *dev) ++{ ++ return 0; ++} +diff --git a/drivers/gpu/drm/nouveau/nvc0_graph.c b/drivers/gpu/drm/nouveau/nvc0_graph.c +new file mode 100644 +index 0000000..edf2b21 +--- /dev/null ++++ b/drivers/gpu/drm/nouveau/nvc0_graph.c +@@ -0,0 +1,74 @@ ++/* ++ * Copyright 2010 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" ++ ++void ++nvc0_graph_fifo_access(struct drm_device *dev, bool enabled) ++{ ++} ++ ++struct nouveau_channel * ++nvc0_graph_channel(struct drm_device *dev) ++{ ++ return NULL; ++} ++ ++int ++nvc0_graph_create_context(struct nouveau_channel *chan) ++{ ++ return 0; ++} ++ ++void ++nvc0_graph_destroy_context(struct nouveau_channel *chan) ++{ ++} ++ ++int ++nvc0_graph_load_context(struct nouveau_channel *chan) ++{ ++ return 0; ++} ++ ++int ++nvc0_graph_unload_context(struct drm_device *dev) ++{ ++ return 0; ++} ++ ++void ++nvc0_graph_takedown(struct drm_device *dev) ++{ ++} ++ ++int ++nvc0_graph_init(struct drm_device *dev) ++{ ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ dev_priv->engine.graph.accel_blocked = true; ++ return 0; ++} +diff --git a/drivers/gpu/drm/nouveau/nvc0_instmem.c b/drivers/gpu/drm/nouveau/nvc0_instmem.c +new file mode 100644 +index 0000000..152d8e8 +--- /dev/null ++++ b/drivers/gpu/drm/nouveau/nvc0_instmem.c +@@ -0,0 +1,229 @@ ++/* ++ * Copyright 2010 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" ++ ++int ++nvc0_instmem_populate(struct drm_device *dev, struct nouveau_gpuobj *gpuobj, ++ uint32_t *size) ++{ ++ int ret; ++ ++ *size = ALIGN(*size, 4096); ++ if (*size == 0) ++ return -EINVAL; ++ ++ ret = nouveau_bo_new(dev, NULL, *size, 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->vinst = gpuobj->im_backing->bo.mem.mm_node->start << PAGE_SHIFT; ++ return 0; ++} ++ ++void ++nvc0_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 ++nvc0_instmem_bind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj) ++{ ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ uint32_t pte, pte_end; ++ uint64_t vram; ++ ++ if (!gpuobj->im_backing || !gpuobj->im_pramin || gpuobj->im_bound) ++ return -EINVAL; ++ ++ NV_DEBUG(dev, "st=0x%lx sz=0x%lx\n", ++ gpuobj->im_pramin->start, gpuobj->im_pramin->size); ++ ++ pte = gpuobj->im_pramin->start >> 12; ++ pte_end = (gpuobj->im_pramin->size >> 12) + pte; ++ vram = gpuobj->vinst; ++ ++ NV_DEBUG(dev, "pramin=0x%lx, pte=%d, pte_end=%d\n", ++ gpuobj->im_pramin->start, pte, pte_end); ++ NV_DEBUG(dev, "first vram page: 0x%010llx\n", gpuobj->vinst); ++ ++ while (pte < pte_end) { ++ nv_wr32(dev, 0x702000 + (pte * 8), (vram >> 8) | 1); ++ nv_wr32(dev, 0x702004 + (pte * 8), 0); ++ vram += 4096; ++ pte++; ++ } ++ dev_priv->engine.instmem.flush(dev); ++ ++ if (1) { ++ u32 chan = nv_rd32(dev, 0x1700) << 16; ++ nv_wr32(dev, 0x100cb8, (chan + 0x1000) >> 8); ++ nv_wr32(dev, 0x100cbc, 0x80000005); ++ } ++ ++ gpuobj->im_bound = 1; ++ return 0; ++} ++ ++int ++nvc0_instmem_unbind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj) ++{ ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ uint32_t pte, pte_end; ++ ++ if (gpuobj->im_bound == 0) ++ return -EINVAL; ++ ++ pte = gpuobj->im_pramin->start >> 12; ++ pte_end = (gpuobj->im_pramin->size >> 12) + pte; ++ while (pte < pte_end) { ++ nv_wr32(dev, 0x702000 + (pte * 8), 0); ++ nv_wr32(dev, 0x702004 + (pte * 8), 0); ++ pte++; ++ } ++ dev_priv->engine.instmem.flush(dev); ++ ++ gpuobj->im_bound = 0; ++ return 0; ++} ++ ++void ++nvc0_instmem_flush(struct drm_device *dev) ++{ ++ nv_wr32(dev, 0x070000, 1); ++ if (!nv_wait(dev, 0x070000, 0x00000002, 0x00000000)) ++ NV_ERROR(dev, "PRAMIN flush timeout\n"); ++} ++ ++int ++nvc0_instmem_suspend(struct drm_device *dev) ++{ ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ u32 *buf; ++ int i; ++ ++ dev_priv->susres.ramin_copy = vmalloc(65536); ++ if (!dev_priv->susres.ramin_copy) ++ return -ENOMEM; ++ buf = dev_priv->susres.ramin_copy; ++ ++ for (i = 0; i < 65536; i += 4) ++ buf[i/4] = nv_rd32(dev, NV04_PRAMIN + i); ++ return 0; ++} ++ ++void ++nvc0_instmem_resume(struct drm_device *dev) ++{ ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ u32 *buf = dev_priv->susres.ramin_copy; ++ u64 chan; ++ int i; ++ ++ chan = dev_priv->vram_size - dev_priv->ramin_rsvd_vram; ++ nv_wr32(dev, 0x001700, chan >> 16); ++ ++ for (i = 0; i < 65536; i += 4) ++ nv_wr32(dev, NV04_PRAMIN + i, buf[i/4]); ++ vfree(dev_priv->susres.ramin_copy); ++ dev_priv->susres.ramin_copy = NULL; ++ ++ nv_wr32(dev, 0x001714, 0xc0000000 | (chan >> 12)); ++} ++ ++int ++nvc0_instmem_init(struct drm_device *dev) ++{ ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ u64 chan, pgt3, imem, lim3 = dev_priv->ramin_size - 1; ++ int ret, i; ++ ++ dev_priv->ramin_rsvd_vram = 1 * 1024 * 1024; ++ chan = dev_priv->vram_size - dev_priv->ramin_rsvd_vram; ++ imem = 4096 + 4096 + 32768; ++ ++ nv_wr32(dev, 0x001700, chan >> 16); ++ ++ /* channel setup */ ++ nv_wr32(dev, 0x700200, lower_32_bits(chan + 0x1000)); ++ nv_wr32(dev, 0x700204, upper_32_bits(chan + 0x1000)); ++ nv_wr32(dev, 0x700208, lower_32_bits(lim3)); ++ nv_wr32(dev, 0x70020c, upper_32_bits(lim3)); ++ ++ /* point pgd -> pgt */ ++ nv_wr32(dev, 0x701000, 0); ++ nv_wr32(dev, 0x701004, ((chan + 0x2000) >> 8) | 1); ++ ++ /* point pgt -> physical vram for channel */ ++ pgt3 = 0x2000; ++ for (i = 0; i < dev_priv->ramin_rsvd_vram; i += 4096, pgt3 += 8) { ++ nv_wr32(dev, 0x700000 + pgt3, ((chan + i) >> 8) | 1); ++ nv_wr32(dev, 0x700004 + pgt3, 0); ++ } ++ ++ /* clear rest of pgt */ ++ for (; i < dev_priv->ramin_size; i += 4096, pgt3 += 8) { ++ nv_wr32(dev, 0x700000 + pgt3, 0); ++ nv_wr32(dev, 0x700004 + pgt3, 0); ++ } ++ ++ /* point bar3 at the channel */ ++ nv_wr32(dev, 0x001714, 0xc0000000 | (chan >> 12)); ++ ++ /* Global PRAMIN heap */ ++ ret = drm_mm_init(&dev_priv->ramin_heap, imem, ++ dev_priv->ramin_size - imem); ++ if (ret) { ++ NV_ERROR(dev, "Failed to init RAMIN heap\n"); ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++void ++nvc0_instmem_takedown(struct drm_device *dev) ++{ ++} diff --git a/drivers/gpu/drm/nouveau/nvreg.h b/drivers/gpu/drm/nouveau/nvreg.h -index 5998c35..ad64673 100644 +index 5998c35..881f8a5 100644 --- a/drivers/gpu/drm/nouveau/nvreg.h +++ b/drivers/gpu/drm/nouveau/nvreg.h @@ -147,28 +147,6 @@ @@ -6995,3 +20407,14 @@ index 5998c35..ad64673 100644 #define NV_PCRTC_INTR_0 0x00600100 # define NV_PCRTC_INTR_0_VBLANK (1 << 0) #define NV_PCRTC_INTR_EN_0 0x00600140 +@@ -285,6 +263,7 @@ + # 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_LCD_ROUTE_MASK 0x3b + # define NV_CIO_CRE_DDC0_STATUS__INDEX 0x36 + # define NV_CIO_CRE_DDC0_WR__INDEX 0x37 + # define NV_CIO_CRE_ILACE__INDEX 0x39 /* interlace */ +-- +1.7.3 + diff --git a/drm-polling-fixes.patch b/drm-polling-fixes.patch new file mode 100644 index 000000000..ebb1be826 --- /dev/null +++ b/drm-polling-fixes.patch @@ -0,0 +1,601 @@ +commit 879d56da9b89b52de2109cadf1369967522428e8 +Author: Dave Airlie +Date: Tue Oct 26 12:55:52 2010 +1000 + + drm/radeon/kms: don't poll dac load detect. + + This is slightly destructive, cpu intensive and can cause lockups. + + Signed-off-by: Dave Airlie + +commit 2563a90cdda1fe490947dc032ce17bf1118afc39 +Author: Dave Airlie +Date: Tue Nov 9 10:26:00 2010 +1000 + + drm: Use a nondestructive mode for output detect when polling (v2) + + v2: Julien Cristau pointed out that @nondestructive results in + double-negatives and confusion when trying to interpret the parameter, + so use @force instead. Much easier to type as well. ;-) + + And fix the miscompilation of vmgfx reported by Sedat Dilek. + + Signed-off-by: Chris Wilson + Cc: stable@kernel.org + Signed-off-by: Dave Airlie + + Conflicts: + + drivers/gpu/drm/i915/intel_tv.c + drivers/gpu/drm/nouveau/nouveau_connector.c + +commit deb557bbbcaa7fa8281d51490c73b51f579bc84d +Author: Dave Airlie +Date: Tue Nov 9 10:24:06 2010 +1000 + + drm: Use a nondestructive mode for output detect when polling + + Destructive load-detection is very expensive and due to failings + elsewhere can trigger system wide stalls of up to 600ms. A simple + first step to correcting this is not to invoke such an expensive + and destructive load-detection operation automatically. + + Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=29536 + Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=16265 + Reported-by: Bruno Prémont + Tested-by: Sitsofe Wheeler + Signed-off-by: Chris Wilson + Cc: stable@kernel.org + Signed-off-by: Dave Airlie + + Conflicts: + + drivers/gpu/drm/i915/intel_tv.c + drivers/gpu/drm/nouveau/nouveau_connector.c + +commit 8d5d3cd3612618a3c2214048788f0b2cc463ce0f +Author: Chris Wilson +Date: Mon Sep 6 23:53:47 2010 +0100 + + drm: Fix regression in disable polling e58f637 + + I broke out my trusty i845 and found a new boot failure, which upon + inspection turned out to be a recursion within: + + drm_helper_probe_single_connector_modes() -> drm_helper_hpd_irq_event() + -> intel_crt_detect() -> drm_helper_probe_single_connector_modes() + + Calling drm_kms_helper_poll_enable() instead performs the desired + re-initialisation of the polling should the user have toggled the + parameter, without the recursive side-effect. + + Signed-off-by: Chris Wilson + Cc: Dave Airlie + Signed-off-by: Dave Airlie + +commit ec0becade1eae9b0f40b12ea4fcc3ea36e993ba3 +Author: Dave Airlie +Date: Tue Nov 9 09:48:34 2010 +1000 + + drm/kms: Add a module parameter to disable polling + + Polling for a VGA device on an old system can be quite expensive, + causing latencies on the order of 600ms. As we hold the mode mutex for + this time and also need the same mutex to move the cursor, we trigger a + user-visible stall. + + The real solution would involve improving the granulatity of the + locking and so perhaps performing some of the probing not under the lock + or some other updates can be done under different locks. Also reducing the + cost of probing for a non-existent monitor would be worthwhile. However, + exposing a parameter to disable polling is a simple workaround in the + meantime. + + In order to accommodate users turning polling on and off at runtime, the + polling is potentially re-enabled on every probe. This is coupled to + the user calling xrandr, which seems to be a vaild time to reset the + polling timeout since the information on the connection has just been + updated. (The presumption being that all connections are probed in a + single xrandr pass, which is currently valid.) + + References: + + Bug 29536 - 2.6.35 causes ~600ms latency every 10s + https://bugs.freedesktop.org/show_bug.cgi?id=29536 + + Bug 16265 - Why is kslowd accumulating so much CPU time? + https://bugzilla.kernel.org/show_bug.cgi?id=16265 + + Signed-off-by: Chris Wilson + Reported-and-tested-by: Bruno Prémont + Signed-off-by: Dave Airlie + + Conflicts: + + drivers/gpu/drm/drm_crtc_helper.c + +commit 77e90256db2f7e6424717f7cc984b22d2832243f +Author: Dan Carpenter +Date: Thu Aug 19 11:46:29 2010 +0200 + + drm: move dereference below check + + "fb_helper_conn" is dereferenced before the check for NULL. It's never + actually NULL here, so this is mostly to keep the static checkers happy. + + Signed-off-by: Dan Carpenter + Signed-off-by: Dave Airlie + +commit 86e25290d9df7a84d185dfc037851d72d270a6c0 +Author: Alex Deucher +Date: Tue Jul 20 03:24:11 2010 -0400 + + drm/radeon/kms: make sure HPD is set to NONE on analog-only connectors + + HPD is digital only. + + Signed-off-by: Alex Deucher + Signed-off-by: Dave Airlie + +diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c +index 25d70d6..d537039 100644 +--- a/drivers/gpu/drm/drm_crtc_helper.c ++++ b/drivers/gpu/drm/drm_crtc_helper.c +@@ -34,6 +34,9 @@ + #include "drm_crtc_helper.h" + #include "drm_fb_helper.h" + ++static bool drm_kms_helper_poll = true; ++module_param_named(poll, drm_kms_helper_poll, bool, 0600); ++ + static void drm_mode_validate_flag(struct drm_connector *connector, + int flags) + { +@@ -98,8 +101,10 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector, + connector->status = connector_status_disconnected; + if (connector->funcs->force) + connector->funcs->force(connector); +- } else +- connector->status = connector->funcs->detect(connector); ++ } else { ++ connector->status = connector->funcs->detect(connector, true); ++ drm_kms_helper_poll_enable(dev); ++ } + + if (connector->status == connector_status_disconnected) { + DRM_DEBUG_KMS("%s is disconnected\n", +@@ -820,6 +825,9 @@ static void output_poll_execute(struct slow_work *work) + bool repoll = false, changed = false; + int ret; + ++ if (!drm_kms_helper_poll) ++ return; ++ + mutex_lock(&dev->mode_config.mutex); + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + +@@ -839,7 +847,7 @@ static void output_poll_execute(struct slow_work *work) + !(connector->polled & DRM_CONNECTOR_POLL_HPD)) + continue; + +- status = connector->funcs->detect(connector); ++ status = connector->funcs->detect(connector, false); + if (old_status != status) + changed = true; + } +@@ -874,6 +882,9 @@ void drm_kms_helper_poll_enable(struct drm_device *dev) + struct drm_connector *connector; + int ret; + ++ if (!dev->mode_config.poll_enabled || !drm_kms_helper_poll) ++ return; ++ + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (connector->polled) + poll = true; +@@ -909,9 +920,12 @@ void drm_helper_hpd_irq_event(struct drm_device *dev) + { + if (!dev->mode_config.poll_enabled) + return; ++ + delayed_slow_work_cancel(&dev->mode_config.output_poll_slow_work); + /* schedule a slow work asap */ +- delayed_slow_work_enqueue(&dev->mode_config.output_poll_slow_work, 0); ++ if (drm_kms_helper_poll) ++ delayed_slow_work_enqueue(&dev->mode_config.output_poll_slow_work, 0); ++ + } + EXPORT_SYMBOL(drm_helper_hpd_irq_event); + +diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c +index 7196620..cef8d8d 100644 +--- a/drivers/gpu/drm/drm_fb_helper.c ++++ b/drivers/gpu/drm/drm_fb_helper.c +@@ -94,10 +94,11 @@ static bool drm_fb_helper_connector_parse_command_line(struct drm_fb_helper_conn + int i; + enum drm_connector_force force = DRM_FORCE_UNSPECIFIED; + struct drm_fb_helper_cmdline_mode *cmdline_mode; +- struct drm_connector *connector = fb_helper_conn->connector; ++ struct drm_connector *connector; + + if (!fb_helper_conn) + return false; ++ connector = fb_helper_conn->connector; + + cmdline_mode = &fb_helper_conn->cmdline_mode; + if (!mode_option) +diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c +index 101d381..9500af1 100644 +--- a/drivers/gpu/drm/drm_sysfs.c ++++ b/drivers/gpu/drm/drm_sysfs.c +@@ -159,7 +159,7 @@ static ssize_t status_show(struct device *device, + struct drm_connector *connector = to_drm_connector(device); + enum drm_connector_status status; + +- status = connector->funcs->detect(connector); ++ status = connector->funcs->detect(connector, true); + return snprintf(buf, PAGE_SIZE, "%s\n", + drm_get_connector_status_name(status)); + } +diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c +index ee0732b..3886b47 100644 +--- a/drivers/gpu/drm/i915/intel_crt.c ++++ b/drivers/gpu/drm/i915/intel_crt.c +@@ -402,7 +402,8 @@ intel_crt_load_detect(struct drm_crtc *crtc, struct intel_encoder *intel_encoder + return status; + } + +-static enum drm_connector_status intel_crt_detect(struct drm_connector *connector) ++static enum drm_connector_status ++intel_crt_detect(struct drm_connector *connector, bool force) + { + struct drm_device *dev = connector->dev; + struct drm_encoder *encoder = intel_attached_encoder(connector); +@@ -421,6 +422,9 @@ static enum drm_connector_status intel_crt_detect(struct drm_connector *connecto + if (intel_crt_detect_ddc(encoder)) + return connector_status_connected; + ++ if (!force) ++ return connector->status; ++ + /* for pre-945g platforms use load detect */ + if (encoder->crtc && encoder->crtc->enabled) { + status = intel_crt_load_detect(encoder->crtc, intel_encoder); +diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c +index d9de8f1..b58249d 100644 +--- a/drivers/gpu/drm/i915/intel_dp.c ++++ b/drivers/gpu/drm/i915/intel_dp.c +@@ -1287,7 +1287,7 @@ ironlake_dp_detect(struct drm_connector *connector) + * \return false if DP port is disconnected. + */ + static enum drm_connector_status +-intel_dp_detect(struct drm_connector *connector) ++intel_dp_detect(struct drm_connector *connector, bool force) + { + struct drm_encoder *encoder = intel_attached_encoder(connector); + struct intel_encoder *intel_encoder = enc_to_intel_encoder(encoder); +diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c +index 227feca..48a1889 100644 +--- a/drivers/gpu/drm/i915/intel_dvo.c ++++ b/drivers/gpu/drm/i915/intel_dvo.c +@@ -211,7 +211,8 @@ static void intel_dvo_mode_set(struct drm_encoder *encoder, + * + * Unimplemented. + */ +-static enum drm_connector_status intel_dvo_detect(struct drm_connector *connector) ++static enum drm_connector_status ++intel_dvo_detect(struct drm_connector *connector, bool force) + { + struct drm_encoder *encoder = intel_attached_encoder(connector); + struct intel_encoder *intel_encoder = enc_to_intel_encoder(encoder); +diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c +index 83bd764..d1decfc 100644 +--- a/drivers/gpu/drm/i915/intel_hdmi.c ++++ b/drivers/gpu/drm/i915/intel_hdmi.c +@@ -134,7 +134,7 @@ static bool intel_hdmi_mode_fixup(struct drm_encoder *encoder, + } + + static enum drm_connector_status +-intel_hdmi_detect(struct drm_connector *connector) ++intel_hdmi_detect(struct drm_connector *connector, bool force) + { + struct drm_encoder *encoder = intel_attached_encoder(connector); + struct intel_encoder *intel_encoder = enc_to_intel_encoder(encoder); +diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c +index 7d42ff1..9ad3425 100644 +--- a/drivers/gpu/drm/i915/intel_lvds.c ++++ b/drivers/gpu/drm/i915/intel_lvds.c +@@ -546,7 +546,8 @@ static void intel_lvds_mode_set(struct drm_encoder *encoder, + * connected and closed means disconnected. We also send hotplug events as + * needed, using lid status notification from the input layer. + */ +-static enum drm_connector_status intel_lvds_detect(struct drm_connector *connector) ++static enum drm_connector_status ++intel_lvds_detect(struct drm_connector *connector, bool force) + { + struct drm_device *dev = connector->dev; + enum drm_connector_status status = connector_status_connected; +@@ -641,7 +642,9 @@ static int intel_lid_notify(struct notifier_block *nb, unsigned long val, + * the LID nofication event. + */ + if (connector) +- connector->status = connector->funcs->detect(connector); ++ connector->status = connector->funcs->detect(connector, ++ false); ++ + /* Don't force modeset on machines where it causes a GPU lockup */ + if (dmi_check_system(intel_no_modeset_on_lid)) + return NOTIFY_OK; +diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c +index 76993ac..76c9b3d 100644 +--- a/drivers/gpu/drm/i915/intel_sdvo.c ++++ b/drivers/gpu/drm/i915/intel_sdvo.c +@@ -1496,7 +1496,7 @@ intel_analog_is_connected(struct drm_device *dev) + if (!analog_connector) + return false; + +- if (analog_connector->funcs->detect(analog_connector) == ++ if (analog_connector->funcs->detect(analog_connector, false) == + connector_status_disconnected) + return false; + +@@ -1567,7 +1567,8 @@ intel_sdvo_hdmi_sink_detect(struct drm_connector *connector) + return status; + } + +-static enum drm_connector_status intel_sdvo_detect(struct drm_connector *connector) ++static enum drm_connector_status ++intel_sdvo_detect(struct drm_connector *connector, bool force) + { + uint16_t response; + u8 status; +diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c +index 6d553c2..ad40f1b 100644 +--- a/drivers/gpu/drm/i915/intel_tv.c ++++ b/drivers/gpu/drm/i915/intel_tv.c +@@ -1336,7 +1336,7 @@ static void intel_tv_find_better_format(struct drm_connector *connector) + * we have a pipe programmed in order to probe the TV. + */ + static enum drm_connector_status +-intel_tv_detect(struct drm_connector *connector) ++intel_tv_detect(struct drm_connector *connector, bool force) + { + struct drm_crtc *crtc; + struct drm_display_mode mode; +@@ -1351,7 +1351,7 @@ intel_tv_detect(struct drm_connector *connector) + + if (encoder->crtc && encoder->crtc->enabled) { + type = intel_tv_detect_type(encoder->crtc, intel_encoder); +- } else { ++ } else if (force) { + crtc = intel_get_load_detect_pipe(intel_encoder, connector, + &mode, &dpms_mode); + if (crtc) { +@@ -1359,8 +1359,9 @@ intel_tv_detect(struct drm_connector *connector) + intel_release_load_detect_pipe(intel_encoder, connector, + dpms_mode); + } else +- type = -1; +- } ++ return connector_status_unknown; ++ } else ++ return connector->status; + + tv_priv->type = type; + +diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c +index 149ed22..1085376 100644 +--- a/drivers/gpu/drm/nouveau/nouveau_connector.c ++++ b/drivers/gpu/drm/nouveau/nouveau_connector.c +@@ -228,7 +228,7 @@ nouveau_connector_set_encoder(struct drm_connector *connector, + } + + static enum drm_connector_status +-nouveau_connector_detect(struct drm_connector *connector) ++nouveau_connector_detect(struct drm_connector *connector, bool force) + { + struct drm_device *dev = connector->dev; + struct nouveau_connector *nv_connector = nouveau_connector(connector); +diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c +index adccbc2..1680600 100644 +--- a/drivers/gpu/drm/radeon/radeon_connectors.c ++++ b/drivers/gpu/drm/radeon/radeon_connectors.c +@@ -467,7 +467,8 @@ static int radeon_lvds_mode_valid(struct drm_connector *connector, + return MODE_OK; + } + +-static enum drm_connector_status radeon_lvds_detect(struct drm_connector *connector) ++static enum drm_connector_status ++radeon_lvds_detect(struct drm_connector *connector, bool force) + { + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + struct drm_encoder *encoder = radeon_best_single_encoder(connector); +@@ -582,7 +583,8 @@ static int radeon_vga_mode_valid(struct drm_connector *connector, + return MODE_OK; + } + +-static enum drm_connector_status radeon_vga_detect(struct drm_connector *connector) ++static enum drm_connector_status ++radeon_vga_detect(struct drm_connector *connector, bool force) + { + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + struct drm_encoder *encoder; +@@ -621,6 +623,11 @@ static enum drm_connector_status radeon_vga_detect(struct drm_connector *connect + ret = connector_status_connected; + } + } else { ++ ++ /* if we aren't forcing don't do destructive polling */ ++ if (!force) ++ return connector->status; ++ + if (radeon_connector->dac_load_detect && encoder) { + encoder_funcs = encoder->helper_private; + ret = encoder_funcs->detect(encoder, connector); +@@ -679,7 +686,8 @@ static int radeon_tv_mode_valid(struct drm_connector *connector, + return MODE_OK; + } + +-static enum drm_connector_status radeon_tv_detect(struct drm_connector *connector) ++static enum drm_connector_status ++radeon_tv_detect(struct drm_connector *connector, bool force) + { + struct drm_encoder *encoder; + struct drm_encoder_helper_funcs *encoder_funcs; +@@ -736,7 +744,8 @@ static int radeon_dvi_get_modes(struct drm_connector *connector) + * we have to check if this analog encoder is shared with anyone else (TV) + * if its shared we have to set the other connector to disconnected. + */ +-static enum drm_connector_status radeon_dvi_detect(struct drm_connector *connector) ++static enum drm_connector_status ++radeon_dvi_detect(struct drm_connector *connector, bool force) + { + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + struct drm_encoder *encoder = NULL; +@@ -806,6 +815,11 @@ static enum drm_connector_status radeon_dvi_detect(struct drm_connector *connect + if ((ret == connector_status_connected) && (radeon_connector->use_digital == true)) + goto out; + ++ if (!force) { ++ ret = connector->status; ++ goto out; ++ } ++ + /* find analog encoder */ + if (radeon_connector->dac_load_detect) { + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { +@@ -962,7 +976,8 @@ static int radeon_dp_get_modes(struct drm_connector *connector) + return ret; + } + +-static enum drm_connector_status radeon_dp_detect(struct drm_connector *connector) ++static enum drm_connector_status ++radeon_dp_detect(struct drm_connector *connector, bool force) + { + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + enum drm_connector_status ret = connector_status_disconnected; +@@ -1082,6 +1097,8 @@ radeon_add_atom_connector(struct drm_device *dev, + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.load_detect_property, + 1); ++ /* no HPD on analog connectors */ ++ radeon_connector->hpd.hpd = RADEON_HPD_NONE; + connector->polled = DRM_CONNECTOR_POLL_CONNECT; + connector->interlace_allowed = true; + connector->doublescan_allowed = true; +@@ -1096,6 +1113,8 @@ radeon_add_atom_connector(struct drm_device *dev, + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.load_detect_property, + 1); ++ /* no HPD on analog connectors */ ++ radeon_connector->hpd.hpd = RADEON_HPD_NONE; + connector->interlace_allowed = true; + connector->doublescan_allowed = true; + break; +@@ -1186,6 +1205,8 @@ radeon_add_atom_connector(struct drm_device *dev, + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.tv_std_property, + radeon_atombios_get_tv_info(rdev)); ++ /* no HPD on analog connectors */ ++ radeon_connector->hpd.hpd = RADEON_HPD_NONE; + } + connector->interlace_allowed = false; + connector->doublescan_allowed = false; +@@ -1209,7 +1230,7 @@ radeon_add_atom_connector(struct drm_device *dev, + break; + } + +- if (hpd->hpd == RADEON_HPD_NONE) { ++ if (radeon_connector->hpd.hpd == RADEON_HPD_NONE) { + if (i2c_bus->valid) + connector->polled = DRM_CONNECTOR_POLL_CONNECT; + } else +@@ -1276,6 +1297,8 @@ radeon_add_legacy_connector(struct drm_device *dev, + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.load_detect_property, + 1); ++ /* no HPD on analog connectors */ ++ radeon_connector->hpd.hpd = RADEON_HPD_NONE; + connector->polled = DRM_CONNECTOR_POLL_CONNECT; + connector->interlace_allowed = true; + connector->doublescan_allowed = true; +@@ -1290,6 +1313,8 @@ radeon_add_legacy_connector(struct drm_device *dev, + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.load_detect_property, + 1); ++ /* no HPD on analog connectors */ ++ radeon_connector->hpd.hpd = RADEON_HPD_NONE; + connector->interlace_allowed = true; + connector->doublescan_allowed = true; + break; +@@ -1328,6 +1353,8 @@ radeon_add_legacy_connector(struct drm_device *dev, + drm_connector_attach_property(&radeon_connector->base, + rdev->mode_info.tv_std_property, + radeon_combios_get_tv_info(rdev)); ++ /* no HPD on analog connectors */ ++ radeon_connector->hpd.hpd = RADEON_HPD_NONE; + } + connector->interlace_allowed = false; + connector->doublescan_allowed = false; +@@ -1345,7 +1372,7 @@ radeon_add_legacy_connector(struct drm_device *dev, + break; + } + +- if (hpd->hpd == RADEON_HPD_NONE) { ++ if (radeon_connector->hpd.hpd == RADEON_HPD_NONE) { + if (i2c_bus->valid) + connector->polled = DRM_CONNECTOR_POLL_CONNECT; + } else +diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +index cfaf690..5b638cb 100644 +--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c ++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +@@ -335,7 +335,8 @@ static void vmw_ldu_connector_restore(struct drm_connector *connector) + } + + static enum drm_connector_status +- vmw_ldu_connector_detect(struct drm_connector *connector) ++ vmw_ldu_connector_detect(struct drm_connector *connector, ++ bool force) + { + if (vmw_connector_to_ldu(connector)->pref_active) + return connector_status_connected; +@@ -516,7 +517,7 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) + + drm_connector_init(dev, connector, &vmw_legacy_connector_funcs, + DRM_MODE_CONNECTOR_LVDS); +- connector->status = vmw_ldu_connector_detect(connector); ++ connector->status = vmw_ldu_connector_detect(connector, true); + + drm_encoder_init(dev, encoder, &vmw_legacy_encoder_funcs, + DRM_MODE_ENCODER_LVDS); +@@ -610,7 +611,7 @@ int vmw_kms_ldu_update_layout(struct vmw_private *dev_priv, unsigned num, + ldu->pref_height = 600; + ldu->pref_active = false; + } +- con->status = vmw_ldu_connector_detect(con); ++ con->status = vmw_ldu_connector_detect(con, true); + } + + mutex_unlock(&dev->mode_config.mutex); +diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h +index 93a1a31..afbb578 100644 +--- a/include/drm/drm_crtc.h ++++ b/include/drm/drm_crtc.h +@@ -420,7 +420,15 @@ struct drm_connector_funcs { + void (*dpms)(struct drm_connector *connector, int mode); + void (*save)(struct drm_connector *connector); + void (*restore)(struct drm_connector *connector); +- enum drm_connector_status (*detect)(struct drm_connector *connector); ++ ++ /* Check to see if anything is attached to the connector. ++ * @force is set to false whilst polling, true when checking the ++ * connector due to user request. @force can be used by the driver ++ * to avoid expensive, destructive operations during automated ++ * probing. ++ */ ++ enum drm_connector_status (*detect)(struct drm_connector *connector, ++ bool force); + int (*fill_modes)(struct drm_connector *connector, uint32_t max_width, uint32_t max_height); + int (*set_property)(struct drm_connector *connector, struct drm_property *property, + uint64_t val); diff --git a/drm-radeon-r600-cs-checker-fixes.patch b/drm-radeon-r600-cs-checker-fixes.patch new file mode 100644 index 000000000..02fe41701 --- /dev/null +++ b/drm-radeon-r600-cs-checker-fixes.patch @@ -0,0 +1,854 @@ +commit 4adf332cc24ee2d46064aaafd8216169d29566d5 +Author: Alex Deucher +Date: Sun Nov 14 20:24:35 2010 -0500 + + drm/radeon/kms: fix and unify tiled buffer alignment checking for r6xx/7xx + + Tiled buffers have the same alignment requirements regardless of + whether the surface is for db, cb, or textures. Previously, the + calculations where inconsistent for each buffer type. + + - Unify the alignment calculations in a common function + - Standardize the alignment units (pixels for pitch/height/depth, + bytes for base) + - properly check the buffer base alignments + + Signed-off-by: Alex Deucher + Signed-off-by: Dave Airlie + +commit c37cb9e61dce7437f63280d9347a9ffdf4ec34e7 +Author: Alex Deucher +Date: Wed Oct 27 01:44:35 2010 -0400 + + drm/radeon/kms: fix tiled db height calculation on 6xx/7xx + + Calculate height based on the slice bitfield rather than the size. + Same as Dave's CB fix. + + Signed-off-by: Alex Deucher + Signed-off-by: Dave Airlie + +commit b80c8fdc2fadd8182b958e91a10f2fa287f993e4 +Author: Alex Deucher +Date: Tue Oct 26 20:22:42 2010 -0400 + + drm/radeon/kms: fix r6xx/7xx 1D tiling CS checker v2 + + broken by: + drm/radeon/r600: fix tiling issues in CS checker. + + v2: only apply it to 1D tiling case. + + Signed-off-by: Alex Deucher + Signed-off-by: Dave Airlie + +commit 4336ac5c0a4e5dfbb51631ad680d6a5d0b295cd3 +Author: Alex Deucher +Date: Mon Oct 18 23:45:39 2010 -0400 + + drm/radeon/kms: fix 2D tile height alignment in the r600 CS checker + + macro tile heights are aligned to num channels, not num banks. + + Noticed by Dave Airlie. + + Signed-off-by: Alex Deucher + Cc: stable@kernel.org + Signed-off-by: Dave Airlie + +commit 5c76976a1419a633f9f33c6547bae00348b855d2 +Author: Dave Airlie +Date: Thu Oct 21 13:55:40 2010 +1000 + + drm/radeon/r600: fix tiling issues in CS checker. + + The CS checker had some incorrect alignment requirements for 2D surfaces, + this made rendering to mipmap levels that were 2D broken. + + Also the CB height was being worked out from the BO size, this doesn't work + at all when rendering mipmap levels, instead we work out what height userspace + wanted from slice max and use that to check it fits inside the BO, however + the DDX send the wrong slice max for an unaligned buffer so we have to workaround + for that even though its a userspace bug. + + Reviewed-by: Alex Deucher + Signed-off-by: Dave Airlie + +commit fa479e9df4558af6f091c45be37f713e64b836a1 +Author: Alex Deucher +Date: Tue Sep 14 10:10:47 2010 -0400 + + drm/radeon/kms: only warn on mipmap size checks in r600 cs checker (v2) + + The texture base address registers are in units of 256 bytes. + The original CS checker treated these offsets as bytes, so the + original check was wrong. I fixed the units in a patch during + the 2.6.36 cycle, but this ended up breaking some existing + userspace (probably due to a bug in either userspace texture allocation + or the drm texture mipmap checker). So for now, until we come + up with a better fix, just warn if the mipmap size it too large. + This will keep existing userspace working and it should be just + as safe as before when we were checking the wrong units. These + are GPU MC addresses, so if they fall outside of the VRAM or + GART apertures, they end up at the GPU default page, so this should + be safe from a security perspective. + + v2: Just disable the warning. It just spams the log and there's + nothing the user can do about it. + + Signed-off-by: Alex Deucher + Cc: Jerome Glisse + Signed-off-by: Dave Airlie + +commit 6e8df81d43d5c95fe37db7f0ef55332de1a4b698 +Author: Dave Airlie +Date: Thu Aug 12 09:40:05 2010 +1000 + + drm/radeon: drop old and broken mesa warning + + This never really got fixed in mesa, and the kernel deals with the problem + just fine, so don't got reporting things that confuse people. + + Signed-off-by: Dave Airlie + +commit 23f012fb9a0633f2f8901440e314d6276255b1c0 +Author: Alex Deucher +Date: Wed Aug 11 11:54:25 2010 -0400 + + drm/radeon/kms: another r6xx/r7xx CS checker fix + + add default case for buffer formats + + Signed-off-by: Alex Deucher + Cc: Andre Maasikas + Signed-off-by: Dave Airlie + +commit 2c7e76decda2d437f0ca064fef1a2d5d8892288e +Author: Alex Deucher +Date: Fri Aug 6 02:54:05 2010 -0400 + + drm/radeon/kms: r600 CS parser fixes + + - buffer offsets in the base regs are 256b aligned so + shift properly when comparing, fixed by Andre Maasikas + - mipmap size was calculated wrong when nlevel=0 + - texture bo offsets were used after the bo base address was added + - vertex resource size register is size - 1, not size + + Signed-off-by: Alex Deucher + Cc: Andre Maasikas + Signed-off-by: Dave Airlie + +commit 85d1363c9f15b5d4303b635142cee0ba9d1473fc +Author: Alex Deucher +Date: Fri Jun 4 18:41:42 2010 -0400 + + drm/radeon/kms: fix CS alignment checking for tiling (v2) + + Covers depth, cb, and textures. Hopefully I got this right. + + v2: - fix bugs: + https://bugs.freedesktop.org/show_bug.cgi?id=28327 + https://bugs.freedesktop.org/show_bug.cgi?id=28381 + - use ALIGNED(), IS_ALIGNED() macros + + Signed-off-by: Alex Deucher + Signed-off-by: Dave Airlie + +commit b956b6e7c7fb207daf32520c0a72c8c06ef1d5f5 +Author: Alex Deucher +Date: Thu May 20 12:43:52 2010 -0400 + + drm/radeon/kms: add tiling support to the cs checker for r6xx/r7xx + + Check for relocs for DB_DEPTH_INFO, CB_COLOR*_INFO, and texture + resources. + + Signed-off-by: Alex Deucher + Signed-off-by: Dave Airlie + + drivers/gpu/drm/radeon/r600_cs.c | 391 ++++++++++++++++++++++++++++++-------- + drivers/gpu/drm/radeon/r600d.h | 12 ++ + 2 files changed, 324 insertions(+), 79 deletions(-) + +diff --git a/drivers/gpu/drm/radeon/r600_cs.c b/drivers/gpu/drm/radeon/r600_cs.c +index 144c32d..0f90fc3 100644 +--- a/drivers/gpu/drm/radeon/r600_cs.c ++++ b/drivers/gpu/drm/radeon/r600_cs.c +@@ -25,6 +25,7 @@ + * Alex Deucher + * Jerome Glisse + */ ++#include + #include "drmP.h" + #include "radeon.h" + #include "r600d.h" +@@ -49,6 +50,7 @@ struct r600_cs_track { + u32 nsamples; + u32 cb_color_base_last[8]; + struct radeon_bo *cb_color_bo[8]; ++ u64 cb_color_bo_mc[8]; + u32 cb_color_bo_offset[8]; + struct radeon_bo *cb_color_frag_bo[8]; + struct radeon_bo *cb_color_tile_bo[8]; +@@ -66,6 +68,7 @@ struct r600_cs_track { + u32 db_depth_size; + u32 db_offset; + struct radeon_bo *db_bo; ++ u64 db_bo_mc; + }; + + static inline int r600_bpe_from_format(u32 *bpe, u32 format) +@@ -132,12 +135,75 @@ static inline int r600_bpe_from_format(u32 *bpe, u32 format) + case V_038004_FMT_GB_GR: + case V_038004_FMT_BG_RG: + case V_038004_COLOR_INVALID: ++ default: + *bpe = 16; + return -EINVAL; + } + return 0; + } + ++struct array_mode_checker { ++ int array_mode; ++ u32 group_size; ++ u32 nbanks; ++ u32 npipes; ++ u32 nsamples; ++ u32 bpe; ++}; ++ ++/* returns alignment in pixels for pitch/height/depth and bytes for base */ ++static inline int r600_get_array_mode_alignment(struct array_mode_checker *values, ++ u32 *pitch_align, ++ u32 *height_align, ++ u32 *depth_align, ++ u64 *base_align) ++{ ++ u32 tile_width = 8; ++ u32 tile_height = 8; ++ u32 macro_tile_width = values->nbanks; ++ u32 macro_tile_height = values->npipes; ++ u32 tile_bytes = tile_width * tile_height * values->bpe * values->nsamples; ++ u32 macro_tile_bytes = macro_tile_width * macro_tile_height * tile_bytes; ++ ++ switch (values->array_mode) { ++ case ARRAY_LINEAR_GENERAL: ++ /* technically tile_width/_height for pitch/height */ ++ *pitch_align = 1; /* tile_width */ ++ *height_align = 1; /* tile_height */ ++ *depth_align = 1; ++ *base_align = 1; ++ break; ++ case ARRAY_LINEAR_ALIGNED: ++ *pitch_align = max((u32)64, (u32)(values->group_size / values->bpe)); ++ *height_align = tile_height; ++ *depth_align = 1; ++ *base_align = values->group_size; ++ break; ++ case ARRAY_1D_TILED_THIN1: ++ *pitch_align = max((u32)tile_width, ++ (u32)(values->group_size / ++ (tile_height * values->bpe * values->nsamples))); ++ *height_align = tile_height; ++ *depth_align = 1; ++ *base_align = values->group_size; ++ break; ++ case ARRAY_2D_TILED_THIN1: ++ *pitch_align = max((u32)macro_tile_width, ++ (u32)(((values->group_size / tile_height) / ++ (values->bpe * values->nsamples)) * ++ values->nbanks)) * tile_width; ++ *height_align = macro_tile_height * tile_height; ++ *depth_align = 1; ++ *base_align = max(macro_tile_bytes, ++ (*pitch_align) * values->bpe * (*height_align) * values->nsamples); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ + static void r600_cs_track_init(struct r600_cs_track *track) + { + int i; +@@ -151,10 +217,12 @@ static void r600_cs_track_init(struct r600_cs_track *track) + track->cb_color_info[i] = 0; + track->cb_color_bo[i] = NULL; + track->cb_color_bo_offset[i] = 0xFFFFFFFF; ++ track->cb_color_bo_mc[i] = 0xFFFFFFFF; + } + track->cb_target_mask = 0xFFFFFFFF; + track->cb_shader_mask = 0xFFFFFFFF; + track->db_bo = NULL; ++ track->db_bo_mc = 0xFFFFFFFF; + /* assume the biggest format and that htile is enabled */ + track->db_depth_info = 7 | (1 << 25); + track->db_depth_view = 0xFFFFC000; +@@ -166,70 +234,58 @@ static void r600_cs_track_init(struct r600_cs_track *track) + static inline int r600_cs_track_validate_cb(struct radeon_cs_parser *p, int i) + { + struct r600_cs_track *track = p->track; +- u32 bpe = 0, pitch, slice_tile_max, size, tmp, height; ++ u32 bpe = 0, slice_tile_max, size, tmp; ++ u32 height, height_align, pitch, pitch_align, depth_align; ++ u64 base_offset, base_align; ++ struct array_mode_checker array_check; + volatile u32 *ib = p->ib->ptr; ++ unsigned array_mode; + + if (G_0280A0_TILE_MODE(track->cb_color_info[i])) { + dev_warn(p->dev, "FMASK or CMASK buffer are not supported by this kernel\n"); + return -EINVAL; + } +- size = radeon_bo_size(track->cb_color_bo[i]); ++ size = radeon_bo_size(track->cb_color_bo[i]) - track->cb_color_bo_offset[i]; + if (r600_bpe_from_format(&bpe, G_0280A0_FORMAT(track->cb_color_info[i]))) { + dev_warn(p->dev, "%s:%d cb invalid format %d for %d (0x%08X)\n", + __func__, __LINE__, G_0280A0_FORMAT(track->cb_color_info[i]), + i, track->cb_color_info[i]); + return -EINVAL; + } +- pitch = (G_028060_PITCH_TILE_MAX(track->cb_color_size[i]) + 1) << 3; ++ /* pitch in pixels */ ++ pitch = (G_028060_PITCH_TILE_MAX(track->cb_color_size[i]) + 1) * 8; + slice_tile_max = G_028060_SLICE_TILE_MAX(track->cb_color_size[i]) + 1; +- if (!pitch) { +- dev_warn(p->dev, "%s:%d cb pitch (%d) for %d invalid (0x%08X)\n", +- __func__, __LINE__, pitch, i, track->cb_color_size[i]); +- return -EINVAL; +- } +- height = size / (pitch * bpe); ++ slice_tile_max *= 64; ++ height = slice_tile_max / pitch; + if (height > 8192) + height = 8192; +- switch (G_0280A0_ARRAY_MODE(track->cb_color_info[i])) { ++ array_mode = G_0280A0_ARRAY_MODE(track->cb_color_info[i]); ++ ++ base_offset = track->cb_color_bo_mc[i] + track->cb_color_bo_offset[i]; ++ array_check.array_mode = array_mode; ++ array_check.group_size = track->group_size; ++ array_check.nbanks = track->nbanks; ++ array_check.npipes = track->npipes; ++ array_check.nsamples = track->nsamples; ++ array_check.bpe = bpe; ++ if (r600_get_array_mode_alignment(&array_check, ++ &pitch_align, &height_align, &depth_align, &base_align)) { ++ dev_warn(p->dev, "%s invalid tiling %d for %d (0x%08X)\n", __func__, ++ G_0280A0_ARRAY_MODE(track->cb_color_info[i]), i, ++ track->cb_color_info[i]); ++ return -EINVAL; ++ } ++ switch (array_mode) { + case V_0280A0_ARRAY_LINEAR_GENERAL: ++ break; + case V_0280A0_ARRAY_LINEAR_ALIGNED: +- if (pitch & 0x3f) { +- dev_warn(p->dev, "%s:%d cb pitch (%d x %d = %d) invalid\n", +- __func__, __LINE__, pitch, bpe, pitch * bpe); +- return -EINVAL; +- } +- if ((pitch * bpe) & (track->group_size - 1)) { +- dev_warn(p->dev, "%s:%d cb pitch (%d) invalid\n", +- __func__, __LINE__, pitch); +- return -EINVAL; +- } + break; + case V_0280A0_ARRAY_1D_TILED_THIN1: +- if ((pitch * 8 * bpe * track->nsamples) & (track->group_size - 1)) { +- dev_warn(p->dev, "%s:%d cb pitch (%d) invalid\n", +- __func__, __LINE__, pitch); +- return -EINVAL; +- } +- height &= ~0x7; +- if (!height) +- height = 8; ++ /* avoid breaking userspace */ ++ if (height > 7) ++ height &= ~0x7; + break; + case V_0280A0_ARRAY_2D_TILED_THIN1: +- if (pitch & ((8 * track->nbanks) - 1)) { +- dev_warn(p->dev, "%s:%d cb pitch (%d) invalid\n", +- __func__, __LINE__, pitch); +- return -EINVAL; +- } +- tmp = pitch * 8 * bpe * track->nsamples; +- tmp = tmp / track->nbanks; +- if (tmp & (track->group_size - 1)) { +- dev_warn(p->dev, "%s:%d cb pitch (%d) invalid\n", +- __func__, __LINE__, pitch); +- return -EINVAL; +- } +- height &= ~((16 * track->npipes) - 1); +- if (!height) +- height = 16 * track->npipes; + break; + default: + dev_warn(p->dev, "%s invalid tiling %d for %d (0x%08X)\n", __func__, +@@ -237,17 +293,43 @@ static inline int r600_cs_track_validate_cb(struct radeon_cs_parser *p, int i) + track->cb_color_info[i]); + return -EINVAL; + } ++ ++ if (!IS_ALIGNED(pitch, pitch_align)) { ++ dev_warn(p->dev, "%s:%d cb pitch (%d) invalid\n", ++ __func__, __LINE__, pitch); ++ return -EINVAL; ++ } ++ if (!IS_ALIGNED(height, height_align)) { ++ dev_warn(p->dev, "%s:%d cb height (%d) invalid\n", ++ __func__, __LINE__, height); ++ return -EINVAL; ++ } ++ if (!IS_ALIGNED(base_offset, base_align)) { ++ dev_warn(p->dev, "%s offset[%d] 0x%llx not aligned\n", __func__, i, base_offset); ++ return -EINVAL; ++ } ++ + /* check offset */ +- tmp = height * pitch; ++ tmp = height * pitch * bpe; + if ((tmp + track->cb_color_bo_offset[i]) > radeon_bo_size(track->cb_color_bo[i])) { +- dev_warn(p->dev, "%s offset[%d] %d to big\n", __func__, i, track->cb_color_bo_offset[i]); +- return -EINVAL; ++ if (array_mode == V_0280A0_ARRAY_LINEAR_GENERAL) { ++ /* the initial DDX does bad things with the CB size occasionally */ ++ /* it rounds up height too far for slice tile max but the BO is smaller */ ++ tmp = (height - 7) * 8 * bpe; ++ if ((tmp + track->cb_color_bo_offset[i]) > radeon_bo_size(track->cb_color_bo[i])) { ++ dev_warn(p->dev, "%s offset[%d] %d %d %lu too big\n", __func__, i, track->cb_color_bo_offset[i], tmp, radeon_bo_size(track->cb_color_bo[i])); ++ return -EINVAL; ++ } ++ } else { ++ dev_warn(p->dev, "%s offset[%d] %d %d %lu too big\n", __func__, i, track->cb_color_bo_offset[i], tmp, radeon_bo_size(track->cb_color_bo[i])); ++ return -EINVAL; ++ } + } + /* limit max tile */ + tmp = (height * pitch) >> 6; + if (tmp < slice_tile_max) + slice_tile_max = tmp; +- tmp = S_028060_PITCH_TILE_MAX((pitch >> 3) - 1) | ++ tmp = S_028060_PITCH_TILE_MAX((pitch / 8) - 1) | + S_028060_SLICE_TILE_MAX(slice_tile_max - 1); + ib[track->cb_color_size_idx[i]] = tmp; + return 0; +@@ -289,7 +371,12 @@ static int r600_cs_track_check(struct radeon_cs_parser *p) + /* Check depth buffer */ + if (G_028800_STENCIL_ENABLE(track->db_depth_control) || + G_028800_Z_ENABLE(track->db_depth_control)) { +- u32 nviews, bpe, ntiles; ++ u32 nviews, bpe, ntiles, size, slice_tile_max; ++ u32 height, height_align, pitch, pitch_align, depth_align; ++ u64 base_offset, base_align; ++ struct array_mode_checker array_check; ++ int array_mode; ++ + if (track->db_bo == NULL) { + dev_warn(p->dev, "z/stencil with no depth buffer\n"); + return -EINVAL; +@@ -321,7 +408,6 @@ static int r600_cs_track_check(struct radeon_cs_parser *p) + dev_warn(p->dev, "z/stencil buffer size not set\n"); + return -EINVAL; + } +- printk_once(KERN_WARNING "You have old & broken userspace please consider updating mesa\n"); + tmp = radeon_bo_size(track->db_bo) - track->db_offset; + tmp = (tmp / bpe) >> 6; + if (!tmp) { +@@ -332,11 +418,63 @@ static int r600_cs_track_check(struct radeon_cs_parser *p) + } + ib[track->db_depth_size_idx] = S_028000_SLICE_TILE_MAX(tmp - 1) | (track->db_depth_size & 0x3FF); + } else { ++ size = radeon_bo_size(track->db_bo); ++ /* pitch in pixels */ ++ pitch = (G_028000_PITCH_TILE_MAX(track->db_depth_size) + 1) * 8; ++ slice_tile_max = G_028000_SLICE_TILE_MAX(track->db_depth_size) + 1; ++ slice_tile_max *= 64; ++ height = slice_tile_max / pitch; ++ if (height > 8192) ++ height = 8192; ++ base_offset = track->db_bo_mc + track->db_offset; ++ array_mode = G_028010_ARRAY_MODE(track->db_depth_info); ++ array_check.array_mode = array_mode; ++ array_check.group_size = track->group_size; ++ array_check.nbanks = track->nbanks; ++ array_check.npipes = track->npipes; ++ array_check.nsamples = track->nsamples; ++ array_check.bpe = bpe; ++ if (r600_get_array_mode_alignment(&array_check, ++ &pitch_align, &height_align, &depth_align, &base_align)) { ++ dev_warn(p->dev, "%s invalid tiling %d (0x%08X)\n", __func__, ++ G_028010_ARRAY_MODE(track->db_depth_info), ++ track->db_depth_info); ++ return -EINVAL; ++ } ++ switch (array_mode) { ++ case V_028010_ARRAY_1D_TILED_THIN1: ++ /* don't break userspace */ ++ height &= ~0x7; ++ break; ++ case V_028010_ARRAY_2D_TILED_THIN1: ++ break; ++ default: ++ dev_warn(p->dev, "%s invalid tiling %d (0x%08X)\n", __func__, ++ G_028010_ARRAY_MODE(track->db_depth_info), ++ track->db_depth_info); ++ return -EINVAL; ++ } ++ ++ if (!IS_ALIGNED(pitch, pitch_align)) { ++ dev_warn(p->dev, "%s:%d db pitch (%d) invalid\n", ++ __func__, __LINE__, pitch); ++ return -EINVAL; ++ } ++ if (!IS_ALIGNED(height, height_align)) { ++ dev_warn(p->dev, "%s:%d db height (%d) invalid\n", ++ __func__, __LINE__, height); ++ return -EINVAL; ++ } ++ if (!IS_ALIGNED(base_offset, base_align)) { ++ dev_warn(p->dev, "%s offset[%d] 0x%llx not aligned\n", __func__, i, base_offset); ++ return -EINVAL; ++ } ++ + ntiles = G_028000_SLICE_TILE_MAX(track->db_depth_size) + 1; + nviews = G_028004_SLICE_MAX(track->db_depth_view) + 1; + tmp = ntiles * bpe * 64 * nviews; + if ((tmp + track->db_offset) > radeon_bo_size(track->db_bo)) { +- dev_warn(p->dev, "z/stencil buffer too small (0x%08X %d %d %d -> %d have %ld)\n", ++ dev_warn(p->dev, "z/stencil buffer too small (0x%08X %d %d %d -> %u have %lu)\n", + track->db_depth_size, ntiles, nviews, bpe, tmp + track->db_offset, + radeon_bo_size(track->db_bo)); + return -EINVAL; +@@ -724,7 +862,25 @@ static inline int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx + track->db_depth_control = radeon_get_ib_value(p, idx); + break; + case R_028010_DB_DEPTH_INFO: +- track->db_depth_info = radeon_get_ib_value(p, idx); ++ if (r600_cs_packet_next_is_pkt3_nop(p)) { ++ r = r600_cs_packet_next_reloc(p, &reloc); ++ if (r) { ++ dev_warn(p->dev, "bad SET_CONTEXT_REG " ++ "0x%04X\n", reg); ++ return -EINVAL; ++ } ++ track->db_depth_info = radeon_get_ib_value(p, idx); ++ ib[idx] &= C_028010_ARRAY_MODE; ++ track->db_depth_info &= C_028010_ARRAY_MODE; ++ if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) { ++ ib[idx] |= S_028010_ARRAY_MODE(V_028010_ARRAY_2D_TILED_THIN1); ++ track->db_depth_info |= S_028010_ARRAY_MODE(V_028010_ARRAY_2D_TILED_THIN1); ++ } else { ++ ib[idx] |= S_028010_ARRAY_MODE(V_028010_ARRAY_1D_TILED_THIN1); ++ track->db_depth_info |= S_028010_ARRAY_MODE(V_028010_ARRAY_1D_TILED_THIN1); ++ } ++ } else ++ track->db_depth_info = radeon_get_ib_value(p, idx); + break; + case R_028004_DB_DEPTH_VIEW: + track->db_depth_view = radeon_get_ib_value(p, idx); +@@ -757,8 +913,25 @@ static inline int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx + case R_0280B4_CB_COLOR5_INFO: + case R_0280B8_CB_COLOR6_INFO: + case R_0280BC_CB_COLOR7_INFO: +- tmp = (reg - R_0280A0_CB_COLOR0_INFO) / 4; +- track->cb_color_info[tmp] = radeon_get_ib_value(p, idx); ++ if (r600_cs_packet_next_is_pkt3_nop(p)) { ++ r = r600_cs_packet_next_reloc(p, &reloc); ++ if (r) { ++ dev_err(p->dev, "bad SET_CONTEXT_REG 0x%04X\n", reg); ++ return -EINVAL; ++ } ++ tmp = (reg - R_0280A0_CB_COLOR0_INFO) / 4; ++ track->cb_color_info[tmp] = radeon_get_ib_value(p, idx); ++ if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) { ++ ib[idx] |= S_0280A0_ARRAY_MODE(V_0280A0_ARRAY_2D_TILED_THIN1); ++ track->cb_color_info[tmp] |= S_0280A0_ARRAY_MODE(V_0280A0_ARRAY_2D_TILED_THIN1); ++ } else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) { ++ ib[idx] |= S_0280A0_ARRAY_MODE(V_0280A0_ARRAY_1D_TILED_THIN1); ++ track->cb_color_info[tmp] |= S_0280A0_ARRAY_MODE(V_0280A0_ARRAY_1D_TILED_THIN1); ++ } ++ } else { ++ tmp = (reg - R_0280A0_CB_COLOR0_INFO) / 4; ++ track->cb_color_info[tmp] = radeon_get_ib_value(p, idx); ++ } + break; + case R_028060_CB_COLOR0_SIZE: + case R_028064_CB_COLOR1_SIZE: +@@ -796,8 +969,6 @@ static inline int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx + return -EINVAL; + } + ib[idx] = track->cb_color_base_last[tmp]; +- printk_once(KERN_WARNING "You have old & broken userspace " +- "please consider updating mesa & xf86-video-ati\n"); + track->cb_color_frag_bo[tmp] = track->cb_color_bo[tmp]; + } else { + r = r600_cs_packet_next_reloc(p, &reloc); +@@ -824,8 +995,6 @@ static inline int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx + return -EINVAL; + } + ib[idx] = track->cb_color_base_last[tmp]; +- printk_once(KERN_WARNING "You have old & broken userspace " +- "please consider updating mesa & xf86-video-ati\n"); + track->cb_color_tile_bo[tmp] = track->cb_color_bo[tmp]; + } else { + r = r600_cs_packet_next_reloc(p, &reloc); +@@ -852,10 +1021,11 @@ static inline int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx + return -EINVAL; + } + tmp = (reg - CB_COLOR0_BASE) / 4; +- track->cb_color_bo_offset[tmp] = radeon_get_ib_value(p, idx); ++ track->cb_color_bo_offset[tmp] = radeon_get_ib_value(p, idx) << 8; + ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + track->cb_color_base_last[tmp] = ib[idx]; + track->cb_color_bo[tmp] = reloc->robj; ++ track->cb_color_bo_mc[tmp] = reloc->lobj.gpu_offset; + break; + case DB_DEPTH_BASE: + r = r600_cs_packet_next_reloc(p, &reloc); +@@ -864,9 +1034,10 @@ static inline int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx + "0x%04X\n", reg); + return -EINVAL; + } +- track->db_offset = radeon_get_ib_value(p, idx); ++ track->db_offset = radeon_get_ib_value(p, idx) << 8; + ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + track->db_bo = reloc->robj; ++ track->db_bo_mc = reloc->lobj.gpu_offset; + break; + case DB_HTILE_DATA_BASE: + case SQ_PGM_START_FS: +@@ -946,8 +1117,9 @@ static inline unsigned minify(unsigned size, unsigned levels) + } + + static void r600_texture_size(unsigned nfaces, unsigned blevel, unsigned nlevels, +- unsigned w0, unsigned h0, unsigned d0, unsigned bpe, +- unsigned *l0_size, unsigned *mipmap_size) ++ unsigned w0, unsigned h0, unsigned d0, unsigned bpe, ++ unsigned pitch_align, ++ unsigned *l0_size, unsigned *mipmap_size) + { + unsigned offset, i, level, face; + unsigned width, height, depth, rowstride, size; +@@ -960,18 +1132,18 @@ static void r600_texture_size(unsigned nfaces, unsigned blevel, unsigned nlevels + height = minify(h0, i); + depth = minify(d0, i); + for(face = 0; face < nfaces; face++) { +- rowstride = ((width * bpe) + 255) & ~255; ++ rowstride = ALIGN((width * bpe), pitch_align); + size = height * rowstride * depth; + offset += size; + offset = (offset + 0x1f) & ~0x1f; + } + } +- *l0_size = (((w0 * bpe) + 255) & ~255) * h0 * d0; ++ *l0_size = ALIGN((w0 * bpe), pitch_align) * h0 * d0; + *mipmap_size = offset; +- if (!blevel) +- *mipmap_size -= *l0_size; + if (!nlevels) + *mipmap_size = *l0_size; ++ if (!blevel) ++ *mipmap_size -= *l0_size; + } + + /** +@@ -985,16 +1157,32 @@ static void r600_texture_size(unsigned nfaces, unsigned blevel, unsigned nlevels + * the texture and mipmap bo object are big enough to cover this resource. + */ + static inline int r600_check_texture_resource(struct radeon_cs_parser *p, u32 idx, +- struct radeon_bo *texture, +- struct radeon_bo *mipmap) ++ struct radeon_bo *texture, ++ struct radeon_bo *mipmap, ++ u64 base_offset, ++ u64 mip_offset, ++ u32 tiling_flags) + { ++ struct r600_cs_track *track = p->track; + u32 nfaces, nlevels, blevel, w0, h0, d0, bpe = 0; + u32 word0, word1, l0_size, mipmap_size; ++ u32 height_align, pitch, pitch_align, depth_align; ++ u64 base_align; ++ struct array_mode_checker array_check; + + /* on legacy kernel we don't perform advanced check */ + if (p->rdev == NULL) + return 0; ++ ++ /* convert to bytes */ ++ base_offset <<= 8; ++ mip_offset <<= 8; ++ + word0 = radeon_get_ib_value(p, idx + 0); ++ if (tiling_flags & RADEON_TILING_MACRO) ++ word0 |= S_038000_TILE_MODE(V_038000_ARRAY_2D_TILED_THIN1); ++ else if (tiling_flags & RADEON_TILING_MICRO) ++ word0 |= S_038000_TILE_MODE(V_038000_ARRAY_1D_TILED_THIN1); + word1 = radeon_get_ib_value(p, idx + 1); + w0 = G_038000_TEX_WIDTH(word0) + 1; + h0 = G_038004_TEX_HEIGHT(word1) + 1; +@@ -1021,24 +1209,59 @@ static inline int r600_check_texture_resource(struct radeon_cs_parser *p, u32 i + __func__, __LINE__, G_038004_DATA_FORMAT(word1)); + return -EINVAL; + } ++ ++ /* pitch in texels */ ++ pitch = (G_038000_PITCH(word0) + 1) * 8; ++ array_check.array_mode = G_038000_TILE_MODE(word0); ++ array_check.group_size = track->group_size; ++ array_check.nbanks = track->nbanks; ++ array_check.npipes = track->npipes; ++ array_check.nsamples = 1; ++ array_check.bpe = bpe; ++ if (r600_get_array_mode_alignment(&array_check, ++ &pitch_align, &height_align, &depth_align, &base_align)) { ++ dev_warn(p->dev, "%s:%d tex array mode (%d) invalid\n", ++ __func__, __LINE__, G_038000_TILE_MODE(word0)); ++ return -EINVAL; ++ } ++ ++ /* XXX check height as well... */ ++ ++ if (!IS_ALIGNED(pitch, pitch_align)) { ++ dev_warn(p->dev, "%s:%d tex pitch (%d) invalid\n", ++ __func__, __LINE__, pitch); ++ return -EINVAL; ++ } ++ if (!IS_ALIGNED(base_offset, base_align)) { ++ dev_warn(p->dev, "%s:%d tex base offset (0x%llx) invalid\n", ++ __func__, __LINE__, base_offset); ++ return -EINVAL; ++ } ++ if (!IS_ALIGNED(mip_offset, base_align)) { ++ dev_warn(p->dev, "%s:%d tex mip offset (0x%llx) invalid\n", ++ __func__, __LINE__, mip_offset); ++ return -EINVAL; ++ } ++ + word0 = radeon_get_ib_value(p, idx + 4); + word1 = radeon_get_ib_value(p, idx + 5); + blevel = G_038010_BASE_LEVEL(word0); + nlevels = G_038014_LAST_LEVEL(word1); +- r600_texture_size(nfaces, blevel, nlevels, w0, h0, d0, bpe, &l0_size, &mipmap_size); ++ r600_texture_size(nfaces, blevel, nlevels, w0, h0, d0, bpe, ++ (pitch_align * bpe), ++ &l0_size, &mipmap_size); + /* using get ib will give us the offset into the texture bo */ +- word0 = radeon_get_ib_value(p, idx + 2); ++ word0 = radeon_get_ib_value(p, idx + 2) << 8; + if ((l0_size + word0) > radeon_bo_size(texture)) { + dev_warn(p->dev, "texture bo too small (%d %d %d %d -> %d have %ld)\n", + w0, h0, bpe, word0, l0_size, radeon_bo_size(texture)); + return -EINVAL; + } + /* using get ib will give us the offset into the mipmap bo */ +- word0 = radeon_get_ib_value(p, idx + 3); ++ word0 = radeon_get_ib_value(p, idx + 3) << 8; + if ((mipmap_size + word0) > radeon_bo_size(mipmap)) { +- dev_warn(p->dev, "mipmap bo too small (%d %d %d %d %d %d -> %d have %ld)\n", +- w0, h0, bpe, blevel, nlevels, word0, mipmap_size, radeon_bo_size(texture)); +- return -EINVAL; ++ /*dev_warn(p->dev, "mipmap bo too small (%d %d %d %d %d %d -> %d have %ld)\n", ++ w0, h0, bpe, blevel, nlevels, word0, mipmap_size, radeon_bo_size(texture));*/ + } + return 0; + } +@@ -1228,7 +1451,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p, + } + for (i = 0; i < (pkt->count / 7); i++) { + struct radeon_bo *texture, *mipmap; +- u32 size, offset; ++ u32 size, offset, base_offset, mip_offset; + + switch (G__SQ_VTX_CONSTANT_TYPE(radeon_get_ib_value(p, idx+(i*7)+6+1))) { + case SQ_TEX_VTX_VALID_TEXTURE: +@@ -1238,7 +1461,11 @@ static int r600_packet3_check(struct radeon_cs_parser *p, + DRM_ERROR("bad SET_RESOURCE\n"); + return -EINVAL; + } +- ib[idx+1+(i*7)+2] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); ++ base_offset = (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); ++ if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) ++ ib[idx+1+(i*7)+0] |= S_038000_TILE_MODE(V_038000_ARRAY_2D_TILED_THIN1); ++ else if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) ++ ib[idx+1+(i*7)+0] |= S_038000_TILE_MODE(V_038000_ARRAY_1D_TILED_THIN1); + texture = reloc->robj; + /* tex mip base */ + r = r600_cs_packet_next_reloc(p, &reloc); +@@ -1246,12 +1473,17 @@ static int r600_packet3_check(struct radeon_cs_parser *p, + DRM_ERROR("bad SET_RESOURCE\n"); + return -EINVAL; + } +- ib[idx+1+(i*7)+3] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); ++ mip_offset = (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff); + mipmap = reloc->robj; + r = r600_check_texture_resource(p, idx+(i*7)+1, +- texture, mipmap); ++ texture, mipmap, ++ base_offset + radeon_get_ib_value(p, idx+1+(i*7)+2), ++ mip_offset + radeon_get_ib_value(p, idx+1+(i*7)+3), ++ reloc->lobj.tiling_flags); + if (r) + return r; ++ ib[idx+1+(i*7)+2] += base_offset; ++ ib[idx+1+(i*7)+3] += mip_offset; + break; + case SQ_TEX_VTX_VALID_BUFFER: + /* vtx base */ +@@ -1261,10 +1493,11 @@ static int r600_packet3_check(struct radeon_cs_parser *p, + return -EINVAL; + } + offset = radeon_get_ib_value(p, idx+1+(i*7)+0); +- size = radeon_get_ib_value(p, idx+1+(i*7)+1); ++ size = radeon_get_ib_value(p, idx+1+(i*7)+1) + 1; + if (p->rdev && (size + offset) > radeon_bo_size(reloc->robj)) { + /* force size to size of the buffer */ +- dev_warn(p->dev, "vbo resource seems too big for the bo\n"); ++ dev_warn(p->dev, "vbo resource seems too big (%d) for the bo (%ld)\n", ++ size + offset, radeon_bo_size(reloc->robj)); + ib[idx+1+(i*7)+1] = radeon_bo_size(reloc->robj); + } + ib[idx+1+(i*7)+0] += (u32)((reloc->lobj.gpu_offset) & 0xffffffff); +diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h +index 84bc28e..9577945 100644 +--- a/drivers/gpu/drm/radeon/r600d.h ++++ b/drivers/gpu/drm/radeon/r600d.h +@@ -51,6 +51,12 @@ + #define PTE_READABLE (1 << 5) + #define PTE_WRITEABLE (1 << 6) + ++/* tiling bits */ ++#define ARRAY_LINEAR_GENERAL 0x00000000 ++#define ARRAY_LINEAR_ALIGNED 0x00000001 ++#define ARRAY_1D_TILED_THIN1 0x00000002 ++#define ARRAY_2D_TILED_THIN1 0x00000004 ++ + /* Registers */ + #define ARB_POP 0x2418 + #define ENABLE_TC128 (1 << 30) +@@ -1155,6 +1161,10 @@ + #define S_038000_TILE_MODE(x) (((x) & 0xF) << 3) + #define G_038000_TILE_MODE(x) (((x) >> 3) & 0xF) + #define C_038000_TILE_MODE 0xFFFFFF87 ++#define V_038000_ARRAY_LINEAR_GENERAL 0x00000000 ++#define V_038000_ARRAY_LINEAR_ALIGNED 0x00000001 ++#define V_038000_ARRAY_1D_TILED_THIN1 0x00000002 ++#define V_038000_ARRAY_2D_TILED_THIN1 0x00000004 + #define S_038000_TILE_TYPE(x) (((x) & 0x1) << 7) + #define G_038000_TILE_TYPE(x) (((x) >> 7) & 0x1) + #define C_038000_TILE_TYPE 0xFFFFFF7F +@@ -1358,6 +1368,8 @@ + #define S_028010_ARRAY_MODE(x) (((x) & 0xF) << 15) + #define G_028010_ARRAY_MODE(x) (((x) >> 15) & 0xF) + #define C_028010_ARRAY_MODE 0xFFF87FFF ++#define V_028010_ARRAY_1D_TILED_THIN1 0x00000002 ++#define V_028010_ARRAY_2D_TILED_THIN1 0x00000004 + #define S_028010_TILE_SURFACE_ENABLE(x) (((x) & 0x1) << 25) + #define G_028010_TILE_SURFACE_ENABLE(x) (((x) >> 25) & 0x1) + #define C_028010_TILE_SURFACE_ENABLE 0xFDFFFFFF diff --git a/drm-revert-drm-fbdev-rework-output-polling-to-be-back-in-core.patch b/drm-revert-drm-fbdev-rework-output-polling-to-be-back-in-core.patch deleted file mode 100644 index 481a08fdc..000000000 --- a/drm-revert-drm-fbdev-rework-output-polling-to-be-back-in-core.patch +++ /dev/null @@ -1,958 +0,0 @@ -From 5b904034b0ab5195d971b139d0c0b67ab21b063c Mon Sep 17 00:00:00 2001 -From: Kyle McMartin -Date: Mon, 21 Jun 2010 20:33:16 +0100 -Subject: Revert "drm/fbdev: rework output polling to be back in the core. (v4)" - -This reverts commit eb1f8e4f3be898df808e2dfc131099f5831d491d. - -Conflicts: - - drivers/gpu/drm/drm_crtc_helper.c - drivers/gpu/drm/i915/i915_dma.c - drivers/gpu/drm/i915/intel_fb.c - drivers/gpu/drm/nouveau/nouveau_fbcon.c - drivers/gpu/drm/radeon/radeon_fb.c - include/drm/drm_crtc_helper.h ---- - drivers/gpu/drm/Kconfig | 2 +- - drivers/gpu/drm/drm_crtc_helper.c | 111 ------------------------ - drivers/gpu/drm/drm_fb_helper.c | 123 +++++++++++++++++++++++---- - drivers/gpu/drm/i915/i915_dma.c | 1 - - drivers/gpu/drm/i915/i915_irq.c | 3 +- - drivers/gpu/drm/i915/intel_crt.c | 5 - - drivers/gpu/drm/i915/intel_display.c | 2 - - drivers/gpu/drm/i915/intel_dp.c | 2 - - drivers/gpu/drm/i915/intel_drv.h | 2 +- - drivers/gpu/drm/i915/intel_fb.c | 14 ++-- - drivers/gpu/drm/i915/intel_hdmi.c | 1 - - drivers/gpu/drm/i915/intel_sdvo.c | 2 - - drivers/gpu/drm/nouveau/nouveau_connector.c | 12 --- - drivers/gpu/drm/nouveau/nouveau_display.c | 1 - - drivers/gpu/drm/nouveau/nouveau_fbcon.c | 13 ++- - drivers/gpu/drm/nouveau/nouveau_fbcon.h | 2 +- - drivers/gpu/drm/nouveau/nouveau_state.c | 5 +- - drivers/gpu/drm/nouveau/nv50_display.c | 2 +- - drivers/gpu/drm/radeon/radeon_connectors.c | 13 --- - drivers/gpu/drm/radeon/radeon_display.c | 10 -- - drivers/gpu/drm/radeon/radeon_fb.c | 15 +++- - drivers/gpu/drm/radeon/radeon_irq_kms.c | 5 +- - drivers/gpu/drm/radeon/radeon_mode.h | 3 +- - include/drm/drm_crtc.h | 17 ---- - include/drm/drm_crtc_helper.h | 6 -- - include/drm/drm_fb_helper.h | 13 +++- - 26 files changed, 155 insertions(+), 230 deletions(-) - -diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig -index c2711c6..a51a1e4 100644 ---- a/drivers/gpu/drm/Kconfig -+++ b/drivers/gpu/drm/Kconfig -@@ -9,7 +9,6 @@ menuconfig DRM - depends on (AGP || AGP=n) && PCI && !EMULATED_CMPXCHG && MMU - select I2C - select I2C_ALGOBIT -- select SLOW_WORK - help - Kernel-level support for the Direct Rendering Infrastructure (DRI) - introduced in XFree86 4.0. If you say Y here, you need to select -@@ -24,6 +23,7 @@ config DRM_KMS_HELPER - depends on DRM - select FB - select FRAMEBUFFER_CONSOLE if !EMBEDDED -+ select SLOW_WORK - help - FB and CRTC helpers for KMS drivers. - -diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c -index 9b2a541..b142ac2 100644 ---- a/drivers/gpu/drm/drm_crtc_helper.c -+++ b/drivers/gpu/drm/drm_crtc_helper.c -@@ -807,114 +807,3 @@ int drm_helper_resume_force_mode(struct drm_device *dev) - return 0; - } - EXPORT_SYMBOL(drm_helper_resume_force_mode); -- --static struct slow_work_ops output_poll_ops; -- --#define DRM_OUTPUT_POLL_PERIOD (10*HZ) --static void output_poll_execute(struct slow_work *work) --{ -- struct delayed_slow_work *delayed_work = container_of(work, struct delayed_slow_work, work); -- struct drm_device *dev = container_of(delayed_work, struct drm_device, mode_config.output_poll_slow_work); -- struct drm_connector *connector; -- enum drm_connector_status old_status, status; -- bool repoll = false, changed = false; -- int ret; -- -- mutex_lock(&dev->mode_config.mutex); -- list_for_each_entry(connector, &dev->mode_config.connector_list, head) { -- -- /* if this is HPD or polled don't check it - -- TV out for instance */ -- if (!connector->polled) -- continue; -- -- else if (connector->polled & (DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT)) -- repoll = true; -- -- old_status = connector->status; -- /* if we are connected and don't want to poll for disconnect -- skip it */ -- if (old_status == connector_status_connected && -- !(connector->polled & DRM_CONNECTOR_POLL_DISCONNECT) && -- !(connector->polled & DRM_CONNECTOR_POLL_HPD)) -- continue; -- -- status = connector->funcs->detect(connector); -- if (old_status != status) -- changed = true; -- } -- -- mutex_unlock(&dev->mode_config.mutex); -- -- if (changed) { -- /* send a uevent + call fbdev */ -- drm_sysfs_hotplug_event(dev); -- if (dev->mode_config.funcs->output_poll_changed) -- dev->mode_config.funcs->output_poll_changed(dev); -- } -- -- if (repoll) { -- ret = delayed_slow_work_enqueue(delayed_work, DRM_OUTPUT_POLL_PERIOD); -- if (ret) -- DRM_ERROR("delayed enqueue failed %d\n", ret); -- } --} -- --void drm_kms_helper_poll_disable(struct drm_device *dev) --{ -- if (!dev->mode_config.poll_enabled) -- return; -- delayed_slow_work_cancel(&dev->mode_config.output_poll_slow_work); --} --EXPORT_SYMBOL(drm_kms_helper_poll_disable); -- --void drm_kms_helper_poll_enable(struct drm_device *dev) --{ -- bool poll = false; -- struct drm_connector *connector; -- int ret; -- -- list_for_each_entry(connector, &dev->mode_config.connector_list, head) { -- if (connector->polled) -- poll = true; -- } -- -- if (poll) { -- ret = delayed_slow_work_enqueue(&dev->mode_config.output_poll_slow_work, DRM_OUTPUT_POLL_PERIOD); -- if (ret) -- DRM_ERROR("delayed enqueue failed %d\n", ret); -- } --} --EXPORT_SYMBOL(drm_kms_helper_poll_enable); -- --void drm_kms_helper_poll_init(struct drm_device *dev) --{ -- slow_work_register_user(THIS_MODULE); -- delayed_slow_work_init(&dev->mode_config.output_poll_slow_work, -- &output_poll_ops); -- dev->mode_config.poll_enabled = true; -- -- drm_kms_helper_poll_enable(dev); --} --EXPORT_SYMBOL(drm_kms_helper_poll_init); -- --void drm_kms_helper_poll_fini(struct drm_device *dev) --{ -- drm_kms_helper_poll_disable(dev); -- slow_work_unregister_user(THIS_MODULE); --} --EXPORT_SYMBOL(drm_kms_helper_poll_fini); -- --void drm_helper_hpd_irq_event(struct drm_device *dev) --{ -- if (!dev->mode_config.poll_enabled) -- return; -- delayed_slow_work_cancel(&dev->mode_config.output_poll_slow_work); -- /* schedule a slow work asap */ -- delayed_slow_work_enqueue(&dev->mode_config.output_poll_slow_work, 0); --} --EXPORT_SYMBOL(drm_helper_hpd_irq_event); -- --static struct slow_work_ops output_poll_ops = { -- .execute = output_poll_execute, --}; -diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c -index 08c4c92..dcc6601 100644 ---- a/drivers/gpu/drm/drm_fb_helper.c -+++ b/drivers/gpu/drm/drm_fb_helper.c -@@ -42,6 +42,8 @@ MODULE_LICENSE("GPL and additional rights"); - - static LIST_HEAD(kernel_fb_helper_list); - -+static struct slow_work_ops output_status_change_ops; -+ - /* simple single crtc case helper function */ - int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) - { -@@ -423,13 +425,19 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper) - - int drm_fb_helper_init(struct drm_device *dev, - struct drm_fb_helper *fb_helper, -- int crtc_count, int max_conn_count) -+ int crtc_count, int max_conn_count, -+ bool polled) - { - struct drm_crtc *crtc; - int ret = 0; - int i; - - fb_helper->dev = dev; -+ fb_helper->poll_enabled = polled; -+ -+ slow_work_register_user(THIS_MODULE); -+ delayed_slow_work_init(&fb_helper->output_status_change_slow_work, -+ &output_status_change_ops); - - INIT_LIST_HEAD(&fb_helper->kernel_fb_list); - -@@ -486,6 +494,8 @@ void drm_fb_helper_fini(struct drm_fb_helper *fb_helper) - - drm_fb_helper_crtc_free(fb_helper); - -+ delayed_slow_work_cancel(&fb_helper->output_status_change_slow_work); -+ slow_work_unregister_user(THIS_MODULE); - } - EXPORT_SYMBOL(drm_fb_helper_fini); - -@@ -703,7 +713,7 @@ int drm_fb_helper_set_par(struct fb_info *info) - - if (fb_helper->delayed_hotplug) { - fb_helper->delayed_hotplug = false; -- drm_fb_helper_hotplug_event(fb_helper); -+ delayed_slow_work_enqueue(&fb_helper->output_status_change_slow_work, 0); - } - return 0; - } -@@ -816,7 +826,7 @@ int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, - if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) { - /* hmm everyone went away - assume VGA cable just fell out - and will come back later. */ -- DRM_INFO("Cannot find any crtc or sizes - going 1024x768\n"); -+ DRM_ERROR("Cannot find any crtc or sizes - going 1024x768\n"); - sizes.fb_width = sizes.surface_width = 1024; - sizes.fb_height = sizes.surface_height = 768; - } -@@ -1362,7 +1372,12 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel) - * we shouldn't end up with no modes here. - */ - if (count == 0) { -- printk(KERN_INFO "No connectors reported connected with modes\n"); -+ if (fb_helper->poll_enabled) { -+ delayed_slow_work_enqueue(&fb_helper->output_status_change_slow_work, -+ 5*HZ); -+ printk(KERN_INFO "No connectors reported connected with modes - started polling\n"); -+ } else -+ printk(KERN_INFO "No connectors reported connected with modes\n"); - } - drm_setup_crtcs(fb_helper); - -@@ -1370,16 +1385,71 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel) - } - EXPORT_SYMBOL(drm_fb_helper_initial_config); - --bool drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) -+/* we got a hotplug irq - need to update fbcon */ -+void drm_helper_fb_hpd_irq_event(struct drm_fb_helper *fb_helper) -+{ -+ /* if we don't have the fbdev registered yet do nothing */ -+ if (!fb_helper->fbdev) -+ return; -+ -+ /* schedule a slow work asap */ -+ delayed_slow_work_enqueue(&fb_helper->output_status_change_slow_work, 0); -+} -+EXPORT_SYMBOL(drm_helper_fb_hpd_irq_event); -+ -+bool drm_helper_fb_hotplug_event(struct drm_fb_helper *fb_helper, bool polled) - { - int count = 0; -+ int ret; - u32 max_width, max_height, bpp_sel; -- bool bound = false, crtcs_bound = false; -- struct drm_crtc *crtc; - - if (!fb_helper->fb) - return false; -+ DRM_DEBUG_KMS("\n"); -+ -+ max_width = fb_helper->fb->width; -+ max_height = fb_helper->fb->height; -+ bpp_sel = fb_helper->fb->bits_per_pixel; -+ -+ count = drm_fb_helper_probe_connector_modes(fb_helper, max_width, -+ max_height); -+ if (fb_helper->poll_enabled && !polled) { -+ if (count) { -+ delayed_slow_work_cancel(&fb_helper->output_status_change_slow_work); -+ } else { -+ ret = delayed_slow_work_enqueue(&fb_helper->output_status_change_slow_work, 5*HZ); -+ } -+ } -+ drm_setup_crtcs(fb_helper); -+ -+ return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); -+} -+EXPORT_SYMBOL(drm_helper_fb_hotplug_event); -+ -+/* -+ * delayed work queue execution function -+ * - check if fbdev is actually in use on the gpu -+ * - if not set delayed flag and repoll if necessary -+ * - check for connector status change -+ * - repoll if 0 modes found -+ *- call driver output status changed notifier -+ */ -+static void output_status_change_execute(struct slow_work *work) -+{ -+ struct delayed_slow_work *delayed_work = container_of(work, struct delayed_slow_work, work); -+ struct drm_fb_helper *fb_helper = container_of(delayed_work, struct drm_fb_helper, output_status_change_slow_work); -+ struct drm_connector *connector; -+ enum drm_connector_status old_status, status; -+ bool repoll, changed = false; -+ int ret; -+ int i; -+ bool bound = false, crtcs_bound = false; -+ struct drm_crtc *crtc; - -+ repoll = fb_helper->poll_enabled; -+ -+ /* first of all check the fbcon framebuffer is actually bound to any crtc */ -+ /* take into account that no crtc at all maybe bound */ - list_for_each_entry(crtc, &fb_helper->dev->mode_config.crtc_list, head) { - if (crtc->fb) - crtcs_bound = true; -@@ -1387,21 +1457,38 @@ bool drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) - bound = true; - } - -- if (!bound && crtcs_bound) { -+ if (bound == false && crtcs_bound) { - fb_helper->delayed_hotplug = true; -- return false; -+ goto requeue; - } -- DRM_DEBUG_KMS("\n"); - -- max_width = fb_helper->fb->width; -- max_height = fb_helper->fb->height; -- bpp_sel = fb_helper->fb->bits_per_pixel; -+ for (i = 0; i < fb_helper->connector_count; i++) { -+ connector = fb_helper->connector_info[i]->connector; -+ old_status = connector->status; -+ status = connector->funcs->detect(connector); -+ if (old_status != status) { -+ changed = true; -+ } -+ if (status == connector_status_connected && repoll) { -+ DRM_DEBUG("%s is connected - stop polling\n", drm_get_connector_name(connector)); -+ repoll = false; -+ } -+ } - -- count = drm_fb_helper_probe_connector_modes(fb_helper, max_width, -- max_height); -- drm_setup_crtcs(fb_helper); -+ if (changed) { -+ if (fb_helper->funcs->fb_output_status_changed) -+ fb_helper->funcs->fb_output_status_changed(fb_helper); -+ } - -- return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); -+requeue: -+ if (repoll) { -+ ret = delayed_slow_work_enqueue(delayed_work, 5*HZ); -+ if (ret) -+ DRM_ERROR("delayed enqueue failed %d\n", ret); -+ } - } --EXPORT_SYMBOL(drm_fb_helper_hotplug_event); -+ -+static struct slow_work_ops output_status_change_ops = { -+ .execute = output_status_change_execute, -+}; - -diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c -index 59a2bf8..76ace2d 100644 ---- a/drivers/gpu/drm/i915/i915_dma.c -+++ b/drivers/gpu/drm/i915/i915_dma.c -@@ -1430,7 +1430,6 @@ static int i915_load_modeset_init(struct drm_device *dev, - if (ret) - goto cleanup_irq; - -- drm_kms_helper_poll_init(dev); - return 0; - - cleanup_irq: -diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c -index 2479be0..6350bd3 100644 ---- a/drivers/gpu/drm/i915/i915_irq.c -+++ b/drivers/gpu/drm/i915/i915_irq.c -@@ -271,7 +271,8 @@ static void i915_hotplug_work_func(struct work_struct *work) - } - } - /* Just fire off a uevent and let userspace tell us what to do */ -- drm_helper_hpd_irq_event(dev); -+ intelfb_hotplug(dev, false); -+ drm_sysfs_hotplug_event(dev); - } - - static void i915_handle_rps_change(struct drm_device *dev) -diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c -index 22ff384..125eded 100644 ---- a/drivers/gpu/drm/i915/intel_crt.c -+++ b/drivers/gpu/drm/i915/intel_crt.c -@@ -584,10 +584,5 @@ void intel_crt_init(struct drm_device *dev) - - drm_sysfs_connector_add(connector); - -- if (I915_HAS_HOTPLUG(dev)) -- connector->polled = DRM_CONNECTOR_POLL_HPD; -- else -- connector->polled = DRM_CONNECTOR_POLL_CONNECT; -- - dev_priv->hotplug_supported_mask |= CRT_HOTPLUG_INT_STATUS; - } -diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c -index d753257..70537cf 100644 ---- a/drivers/gpu/drm/i915/intel_display.c -+++ b/drivers/gpu/drm/i915/intel_display.c -@@ -5036,7 +5036,6 @@ intel_user_framebuffer_create(struct drm_device *dev, - - static const struct drm_mode_config_funcs intel_mode_funcs = { - .fb_create = intel_user_framebuffer_create, -- .output_poll_changed = intel_fb_output_poll_changed, - }; - - static struct drm_gem_object * -@@ -5538,7 +5537,6 @@ void intel_modeset_cleanup(struct drm_device *dev) - - mutex_lock(&dev->struct_mutex); - -- drm_kms_helper_poll_fini(dev); - intel_fbdev_fini(dev); - - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { -diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c -index 49b54f0..1815df5 100644 ---- a/drivers/gpu/drm/i915/intel_dp.c -+++ b/drivers/gpu/drm/i915/intel_dp.c -@@ -1393,8 +1393,6 @@ intel_dp_init(struct drm_device *dev, int output_reg) - DRM_MODE_CONNECTOR_DisplayPort); - drm_connector_helper_add(connector, &intel_dp_connector_helper_funcs); - -- connector->polled = DRM_CONNECTOR_POLL_HPD; -- - if (output_reg == DP_A) - intel_encoder->type = INTEL_OUTPUT_EDP; - else -diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h -index df931f7..3230e8d 100644 ---- a/drivers/gpu/drm/i915/intel_drv.h -+++ b/drivers/gpu/drm/i915/intel_drv.h -@@ -235,5 +235,5 @@ extern int intel_overlay_put_image(struct drm_device *dev, void *data, - extern int intel_overlay_attrs(struct drm_device *dev, void *data, - struct drm_file *file_priv); - --extern void intel_fb_output_poll_changed(struct drm_device *dev); -+void intelfb_hotplug(struct drm_device *dev, bool polled); - #endif /* __INTEL_DRV_H__ */ -diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c -index c3c5052..79098b3 100644 ---- a/drivers/gpu/drm/i915/intel_fb.c -+++ b/drivers/gpu/drm/i915/intel_fb.c -@@ -211,6 +211,12 @@ static int intel_fb_find_or_create_single(struct drm_fb_helper *helper, - return new_fb; - } - -+void intelfb_hotplug(struct drm_device *dev, bool polled) -+{ -+ drm_i915_private_t *dev_priv = dev->dev_private; -+ drm_helper_fb_hpd_irq_event(&dev_priv->fbdev->helper); -+} -+ - static struct drm_fb_helper_funcs intel_fb_helper_funcs = { - .gamma_set = intel_crtc_fb_gamma_set, - .gamma_get = intel_crtc_fb_gamma_get, -@@ -256,7 +262,7 @@ int intel_fbdev_init(struct drm_device *dev) - - ret = drm_fb_helper_init(dev, &ifbdev->helper, - dev_priv->num_pipe, -- INTELFB_CONN_LIMIT); -+ INTELFB_CONN_LIMIT, false); - if (ret) { - kfree(ifbdev); - return ret; -@@ -278,9 +284,3 @@ void intel_fbdev_fini(struct drm_device *dev) - dev_priv->fbdev = NULL; - } - MODULE_LICENSE("GPL and additional rights"); -- --void intel_fb_output_poll_changed(struct drm_device *dev) --{ -- drm_i915_private_t *dev_priv = dev->dev_private; -- drm_fb_helper_hotplug_event(&dev_priv->fbdev->helper); --} -diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c -index 83bd764..acaca07 100644 ---- a/drivers/gpu/drm/i915/intel_hdmi.c -+++ b/drivers/gpu/drm/i915/intel_hdmi.c -@@ -240,7 +240,6 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg) - - intel_encoder->type = INTEL_OUTPUT_HDMI; - -- connector->polled = DRM_CONNECTOR_POLL_HPD; - connector->interlace_allowed = 0; - connector->doublescan_allowed = 0; - intel_encoder->crtc_mask = (1 << 0) | (1 << 1); -diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c -index 76993ac..1c716b5 100644 ---- a/drivers/gpu/drm/i915/intel_sdvo.c -+++ b/drivers/gpu/drm/i915/intel_sdvo.c -@@ -2218,7 +2218,6 @@ intel_sdvo_dvi_init(struct intel_encoder *intel_encoder, int device) - } - - connector = &intel_connector->base; -- connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; - encoder->encoder_type = DRM_MODE_ENCODER_TMDS; - connector->connector_type = DRM_MODE_CONNECTOR_DVID; - -@@ -2285,7 +2284,6 @@ intel_sdvo_analog_init(struct intel_encoder *intel_encoder, int device) - return false; - - connector = &intel_connector->base; -- connector->polled = DRM_CONNECTOR_POLL_CONNECT; - encoder->encoder_type = DRM_MODE_ENCODER_DAC; - connector->connector_type = DRM_MODE_CONNECTOR_VGA; - sdvo_connector = intel_connector->dev_priv; -diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c -index 149ed22..9a61f3c 100644 ---- a/drivers/gpu/drm/nouveau/nouveau_connector.c -+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c -@@ -846,7 +846,6 @@ nouveau_connector_create(struct drm_device *dev, - - switch (dcb->type) { - case DCB_CONNECTOR_VGA: -- connector->polled = DRM_CONNECTOR_POLL_CONNECT; - if (dev_priv->card_type >= NV_50) { - drm_connector_attach_property(connector, - dev->mode_config.scaling_mode_property, -@@ -858,17 +857,6 @@ nouveau_connector_create(struct drm_device *dev, - case DCB_CONNECTOR_TV_3: - nv_connector->scaling_mode = DRM_MODE_SCALE_NONE; - break; -- case DCB_CONNECTOR_DP: -- case DCB_CONNECTOR_eDP: -- case DCB_CONNECTOR_HDMI_0: -- case DCB_CONNECTOR_HDMI_1: -- case DCB_CONNECTOR_DVI_I: -- case DCB_CONNECTOR_DVI_D: -- if (dev_priv->card_type >= NV_50) -- connector->polled = DRM_CONNECTOR_POLL_HPD; -- else -- connector->polled = DRM_CONNECTOR_POLL_CONNECT; -- /* fall-through */ - default: - nv_connector->scaling_mode = DRM_MODE_SCALE_FULLSCREEN; - -diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c -index 74e6b4e..9d7928f 100644 ---- a/drivers/gpu/drm/nouveau/nouveau_display.c -+++ b/drivers/gpu/drm/nouveau/nouveau_display.c -@@ -101,6 +101,5 @@ nouveau_user_framebuffer_create(struct drm_device *dev, - - const struct drm_mode_config_funcs nouveau_mode_config_funcs = { - .fb_create = nouveau_user_framebuffer_create, -- .output_poll_changed = nouveau_fbcon_output_poll_changed, - }; - -diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c -index c9a4a0d..0a59f96 100644 ---- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c -+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c -@@ -326,11 +326,15 @@ nouveau_fbcon_find_or_create_single(struct drm_fb_helper *helper, - return new_fb; - } - --void --nouveau_fbcon_output_poll_changed(struct drm_device *dev) -+void nouveau_fbcon_hotplug(struct drm_device *dev) - { - struct drm_nouveau_private *dev_priv = dev->dev_private; -- drm_fb_helper_hotplug_event(&dev_priv->nfbdev->helper); -+ drm_helper_fb_hpd_irq_event(&dev_priv->nfbdev->helper); -+} -+ -+static void nouveau_fbcon_output_status_changed(struct drm_fb_helper *fb_helper) -+{ -+ drm_helper_fb_hotplug_event(fb_helper, true); - } - - int -@@ -370,6 +374,7 @@ static struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = { - .gamma_set = nouveau_fbcon_gamma_set, - .gamma_get = nouveau_fbcon_gamma_get, - .fb_probe = nouveau_fbcon_find_or_create_single, -+ .fb_output_status_changed = nouveau_fbcon_output_status_changed, - }; - - -@@ -387,7 +392,7 @@ int nouveau_fbcon_init(struct drm_device *dev) - dev_priv->nfbdev = nfbdev; - nfbdev->helper.funcs = &nouveau_fbcon_helper_funcs; - -- ret = drm_fb_helper_init(dev, &nfbdev->helper, 2, 4); -+ ret = drm_fb_helper_init(dev, &nfbdev->helper, 2, 4, true); - if (ret) { - kfree(nfbdev); - return ret; -diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.h b/drivers/gpu/drm/nouveau/nouveau_fbcon.h -index e7e1268..bf8e00d 100644 ---- a/drivers/gpu/drm/nouveau/nouveau_fbcon.h -+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.h -@@ -58,6 +58,6 @@ void nouveau_fbcon_zfill_all(struct drm_device *dev); - void nouveau_fbcon_save_disable_accel(struct drm_device *dev); - void nouveau_fbcon_restore_accel(struct drm_device *dev); - --void nouveau_fbcon_output_poll_changed(struct drm_device *dev); -+void nouveau_fbcon_hotplug(struct drm_device *dev); - #endif /* __NV50_FBCON_H__ */ - -diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c -index b02a231..4dcb976 100644 ---- a/drivers/gpu/drm/nouveau/nouveau_state.c -+++ b/drivers/gpu/drm/nouveau/nouveau_state.c -@@ -519,10 +519,8 @@ nouveau_card_init(struct drm_device *dev) - - dev_priv->init_state = NOUVEAU_CARD_INIT_DONE; - -- if (drm_core_check_feature(dev, DRIVER_MODESET)) { -+ if (drm_core_check_feature(dev, DRIVER_MODESET)) - nouveau_fbcon_init(dev); -- drm_kms_helper_poll_init(dev); -- } - - return 0; - -@@ -844,7 +842,6 @@ int nouveau_unload(struct drm_device *dev) - struct drm_nouveau_private *dev_priv = dev->dev_private; - - if (drm_core_check_feature(dev, DRIVER_MODESET)) { -- drm_kms_helper_poll_fini(dev); - nouveau_fbcon_fini(dev); - if (dev_priv->card_type >= NV_50) - nv50_display_destroy(dev); -diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c -index 580a5d1..e6a44af 100644 ---- a/drivers/gpu/drm/nouveau/nv50_display.c -+++ b/drivers/gpu/drm/nouveau/nv50_display.c -@@ -980,7 +980,7 @@ nv50_display_irq_hotplug_bh(struct work_struct *work) - if (dev_priv->chipset >= 0x90) - nv_wr32(dev, 0xe074, nv_rd32(dev, 0xe074)); - -- drm_helper_hpd_irq_event(dev); -+ nouveau_fbcon_hotplug(dev); - } - - void -diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c -index 0c7ccc6..40a24c9 100644 ---- a/drivers/gpu/drm/radeon/radeon_connectors.c -+++ b/drivers/gpu/drm/radeon/radeon_connectors.c -@@ -1085,7 +1085,6 @@ radeon_add_atom_connector(struct drm_device *dev, - drm_connector_attach_property(&radeon_connector->base, - rdev->mode_info.load_detect_property, - 1); -- connector->polled = DRM_CONNECTOR_POLL_CONNECT; - break; - case DRM_MODE_CONNECTOR_DVIA: - drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type); -@@ -1212,12 +1211,6 @@ radeon_add_atom_connector(struct drm_device *dev, - break; - } - -- if (hpd->hpd == RADEON_HPD_NONE) { -- if (i2c_bus->valid) -- connector->polled = DRM_CONNECTOR_POLL_CONNECT; -- } else -- connector->polled = DRM_CONNECTOR_POLL_HPD; -- - connector->display_info.subpixel_order = subpixel_order; - drm_sysfs_connector_add(connector); - return; -@@ -1279,7 +1272,6 @@ radeon_add_legacy_connector(struct drm_device *dev, - drm_connector_attach_property(&radeon_connector->base, - rdev->mode_info.load_detect_property, - 1); -- connector->polled = DRM_CONNECTOR_POLL_CONNECT; - break; - case DRM_MODE_CONNECTOR_DVIA: - drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type); -@@ -1348,11 +1340,6 @@ radeon_add_legacy_connector(struct drm_device *dev, - break; - } - -- if (hpd->hpd == RADEON_HPD_NONE) { -- if (i2c_bus->valid) -- connector->polled = DRM_CONNECTOR_POLL_CONNECT; -- } else -- connector->polled = DRM_CONNECTOR_POLL_HPD; - connector->display_info.subpixel_order = subpixel_order; - drm_sysfs_connector_add(connector); - return; -diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c -index c73444a..ed756be 100644 ---- a/drivers/gpu/drm/radeon/radeon_display.c -+++ b/drivers/gpu/drm/radeon/radeon_display.c -@@ -887,15 +887,8 @@ radeon_user_framebuffer_create(struct drm_device *dev, - return &radeon_fb->base; - } - --static void radeon_output_poll_changed(struct drm_device *dev) --{ -- struct radeon_device *rdev = dev->dev_private; -- radeon_fb_output_poll_changed(rdev); --} -- - static const struct drm_mode_config_funcs radeon_mode_funcs = { - .fb_create = radeon_user_framebuffer_create, -- .output_poll_changed = radeon_output_poll_changed - }; - - struct drm_prop_enum_list { -@@ -1044,8 +1037,6 @@ int radeon_modeset_init(struct radeon_device *rdev) - radeon_pm_init(rdev); - - radeon_fbdev_init(rdev); -- drm_kms_helper_poll_init(rdev->ddev); -- - return 0; - } - -@@ -1058,7 +1049,6 @@ void radeon_modeset_fini(struct radeon_device *rdev) - radeon_pm_fini(rdev); - - if (rdev->mode_info.mode_config_initialized) { -- drm_kms_helper_poll_fini(rdev->ddev); - radeon_hpd_fini(rdev); - drm_mode_config_cleanup(rdev->ddev); - rdev->mode_info.mode_config_initialized = false; -diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c -index dc1634b..7dc38f6 100644 ---- a/drivers/gpu/drm/radeon/radeon_fb.c -+++ b/drivers/gpu/drm/radeon/radeon_fb.c -@@ -316,9 +316,16 @@ int radeon_parse_options(char *options) - return 0; - } - --void radeon_fb_output_poll_changed(struct radeon_device *rdev) -+void radeonfb_hotplug(struct drm_device *dev, bool polled) - { -- drm_fb_helper_hotplug_event(&rdev->mode_info.rfbdev->helper); -+ struct radeon_device *rdev = dev->dev_private; -+ -+ drm_helper_fb_hpd_irq_event(&rdev->mode_info.rfbdev->helper); -+} -+ -+static void radeon_fb_output_status_changed(struct drm_fb_helper *fb_helper) -+{ -+ drm_helper_fb_hotplug_event(fb_helper, true); - } - - static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfbdev) -@@ -357,6 +364,7 @@ static struct drm_fb_helper_funcs radeon_fb_helper_funcs = { - .gamma_set = radeon_crtc_fb_gamma_set, - .gamma_get = radeon_crtc_fb_gamma_get, - .fb_probe = radeon_fb_find_or_create_single, -+ .fb_output_status_changed = radeon_fb_output_status_changed, - }; - - int radeon_fbdev_init(struct radeon_device *rdev) -@@ -379,7 +387,7 @@ int radeon_fbdev_init(struct radeon_device *rdev) - - ret = drm_fb_helper_init(rdev->ddev, &rfbdev->helper, - rdev->num_crtc, -- RADEONFB_CONN_LIMIT); -+ RADEONFB_CONN_LIMIT, true); - if (ret) { - kfree(rfbdev); - return ret; -@@ -388,6 +396,7 @@ int radeon_fbdev_init(struct radeon_device *rdev) - drm_fb_helper_single_add_all_connectors(&rfbdev->helper); - drm_fb_helper_initial_config(&rfbdev->helper, bpp_sel); - return 0; -+ - } - - void radeon_fbdev_fini(struct radeon_device *rdev) -diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c -index 059bfa4..b0178de 100644 ---- a/drivers/gpu/drm/radeon/radeon_irq_kms.c -+++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c -@@ -26,7 +26,6 @@ - * Jerome Glisse - */ - #include "drmP.h" --#include "drm_crtc_helper.h" - #include "radeon_drm.h" - #include "radeon_reg.h" - #include "radeon.h" -@@ -56,7 +55,9 @@ static void radeon_hotplug_work_func(struct work_struct *work) - radeon_connector_hotplug(connector); - } - /* Just fire off a uevent and let userspace tell us what to do */ -- drm_helper_hpd_irq_event(dev); -+ radeonfb_hotplug(dev, false); -+ -+ drm_sysfs_hotplug_event(dev); - } - - void radeon_driver_irq_preinstall_kms(struct drm_device *dev) -diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h -index 67358ba..fdd1611 100644 ---- a/drivers/gpu/drm/radeon/radeon_mode.h -+++ b/drivers/gpu/drm/radeon/radeon_mode.h -@@ -588,6 +588,5 @@ void radeon_fbdev_fini(struct radeon_device *rdev); - void radeon_fbdev_set_suspend(struct radeon_device *rdev, int state); - int radeon_fbdev_total_size(struct radeon_device *rdev); - bool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj); -- --void radeon_fb_output_poll_changed(struct radeon_device *rdev); -+void radeonfb_hotplug(struct drm_device *dev, bool polled); - #endif -diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h -index 93a1a31..a7148d2 100644 ---- a/include/drm/drm_crtc.h -+++ b/include/drm/drm_crtc.h -@@ -31,7 +31,6 @@ - #include - - #include --#include - - struct drm_device; - struct drm_mode_set; -@@ -461,15 +460,6 @@ enum drm_connector_force { - DRM_FORCE_ON_DIGITAL, /* for DVI-I use digital connector */ - }; - --/* should we poll this connector for connects and disconnects */ --/* hot plug detectable */ --#define DRM_CONNECTOR_POLL_HPD (1 << 0) --/* poll for connections */ --#define DRM_CONNECTOR_POLL_CONNECT (1 << 1) --/* can cleanly poll for disconnections without flickering the screen */ --/* DACs should rarely do this without a lot of testing */ --#define DRM_CONNECTOR_POLL_DISCONNECT (1 << 2) -- - /** - * drm_connector - central DRM connector control structure - * @crtc: CRTC this connector is currently connected to, NULL if none -@@ -514,8 +504,6 @@ struct drm_connector { - u32 property_ids[DRM_CONNECTOR_MAX_PROPERTY]; - uint64_t property_values[DRM_CONNECTOR_MAX_PROPERTY]; - -- uint8_t polled; /* DRM_CONNECTOR_POLL_* */ -- - /* requested DPMS state */ - int dpms; - -@@ -555,7 +543,6 @@ struct drm_mode_set { - */ - struct drm_mode_config_funcs { - struct drm_framebuffer *(*fb_create)(struct drm_device *dev, struct drm_file *file_priv, struct drm_mode_fb_cmd *mode_cmd); -- void (*output_poll_changed)(struct drm_device *dev); - }; - - struct drm_mode_group { -@@ -593,10 +580,6 @@ struct drm_mode_config { - struct drm_mode_config_funcs *funcs; - resource_size_t fb_base; - -- /* output poll support */ -- bool poll_enabled; -- struct delayed_slow_work output_poll_slow_work; -- - /* pointers to standard properties */ - struct list_head property_blob_list; - struct drm_property *edid_property; -diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h -index 1121f77..b1fa0f8 100644 ---- a/include/drm/drm_crtc_helper.h -+++ b/include/drm/drm_crtc_helper.h -@@ -127,10 +127,4 @@ static inline void drm_connector_helper_add(struct drm_connector *connector, - } - - extern int drm_helper_resume_force_mode(struct drm_device *dev); --extern void drm_kms_helper_poll_init(struct drm_device *dev); --extern void drm_kms_helper_poll_fini(struct drm_device *dev); --extern void drm_helper_hpd_irq_event(struct drm_device *dev); -- --extern void drm_kms_helper_poll_disable(struct drm_device *dev); --extern void drm_kms_helper_poll_enable(struct drm_device *dev); - #endif -diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h -index f0a6afc..9b55a94 100644 ---- a/include/drm/drm_fb_helper.h -+++ b/include/drm/drm_fb_helper.h -@@ -30,6 +30,8 @@ - #ifndef DRM_FB_HELPER_H - #define DRM_FB_HELPER_H - -+#include -+ - struct drm_fb_helper; - - struct drm_fb_helper_crtc { -@@ -69,6 +71,9 @@ struct drm_fb_helper_funcs { - - int (*fb_probe)(struct drm_fb_helper *helper, - struct drm_fb_helper_surface_size *sizes); -+ -+ void (*fb_output_status_changed)(struct drm_fb_helper *helper); -+ - }; - - struct drm_fb_helper_connector { -@@ -90,6 +95,8 @@ struct drm_fb_helper { - u32 pseudo_palette[17]; - struct list_head kernel_fb_list; - -+ struct delayed_slow_work output_status_change_slow_work; -+ bool poll_enabled; - /* we got a hotplug but fbdev wasn't running the console - delay until next set_par */ - bool delayed_hotplug; -@@ -100,7 +107,7 @@ int drm_fb_helper_single_fb_probe(struct drm_fb_helper *helper, - - int drm_fb_helper_init(struct drm_device *dev, - struct drm_fb_helper *helper, int crtc_count, -- int max_conn); -+ int max_conn, bool polled); - void drm_fb_helper_fini(struct drm_fb_helper *helper); - int drm_fb_helper_blank(int blank, struct fb_info *info); - int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, -@@ -123,8 +130,10 @@ void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, - - int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info); - --bool drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper); -+bool drm_helper_fb_hotplug_event(struct drm_fb_helper *fb_helper, -+ bool polled); - bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel); - int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper); - -+void drm_helper_fb_hpd_irq_event(struct drm_fb_helper *fb_helper); - #endif --- -1.7.0.1 - diff --git a/drm-sil164-module.patch b/drm-sil164-module.patch new file mode 100644 index 000000000..06194e6a1 --- /dev/null +++ b/drm-sil164-module.patch @@ -0,0 +1,592 @@ +From f1719f0dcd68ca4de42c7b00ef2b37658007dda7 Mon Sep 17 00:00:00 2001 +From: Francisco Jerez +Date: Thu, 22 Jul 2010 17:06:18 +0200 +Subject: [PATCH 2/5] drm-sil164-module + +drm: Import driver for the sil164 I2C TMDS transmitter. + +sil164 transmitters are used for DVI outputs on Intel/nvidia and ATI setups. + +So far only nouveau can use this driver. + +Signed-off-by: Francisco Jerez +Tested-by: Patrice Mandin +Signed-off-by: Dave Airlie +--- + drivers/gpu/drm/i2c/Makefile | 3 + + drivers/gpu/drm/i2c/sil164_drv.c | 462 ++++++++++++++++++++++++++++++++++++++ + drivers/gpu/drm/nouveau/Kconfig | 9 + + include/drm/i2c/sil164.h | 63 +++++ + 4 files changed, 537 insertions(+), 0 deletions(-) + create mode 100644 drivers/gpu/drm/i2c/sil164_drv.c + create mode 100644 include/drm/i2c/sil164.h + +diff --git a/drivers/gpu/drm/i2c/Makefile b/drivers/gpu/drm/i2c/Makefile +index 6d2abaf..9286256 100644 +--- a/drivers/gpu/drm/i2c/Makefile ++++ b/drivers/gpu/drm/i2c/Makefile +@@ -2,3 +2,6 @@ ccflags-y := -Iinclude/drm + + ch7006-y := ch7006_drv.o ch7006_mode.o + obj-$(CONFIG_DRM_I2C_CH7006) += ch7006.o ++ ++sil164-y := sil164_drv.o ++obj-$(CONFIG_DRM_I2C_SIL164) += sil164.o +diff --git a/drivers/gpu/drm/i2c/sil164_drv.c b/drivers/gpu/drm/i2c/sil164_drv.c +new file mode 100644 +index 0000000..0b67732 +--- /dev/null ++++ b/drivers/gpu/drm/i2c/sil164_drv.c +@@ -0,0 +1,462 @@ ++/* ++ * Copyright (C) 2010 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 "drm_encoder_slave.h" ++#include "i2c/sil164.h" ++ ++struct sil164_priv { ++ struct sil164_encoder_params config; ++ struct i2c_client *duallink_slave; ++ ++ uint8_t saved_state[0x10]; ++ uint8_t saved_slave_state[0x10]; ++}; ++ ++#define to_sil164_priv(x) \ ++ ((struct sil164_priv *)to_encoder_slave(x)->slave_priv) ++ ++#define sil164_dbg(client, format, ...) do { \ ++ if (drm_debug & DRM_UT_KMS) \ ++ dev_printk(KERN_DEBUG, &client->dev, \ ++ "%s: " format, __func__, ## __VA_ARGS__); \ ++ } while (0) ++#define sil164_info(client, format, ...) \ ++ dev_info(&client->dev, format, __VA_ARGS__) ++#define sil164_err(client, format, ...) \ ++ dev_err(&client->dev, format, __VA_ARGS__) ++ ++#define SIL164_I2C_ADDR_MASTER 0x38 ++#define SIL164_I2C_ADDR_SLAVE 0x39 ++ ++/* HW register definitions */ ++ ++#define SIL164_VENDOR_LO 0x0 ++#define SIL164_VENDOR_HI 0x1 ++#define SIL164_DEVICE_LO 0x2 ++#define SIL164_DEVICE_HI 0x3 ++#define SIL164_REVISION 0x4 ++#define SIL164_FREQ_MIN 0x6 ++#define SIL164_FREQ_MAX 0x7 ++#define SIL164_CONTROL0 0x8 ++# define SIL164_CONTROL0_POWER_ON 0x01 ++# define SIL164_CONTROL0_EDGE_RISING 0x02 ++# define SIL164_CONTROL0_INPUT_24BIT 0x04 ++# define SIL164_CONTROL0_DUAL_EDGE 0x08 ++# define SIL164_CONTROL0_HSYNC_ON 0x10 ++# define SIL164_CONTROL0_VSYNC_ON 0x20 ++#define SIL164_DETECT 0x9 ++# define SIL164_DETECT_INTR_STAT 0x01 ++# define SIL164_DETECT_HOTPLUG_STAT 0x02 ++# define SIL164_DETECT_RECEIVER_STAT 0x04 ++# define SIL164_DETECT_INTR_MODE_RECEIVER 0x00 ++# define SIL164_DETECT_INTR_MODE_HOTPLUG 0x08 ++# define SIL164_DETECT_OUT_MODE_HIGH 0x00 ++# define SIL164_DETECT_OUT_MODE_INTR 0x10 ++# define SIL164_DETECT_OUT_MODE_RECEIVER 0x20 ++# define SIL164_DETECT_OUT_MODE_HOTPLUG 0x30 ++# define SIL164_DETECT_VSWING_STAT 0x80 ++#define SIL164_CONTROL1 0xa ++# define SIL164_CONTROL1_DESKEW_ENABLE 0x10 ++# define SIL164_CONTROL1_DESKEW_INCR_SHIFT 5 ++#define SIL164_GPIO 0xb ++#define SIL164_CONTROL2 0xc ++# define SIL164_CONTROL2_FILTER_ENABLE 0x01 ++# define SIL164_CONTROL2_FILTER_SETTING_SHIFT 1 ++# define SIL164_CONTROL2_DUALLINK_MASTER 0x40 ++# define SIL164_CONTROL2_SYNC_CONT 0x80 ++#define SIL164_DUALLINK 0xd ++# define SIL164_DUALLINK_ENABLE 0x10 ++# define SIL164_DUALLINK_SKEW_SHIFT 5 ++#define SIL164_PLLZONE 0xe ++# define SIL164_PLLZONE_STAT 0x08 ++# define SIL164_PLLZONE_FORCE_ON 0x10 ++# define SIL164_PLLZONE_FORCE_HIGH 0x20 ++ ++/* HW access functions */ ++ ++static void ++sil164_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) ++ sil164_err(client, "Error %d writing to subaddress 0x%x\n", ++ ret, addr); ++} ++ ++static uint8_t ++sil164_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: ++ sil164_err(client, "Error %d reading from subaddress 0x%x\n", ++ ret, addr); ++ return 0; ++} ++ ++static void ++sil164_save_state(struct i2c_client *client, uint8_t *state) ++{ ++ int i; ++ ++ for (i = 0x8; i <= 0xe; i++) ++ state[i] = sil164_read(client, i); ++} ++ ++static void ++sil164_restore_state(struct i2c_client *client, uint8_t *state) ++{ ++ int i; ++ ++ for (i = 0x8; i <= 0xe; i++) ++ sil164_write(client, i, state[i]); ++} ++ ++static void ++sil164_set_power_state(struct i2c_client *client, bool on) ++{ ++ uint8_t control0 = sil164_read(client, SIL164_CONTROL0); ++ ++ if (on) ++ control0 |= SIL164_CONTROL0_POWER_ON; ++ else ++ control0 &= ~SIL164_CONTROL0_POWER_ON; ++ ++ sil164_write(client, SIL164_CONTROL0, control0); ++} ++ ++static void ++sil164_init_state(struct i2c_client *client, ++ struct sil164_encoder_params *config, ++ bool duallink) ++{ ++ sil164_write(client, SIL164_CONTROL0, ++ SIL164_CONTROL0_HSYNC_ON | ++ SIL164_CONTROL0_VSYNC_ON | ++ (config->input_edge ? SIL164_CONTROL0_EDGE_RISING : 0) | ++ (config->input_width ? SIL164_CONTROL0_INPUT_24BIT : 0) | ++ (config->input_dual ? SIL164_CONTROL0_DUAL_EDGE : 0)); ++ ++ sil164_write(client, SIL164_DETECT, ++ SIL164_DETECT_INTR_STAT | ++ SIL164_DETECT_OUT_MODE_RECEIVER); ++ ++ sil164_write(client, SIL164_CONTROL1, ++ (config->input_skew ? SIL164_CONTROL1_DESKEW_ENABLE : 0) | ++ (((config->input_skew + 4) & 0x7) ++ << SIL164_CONTROL1_DESKEW_INCR_SHIFT)); ++ ++ sil164_write(client, SIL164_CONTROL2, ++ SIL164_CONTROL2_SYNC_CONT | ++ (config->pll_filter ? 0 : SIL164_CONTROL2_FILTER_ENABLE) | ++ (4 << SIL164_CONTROL2_FILTER_SETTING_SHIFT)); ++ ++ sil164_write(client, SIL164_PLLZONE, 0); ++ ++ if (duallink) ++ sil164_write(client, SIL164_DUALLINK, ++ SIL164_DUALLINK_ENABLE | ++ (((config->duallink_skew + 4) & 0x7) ++ << SIL164_DUALLINK_SKEW_SHIFT)); ++ else ++ sil164_write(client, SIL164_DUALLINK, 0); ++} ++ ++/* DRM encoder functions */ ++ ++static void ++sil164_encoder_set_config(struct drm_encoder *encoder, void *params) ++{ ++ struct sil164_priv *priv = to_sil164_priv(encoder); ++ ++ priv->config = *(struct sil164_encoder_params *)params; ++} ++ ++static void ++sil164_encoder_dpms(struct drm_encoder *encoder, int mode) ++{ ++ struct sil164_priv *priv = to_sil164_priv(encoder); ++ bool on = (mode == DRM_MODE_DPMS_ON); ++ bool duallink = (on && encoder->crtc->mode.clock > 165000); ++ ++ sil164_set_power_state(drm_i2c_encoder_get_client(encoder), on); ++ ++ if (priv->duallink_slave) ++ sil164_set_power_state(priv->duallink_slave, duallink); ++} ++ ++static void ++sil164_encoder_save(struct drm_encoder *encoder) ++{ ++ struct sil164_priv *priv = to_sil164_priv(encoder); ++ ++ sil164_save_state(drm_i2c_encoder_get_client(encoder), ++ priv->saved_state); ++ ++ if (priv->duallink_slave) ++ sil164_save_state(priv->duallink_slave, ++ priv->saved_slave_state); ++} ++ ++static void ++sil164_encoder_restore(struct drm_encoder *encoder) ++{ ++ struct sil164_priv *priv = to_sil164_priv(encoder); ++ ++ sil164_restore_state(drm_i2c_encoder_get_client(encoder), ++ priv->saved_state); ++ ++ if (priv->duallink_slave) ++ sil164_restore_state(priv->duallink_slave, ++ priv->saved_slave_state); ++} ++ ++static bool ++sil164_encoder_mode_fixup(struct drm_encoder *encoder, ++ struct drm_display_mode *mode, ++ struct drm_display_mode *adjusted_mode) ++{ ++ return true; ++} ++ ++static int ++sil164_encoder_mode_valid(struct drm_encoder *encoder, ++ struct drm_display_mode *mode) ++{ ++ struct sil164_priv *priv = to_sil164_priv(encoder); ++ ++ if (mode->clock < 32000) ++ return MODE_CLOCK_LOW; ++ ++ if (mode->clock > 330000 || ++ (mode->clock > 165000 && !priv->duallink_slave)) ++ return MODE_CLOCK_HIGH; ++ ++ return MODE_OK; ++} ++ ++static void ++sil164_encoder_mode_set(struct drm_encoder *encoder, ++ struct drm_display_mode *mode, ++ struct drm_display_mode *adjusted_mode) ++{ ++ struct sil164_priv *priv = to_sil164_priv(encoder); ++ bool duallink = adjusted_mode->clock > 165000; ++ ++ sil164_init_state(drm_i2c_encoder_get_client(encoder), ++ &priv->config, duallink); ++ ++ if (priv->duallink_slave) ++ sil164_init_state(priv->duallink_slave, ++ &priv->config, duallink); ++ ++ sil164_encoder_dpms(encoder, DRM_MODE_DPMS_ON); ++} ++ ++static enum drm_connector_status ++sil164_encoder_detect(struct drm_encoder *encoder, ++ struct drm_connector *connector) ++{ ++ struct i2c_client *client = drm_i2c_encoder_get_client(encoder); ++ ++ if (sil164_read(client, SIL164_DETECT) & SIL164_DETECT_HOTPLUG_STAT) ++ return connector_status_connected; ++ else ++ return connector_status_disconnected; ++} ++ ++static int ++sil164_encoder_get_modes(struct drm_encoder *encoder, ++ struct drm_connector *connector) ++{ ++ return 0; ++} ++ ++static int ++sil164_encoder_create_resources(struct drm_encoder *encoder, ++ struct drm_connector *connector) ++{ ++ return 0; ++} ++ ++static int ++sil164_encoder_set_property(struct drm_encoder *encoder, ++ struct drm_connector *connector, ++ struct drm_property *property, ++ uint64_t val) ++{ ++ return 0; ++} ++ ++static void ++sil164_encoder_destroy(struct drm_encoder *encoder) ++{ ++ struct sil164_priv *priv = to_sil164_priv(encoder); ++ ++ if (priv->duallink_slave) ++ i2c_unregister_device(priv->duallink_slave); ++ ++ kfree(priv); ++ drm_i2c_encoder_destroy(encoder); ++} ++ ++static struct drm_encoder_slave_funcs sil164_encoder_funcs = { ++ .set_config = sil164_encoder_set_config, ++ .destroy = sil164_encoder_destroy, ++ .dpms = sil164_encoder_dpms, ++ .save = sil164_encoder_save, ++ .restore = sil164_encoder_restore, ++ .mode_fixup = sil164_encoder_mode_fixup, ++ .mode_valid = sil164_encoder_mode_valid, ++ .mode_set = sil164_encoder_mode_set, ++ .detect = sil164_encoder_detect, ++ .get_modes = sil164_encoder_get_modes, ++ .create_resources = sil164_encoder_create_resources, ++ .set_property = sil164_encoder_set_property, ++}; ++ ++/* I2C driver functions */ ++ ++static int ++sil164_probe(struct i2c_client *client, const struct i2c_device_id *id) ++{ ++ int vendor = sil164_read(client, SIL164_VENDOR_HI) << 8 | ++ sil164_read(client, SIL164_VENDOR_LO); ++ int device = sil164_read(client, SIL164_DEVICE_HI) << 8 | ++ sil164_read(client, SIL164_DEVICE_LO); ++ int rev = sil164_read(client, SIL164_REVISION); ++ ++ if (vendor != 0x1 || device != 0x6) { ++ sil164_dbg(client, "Unknown device %x:%x.%x\n", ++ vendor, device, rev); ++ return -ENODEV; ++ } ++ ++ sil164_info(client, "Detected device %x:%x.%x\n", ++ vendor, device, rev); ++ ++ return 0; ++} ++ ++static int ++sil164_remove(struct i2c_client *client) ++{ ++ return 0; ++} ++ ++static struct i2c_client * ++sil164_detect_slave(struct i2c_client *client) ++{ ++ struct i2c_adapter *adap = client->adapter; ++ struct i2c_msg msg = { ++ .addr = SIL164_I2C_ADDR_SLAVE, ++ .len = 0, ++ }; ++ const struct i2c_board_info info = { ++ I2C_BOARD_INFO("sil164", SIL164_I2C_ADDR_SLAVE) ++ }; ++ ++ if (i2c_transfer(adap, &msg, 1) != 1) { ++ sil164_dbg(adap, "No dual-link slave found."); ++ return NULL; ++ } ++ ++ return i2c_new_device(adap, &info); ++} ++ ++static int ++sil164_encoder_init(struct i2c_client *client, ++ struct drm_device *dev, ++ struct drm_encoder_slave *encoder) ++{ ++ struct sil164_priv *priv; ++ ++ priv = kzalloc(sizeof(*priv), GFP_KERNEL); ++ if (!priv) ++ return -ENOMEM; ++ ++ encoder->slave_priv = priv; ++ encoder->slave_funcs = &sil164_encoder_funcs; ++ ++ priv->duallink_slave = sil164_detect_slave(client); ++ ++ return 0; ++} ++ ++static struct i2c_device_id sil164_ids[] = { ++ { "sil164", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, sil164_ids); ++ ++static struct drm_i2c_encoder_driver sil164_driver = { ++ .i2c_driver = { ++ .probe = sil164_probe, ++ .remove = sil164_remove, ++ .driver = { ++ .name = "sil164", ++ }, ++ .id_table = sil164_ids, ++ }, ++ .encoder_init = sil164_encoder_init, ++}; ++ ++/* Module initialization */ ++ ++static int __init ++sil164_init(void) ++{ ++ return drm_i2c_encoder_register(THIS_MODULE, &sil164_driver); ++} ++ ++static void __exit ++sil164_exit(void) ++{ ++ drm_i2c_encoder_unregister(&sil164_driver); ++} ++ ++MODULE_AUTHOR("Francisco Jerez "); ++MODULE_DESCRIPTION("Silicon Image sil164 TMDS transmitter driver"); ++MODULE_LICENSE("GPL and additional rights"); ++ ++module_init(sil164_init); ++module_exit(sil164_exit); +diff --git a/drivers/gpu/drm/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig +index 1175429..6b8967a 100644 +--- a/drivers/gpu/drm/nouveau/Kconfig ++++ b/drivers/gpu/drm/nouveau/Kconfig +@@ -41,4 +41,13 @@ config DRM_I2C_CH7006 + + This driver is currently only useful if you're also using + the nouveau driver. ++ ++config DRM_I2C_SIL164 ++ tristate "Silicon Image sil164 TMDS transmitter" ++ default m if DRM_NOUVEAU ++ help ++ Support for sil164 and similar single-link (or dual-link ++ when used in pairs) TMDS transmitters, used in some nVidia ++ video cards. ++ + endmenu +diff --git a/include/drm/i2c/sil164.h b/include/drm/i2c/sil164.h +new file mode 100644 +index 0000000..205e273 +--- /dev/null ++++ b/include/drm/i2c/sil164.h +@@ -0,0 +1,63 @@ ++/* ++ * Copyright (C) 2010 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_SIL164_H__ ++#define __DRM_I2C_SIL164_H__ ++ ++/** ++ * struct sil164_encoder_params ++ * ++ * Describes how the sil164 is connected to the GPU. It should be used ++ * as the @params parameter of its @set_config method. ++ * ++ * See "http://www.siliconimage.com/docs/SiI-DS-0021-E-164.pdf". ++ */ ++struct sil164_encoder_params { ++ enum { ++ SIL164_INPUT_EDGE_FALLING = 0, ++ SIL164_INPUT_EDGE_RISING ++ } input_edge; ++ ++ enum { ++ SIL164_INPUT_WIDTH_12BIT = 0, ++ SIL164_INPUT_WIDTH_24BIT ++ } input_width; ++ ++ enum { ++ SIL164_INPUT_SINGLE_EDGE = 0, ++ SIL164_INPUT_DUAL_EDGE ++ } input_dual; ++ ++ enum { ++ SIL164_PLL_FILTER_ON = 0, ++ SIL164_PLL_FILTER_OFF, ++ } pll_filter; ++ ++ int input_skew; /** < Allowed range [-4, 3], use 0 for no de-skew. */ ++ int duallink_skew; /** < Allowed range [-4, 3]. */ ++}; ++ ++#endif +-- +1.7.2 + diff --git a/drm-simplify-i2c-config.patch b/drm-simplify-i2c-config.patch new file mode 100644 index 000000000..c1fed73d7 --- /dev/null +++ b/drm-simplify-i2c-config.patch @@ -0,0 +1,45 @@ +From 74ef65374ae6d0eead4a631aea3aca80d016ff0f Mon Sep 17 00:00:00 2001 +From: Francisco Jerez +Date: Thu, 22 Jul 2010 17:07:38 +0200 +Subject: [PATCH 1/5] drm-simplify-i2c-config + +drm/kms: Simplify setup of the initial I2C encoder config. + +In most use cases the driver will be using the same static config all +the time: interpreting i2c_board_info::platform_data as the default +config we can can save the GPU driver a redundant set_config() call. + +Signed-off-by: Francisco Jerez +Signed-off-by: Dave Airlie +--- + drivers/gpu/drm/drm_encoder_slave.c | 7 +++++++ + 1 files changed, 7 insertions(+), 0 deletions(-) + +diff --git a/drivers/gpu/drm/drm_encoder_slave.c b/drivers/gpu/drm/drm_encoder_slave.c +index f018469..d62c064 100644 +--- a/drivers/gpu/drm/drm_encoder_slave.c ++++ b/drivers/gpu/drm/drm_encoder_slave.c +@@ -41,6 +41,9 @@ + * &drm_encoder_slave. The @slave_funcs field will be initialized with + * the hooks provided by the slave driver. + * ++ * If @info->platform_data is non-NULL it will be used as the initial ++ * slave config. ++ * + * Returns 0 on success or a negative errno on failure, in particular, + * -ENODEV is returned when no matching driver is found. + */ +@@ -85,6 +88,10 @@ int drm_i2c_encoder_init(struct drm_device *dev, + if (err) + goto fail_unregister; + ++ if (info->platform_data) ++ encoder->slave_funcs->set_config(&encoder->base, ++ info->platform_data); ++ + return 0; + + fail_unregister: +-- +1.7.2 + diff --git a/drm-ttm-fix-two-race-conditions-fix-busy-codepaths.patch b/drm-ttm-fix-two-race-conditions-fix-busy-codepaths.patch new file mode 100644 index 000000000..0eed9edd1 --- /dev/null +++ b/drm-ttm-fix-two-race-conditions-fix-busy-codepaths.patch @@ -0,0 +1,171 @@ +From 08ae078a33245bc01dcf895bd886f30103cc6178 Mon Sep 17 00:00:00 2001 +From: Thomas Hellstrom +Date: Thu, 30 Sep 2010 12:36:45 +0200 +Subject: drm/ttm: Fix two race conditions + fix busy codepaths + +This fixes a race pointed out by Dave Airlie where we don't take a buffer +object about to be destroyed off the LRU lists properly. It also fixes a rare +case where a buffer object could be destroyed in the middle of an +accelerated eviction. + +The patch also adds a utility function that can be used to prematurely +release GPU memory space usage of an object waiting to be destroyed. +For example during eviction or swapout. + +The above mentioned commit didn't queue the buffer on the delayed destroy +list under some rare circumstances. It also didn't completely honor the +remove_all parameter. + +Fixes: +https://bugzilla.redhat.com/show_bug.cgi?id=615505 +http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=591061 + +Signed-off-by: Thomas Hellstrom +Signed-off-by: Dave Airlie +--- + drivers/gpu/drm/ttm/ttm_bo.c | 84 +++++++++++++++++++++++++++++++++++------ + include/drm/ttm/ttm_bo_api.h | 4 +- + 2 files changed, 74 insertions(+), 14 deletions(-) + +diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c +index 555ebb1..77f22ba 100644 +--- a/drivers/gpu/drm/ttm/ttm_bo.c ++++ b/drivers/gpu/drm/ttm/ttm_bo.c +@@ -442,6 +442,43 @@ out_err: + } + + /** ++ * Call bo::reserved and with the lru lock held. ++ * Will release GPU memory type usage on destruction. ++ * This is the place to put in driver specific hooks. ++ * Will release the bo::reserved lock and the ++ * lru lock on exit. ++ */ ++ ++static void ttm_bo_cleanup_memtype_use(struct ttm_buffer_object *bo) ++{ ++ struct ttm_bo_global *glob = bo->glob; ++ ++ if (bo->ttm) { ++ ++ /** ++ * Release the lru_lock, since we don't want to have ++ * an atomic requirement on ttm_tt[unbind|destroy]. ++ */ ++ ++ spin_unlock(&glob->lru_lock); ++ ttm_tt_unbind(bo->ttm); ++ ttm_tt_destroy(bo->ttm); ++ bo->ttm = NULL; ++ spin_lock(&glob->lru_lock); ++ } ++ ++ if (bo->mem.mm_node) { ++ drm_mm_put_block(bo->mem.mm_node); ++ bo->mem.mm_node = NULL; ++ } ++ ++ atomic_set(&bo->reserved, 0); ++ wake_up_all(&bo->event_queue); ++ spin_unlock(&glob->lru_lock); ++} ++ ++ ++/** + * If bo idle, remove from delayed- and lru lists, and unref. + * If not idle, and already on delayed list, do nothing. + * If not idle, and not on delayed list, put on delayed list, +@@ -456,6 +493,7 @@ static int ttm_bo_cleanup_refs(struct ttm_buffer_object *bo, bool remove_all) + int ret; + + spin_lock(&bo->lock); ++retry: + (void) ttm_bo_wait(bo, false, false, !remove_all); + + if (!bo->sync_obj) { +@@ -464,32 +502,52 @@ static int ttm_bo_cleanup_refs(struct ttm_buffer_object *bo, bool remove_all) + spin_unlock(&bo->lock); + + spin_lock(&glob->lru_lock); +- put_count = ttm_bo_del_from_lru(bo); ++ ret = ttm_bo_reserve_locked(bo, false, !remove_all, false, 0); ++ ++ /** ++ * Someone else has the object reserved. Bail and retry. ++ */ + +- ret = ttm_bo_reserve_locked(bo, false, false, false, 0); +- BUG_ON(ret); +- if (bo->ttm) +- ttm_tt_unbind(bo->ttm); ++ if (unlikely(ret == -EBUSY)) { ++ spin_unlock(&glob->lru_lock); ++ spin_lock(&bo->lock); ++ goto requeue; ++ } ++ ++ /** ++ * We can re-check for sync object without taking ++ * the bo::lock since setting the sync object requires ++ * also bo::reserved. A busy object at this point may ++ * be caused by another thread starting an accelerated ++ * eviction. ++ */ ++ ++ if (unlikely(bo->sync_obj)) { ++ atomic_set(&bo->reserved, 0); ++ wake_up_all(&bo->event_queue); ++ spin_unlock(&glob->lru_lock); ++ spin_lock(&bo->lock); ++ if (remove_all) ++ goto retry; ++ else ++ goto requeue; ++ } ++ ++ put_count = ttm_bo_del_from_lru(bo); + + if (!list_empty(&bo->ddestroy)) { + list_del_init(&bo->ddestroy); + ++put_count; + } +- if (bo->mem.mm_node) { +- bo->mem.mm_node->private = NULL; +- drm_mm_put_block(bo->mem.mm_node); +- bo->mem.mm_node = NULL; +- } +- spin_unlock(&glob->lru_lock); + +- atomic_set(&bo->reserved, 0); ++ ttm_bo_cleanup_memtype_use(bo); + + while (put_count--) + kref_put(&bo->list_kref, ttm_bo_ref_bug); + + return 0; + } +- ++requeue: + spin_lock(&glob->lru_lock); + if (list_empty(&bo->ddestroy)) { + void *sync_obj = bo->sync_obj; +diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h +index 267a86c..2040e6c 100644 +--- a/include/drm/ttm/ttm_bo_api.h ++++ b/include/drm/ttm/ttm_bo_api.h +@@ -246,9 +246,11 @@ struct ttm_buffer_object { + + atomic_t reserved; + +- + /** + * Members protected by the bo::lock ++ * In addition, setting sync_obj to anything else ++ * than NULL requires bo::reserved to be held. This allows for ++ * checking NULL while reserved but not holding bo::lock. + */ + + void *sync_obj_arg; +-- +1.7.3.2 + diff --git a/drm-ttm-fix.patch b/drm-ttm-fix.patch new file mode 100644 index 000000000..f0b0c1204 --- /dev/null +++ b/drm-ttm-fix.patch @@ -0,0 +1,39 @@ +From 0fbecd400dd0a82d465b3086f209681e8c54cb0f Mon Sep 17 00:00:00 2001 +From: Francisco Jerez +Date: Tue, 21 Sep 2010 02:15:15 +0200 +Subject: [PATCH] drm/ttm: Clear the ghost cpu_writers flag on ttm_buffer_object_transfer. + +It makes sense for a BO to move after a process has requested +exclusive RW access on it (e.g. because the BO used to be located in +unmappable VRAM and we intercepted the CPU access from the fault +handler). + +If we let the ghost object inherit cpu_writers from the original +object, ttm_bo_release_list() will raise a kernel BUG when the ghost +object is destroyed. This can be reproduced with the nouveau driver on +nv5x. + +Reported-by: Marcin Slusarz +Reviewed-by: Jerome Glisse +Tested-by: Marcin Slusarz +Signed-off-by: Francisco Jerez +Signed-off-by: Dave Airlie +--- + drivers/gpu/drm/ttm/ttm_bo_util.c | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) + +diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c +index 7cffb3e..3451a82 100644 +--- a/drivers/gpu/drm/ttm/ttm_bo_util.c ++++ b/drivers/gpu/drm/ttm/ttm_bo_util.c +@@ -351,6 +351,7 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo, + INIT_LIST_HEAD(&fbo->lru); + INIT_LIST_HEAD(&fbo->swap); + fbo->vm_node = NULL; ++ atomic_set(&fbo->cpu_writers, 0); + + fbo->sync_obj = driver->sync_obj_ref(bo->sync_obj); + kref_init(&fbo->list_kref); +-- +1.7.3.1 + diff --git a/e1000e-82566DC-fails-to-get-link.patch b/e1000e-82566DC-fails-to-get-link.patch new file mode 100644 index 000000000..5a0c1dc27 --- /dev/null +++ b/e1000e-82566DC-fails-to-get-link.patch @@ -0,0 +1,55 @@ +From: Bruce Allan +Date: Wed, 22 Sep 2010 17:15:54 +0000 (+0000) +Subject: e1000e: 82566DC fails to get link +X-Git-Tag: v2.6.36-rc6~6^2~23 +X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=5f3eed6fe0e36e4b56c8dd9160241a868ee0de2a + +e1000e: 82566DC fails to get link + +Two recent patches to cleanup the reset[1] and initial PHY configuration[2] +code paths for ICH/PCH devices inadvertently left out a 10msec delay and +device ID check respectively which are necessary for the 82566DC (device id +0x104b) to be configured properly, otherwise it will not get link. + +[1] commit e98cac447cc1cc418dff1d610a5c79c4f2bdec7f +[2] commit 3f0c16e84438d657d29446f85fe375794a93f159 + +CC: stable@kernel.org +Signed-off-by: Bruce Allan +Tested-by: Jeff Pieper +Signed-off-by: Jeff Kirsher +Signed-off-by: David S. Miller +--- + +diff --git a/drivers/net/e1000e/ich8lan.c b/drivers/net/e1000e/ich8lan.c +index fc8c3ce..6f9cb0d 100644 +--- a/drivers/net/e1000e/ich8lan.c ++++ b/drivers/net/e1000e/ich8lan.c +@@ -932,7 +932,6 @@ out: + **/ + static s32 e1000_sw_lcd_config_ich8lan(struct e1000_hw *hw) + { +- struct e1000_adapter *adapter = hw->adapter; + struct e1000_phy_info *phy = &hw->phy; + u32 i, data, cnf_size, cnf_base_addr, sw_cfg_mask; + s32 ret_val = 0; +@@ -950,7 +949,8 @@ static s32 e1000_sw_lcd_config_ich8lan(struct e1000_hw *hw) + if (phy->type != e1000_phy_igp_3) + return ret_val; + +- if (adapter->pdev->device == E1000_DEV_ID_ICH8_IGP_AMT) { ++ if ((hw->adapter->pdev->device == E1000_DEV_ID_ICH8_IGP_AMT) || ++ (hw->adapter->pdev->device == E1000_DEV_ID_ICH8_IGP_C)) { + sw_cfg_mask = E1000_FEXTNVM_SW_CONFIG; + break; + } +@@ -1626,6 +1626,9 @@ static s32 e1000_post_phy_reset_ich8lan(struct e1000_hw *hw) + if (e1000_check_reset_block(hw)) + goto out; + ++ /* Allow time for h/w to get to quiescent state after reset */ ++ msleep(10); ++ + /* Perform any necessary post-reset workarounds */ + switch (hw->mac.type) { + case e1000_pchlan: diff --git a/e1000e-cleanup-e1000_sw_lcd_config_ich8lan.patch b/e1000e-cleanup-e1000_sw_lcd_config_ich8lan.patch new file mode 100644 index 000000000..d56ae5c00 --- /dev/null +++ b/e1000e-cleanup-e1000_sw_lcd_config_ich8lan.patch @@ -0,0 +1,68 @@ +From: Bruce Allan +Date: Wed, 16 Jun 2010 13:26:17 +0000 (+0000) +Subject: e1000e: cleanup e1000_sw_lcd_config_ich8lan() +X-Git-Tag: v2.6.36-rc1~571^2~529 +X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=3f0c16e84438d657d29446f85fe375794a93f159 + +e1000e: cleanup e1000_sw_lcd_config_ich8lan() + +Do not acquire and release the PHY unnecessarily for parts that return +from this workaround without actually accessing the PHY registers. + +Signed-off-by: Bruce Allan +Tested-by: Jeff Pieper +Signed-off-by: Jeff Kirsher +Signed-off-by: David S. Miller +--- + +diff --git a/drivers/net/e1000e/ich8lan.c b/drivers/net/e1000e/ich8lan.c +index b2507d9..5d8fad3 100644 +--- a/drivers/net/e1000e/ich8lan.c ++++ b/drivers/net/e1000e/ich8lan.c +@@ -820,14 +820,6 @@ static s32 e1000_sw_lcd_config_ich8lan(struct e1000_hw *hw) + s32 ret_val = 0; + u16 word_addr, reg_data, reg_addr, phy_page = 0; + +- if (!(hw->mac.type == e1000_ich8lan && phy->type == e1000_phy_igp_3) && +- !(hw->mac.type == e1000_pchlan)) +- return ret_val; +- +- ret_val = hw->phy.ops.acquire(hw); +- if (ret_val) +- return ret_val; +- + /* + * Initialize the PHY from the NVM on ICH platforms. This + * is needed due to an issue where the NVM configuration is +@@ -835,12 +827,26 @@ static s32 e1000_sw_lcd_config_ich8lan(struct e1000_hw *hw) + * Therefore, after each PHY reset, we will load the + * configuration data out of the NVM manually. + */ +- if ((adapter->pdev->device == E1000_DEV_ID_ICH8_IGP_M_AMT) || +- (adapter->pdev->device == E1000_DEV_ID_ICH8_IGP_M) || +- (hw->mac.type == e1000_pchlan)) ++ switch (hw->mac.type) { ++ case e1000_ich8lan: ++ if (phy->type != e1000_phy_igp_3) ++ return ret_val; ++ ++ if (adapter->pdev->device == E1000_DEV_ID_ICH8_IGP_AMT) { ++ sw_cfg_mask = E1000_FEXTNVM_SW_CONFIG; ++ break; ++ } ++ /* Fall-thru */ ++ case e1000_pchlan: + sw_cfg_mask = E1000_FEXTNVM_SW_CONFIG_ICH8M; +- else +- sw_cfg_mask = E1000_FEXTNVM_SW_CONFIG; ++ break; ++ default: ++ return ret_val; ++ } ++ ++ ret_val = hw->phy.ops.acquire(hw); ++ if (ret_val) ++ return ret_val; + + data = er32(FEXTNVM); + if (!(data & sw_cfg_mask)) diff --git a/efifb-add-more-models.patch b/efifb-add-more-models.patch new file mode 100644 index 000000000..81c4ecf6a --- /dev/null +++ b/efifb-add-more-models.patch @@ -0,0 +1,124 @@ +commit a5757c2a474a15f87e5baa9a4caacc31cde2bae6 +Author: Luke Macken +Date: Wed Sep 22 13:05:04 2010 -0700 + + efifb: support the EFI framebuffer on more Apple hardware + + Enable the EFI framebuffer on 14 more Macs, including the iMac11,1 + iMac10,1 iMac8,1 Macmini3,1 Macmini4,1 MacBook5,1 MacBook6,1 MacBook7,1 + MacBookPro2,2 MacBookPro5,2 MacBookPro5,3 MacBookPro6,1 MacBookPro6,2 and + MacBookPro7,1 + + Information gathered from various user submissions. + + https://bugzilla.redhat.com/show_bug.cgi?id=528232 + http://ubuntuforums.org/showthread.php?t=1557326 + + [akpm@linux-foundation.org: coding-style fixes] + Signed-off-by: Luke Macken + Signed-off-by: Peter Jones + Signed-off-by: Andrew Morton + Signed-off-by: Linus Torvalds + +diff --git a/drivers/video/efifb.c b/drivers/video/efifb.c +index c082b61..70477c2 100644 +--- a/drivers/video/efifb.c ++++ b/drivers/video/efifb.c +@@ -39,17 +39,31 @@ enum { + M_I20, /* 20-Inch iMac */ + M_I20_SR, /* 20-Inch iMac (Santa Rosa) */ + M_I24, /* 24-Inch iMac */ ++ M_I24_8_1, /* 24-Inch iMac, 8,1th gen */ ++ M_I24_10_1, /* 24-Inch iMac, 10,1th gen */ ++ M_I27_11_1, /* 27-Inch iMac, 11,1th gen */ + M_MINI, /* Mac Mini */ ++ M_MINI_3_1, /* Mac Mini, 3,1th gen */ ++ M_MINI_4_1, /* Mac Mini, 4,1th gen */ + M_MB, /* MacBook */ + M_MB_2, /* MacBook, 2nd rev. */ + M_MB_3, /* MacBook, 3rd rev. */ ++ M_MB_5_1, /* MacBook, 5th rev. */ ++ M_MB_6_1, /* MacBook, 6th rev. */ ++ M_MB_7_1, /* MacBook, 7th rev. */ + M_MB_SR, /* MacBook, 2nd gen, (Santa Rosa) */ + M_MBA, /* MacBook Air */ + M_MBP, /* MacBook Pro */ + M_MBP_2, /* MacBook Pro 2nd gen */ ++ M_MBP_2_2, /* MacBook Pro 2,2nd gen */ + M_MBP_SR, /* MacBook Pro (Santa Rosa) */ + M_MBP_4, /* MacBook Pro, 4th gen */ + M_MBP_5_1, /* MacBook Pro, 5,1th gen */ ++ M_MBP_5_2, /* MacBook Pro, 5,2th gen */ ++ M_MBP_5_3, /* MacBook Pro, 5,3rd gen */ ++ M_MBP_6_1, /* MacBook Pro, 6,1th gen */ ++ M_MBP_6_2, /* MacBook Pro, 6,2th gen */ ++ M_MBP_7_1, /* MacBook Pro, 7,1th gen */ + M_UNKNOWN /* placeholder */ + }; + +@@ -64,14 +78,28 @@ static struct efifb_dmi_info { + [M_I20] = { "i20", 0x80010000, 1728 * 4, 1680, 1050 }, /* guess */ + [M_I20_SR] = { "imac7", 0x40010000, 1728 * 4, 1680, 1050 }, + [M_I24] = { "i24", 0x80010000, 2048 * 4, 1920, 1200 }, /* guess */ ++ [M_I24_8_1] = { "imac8", 0xc0060000, 2048 * 4, 1920, 1200 }, ++ [M_I24_10_1] = { "imac10", 0xc0010000, 2048 * 4, 1920, 1080 }, ++ [M_I27_11_1] = { "imac11", 0xc0010000, 2560 * 4, 2560, 1440 }, + [M_MINI]= { "mini", 0x80000000, 2048 * 4, 1024, 768 }, ++ [M_MINI_3_1] = { "mini31", 0x40010000, 1024 * 4, 1024, 768 }, ++ [M_MINI_4_1] = { "mini41", 0xc0010000, 2048 * 4, 1920, 1200 }, + [M_MB] = { "macbook", 0x80000000, 2048 * 4, 1280, 800 }, ++ [M_MB_5_1] = { "macbook51", 0x80010000, 2048 * 4, 1280, 800 }, ++ [M_MB_6_1] = { "macbook61", 0x80010000, 2048 * 4, 1280, 800 }, ++ [M_MB_7_1] = { "macbook71", 0x80010000, 2048 * 4, 1280, 800 }, + [M_MBA] = { "mba", 0x80000000, 2048 * 4, 1280, 800 }, + [M_MBP] = { "mbp", 0x80010000, 1472 * 4, 1440, 900 }, + [M_MBP_2] = { "mbp2", 0, 0, 0, 0 }, /* placeholder */ ++ [M_MBP_2_2] = { "mbp22", 0x80010000, 1472 * 4, 1440, 900 }, + [M_MBP_SR] = { "mbp3", 0x80030000, 2048 * 4, 1440, 900 }, + [M_MBP_4] = { "mbp4", 0xc0060000, 2048 * 4, 1920, 1200 }, + [M_MBP_5_1] = { "mbp51", 0xc0010000, 2048 * 4, 1440, 900 }, ++ [M_MBP_5_2] = { "mbp52", 0xc0010000, 2048 * 4, 1920, 1200 }, ++ [M_MBP_5_3] = { "mbp53", 0xd0010000, 2048 * 4, 1440, 900 }, ++ [M_MBP_6_1] = { "mbp61", 0x90030000, 2048 * 4, 1920, 1200 }, ++ [M_MBP_6_2] = { "mbp62", 0x90030000, 2048 * 4, 1680, 1050 }, ++ [M_MBP_7_1] = { "mbp71", 0xc0010000, 2048 * 4, 1280, 800 }, + [M_UNKNOWN] = { NULL, 0, 0, 0, 0 } + }; + +@@ -92,7 +120,12 @@ static const struct dmi_system_id dmi_system_table[] __initconst = { + EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "iMac6,1", M_I24), + EFIFB_DMI_SYSTEM_ID("Apple Inc.", "iMac6,1", M_I24), + EFIFB_DMI_SYSTEM_ID("Apple Inc.", "iMac7,1", M_I20_SR), ++ EFIFB_DMI_SYSTEM_ID("Apple Inc.", "iMac8,1", M_I24_8_1), ++ EFIFB_DMI_SYSTEM_ID("Apple Inc.", "iMac10,1", M_I24_10_1), ++ EFIFB_DMI_SYSTEM_ID("Apple Inc.", "iMac11,1", M_I27_11_1), + EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "Macmini1,1", M_MINI), ++ EFIFB_DMI_SYSTEM_ID("Apple Inc.", "Macmini3,1", M_MINI_3_1), ++ EFIFB_DMI_SYSTEM_ID("Apple Inc.", "Macmini4,1", M_MINI_4_1), + EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBook1,1", M_MB), + /* At least one of these two will be right; maybe both? */ + EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBook2,1", M_MB), +@@ -101,14 +134,23 @@ static const struct dmi_system_id dmi_system_table[] __initconst = { + EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBook3,1", M_MB), + EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBook3,1", M_MB), + EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBook4,1", M_MB), ++ EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBook5,1", M_MB_5_1), ++ EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBook6,1", M_MB_6_1), ++ EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBook7,1", M_MB_7_1), + EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookAir1,1", M_MBA), + EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBookPro1,1", M_MBP), + EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBookPro2,1", M_MBP_2), ++ EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBookPro2,2", M_MBP_2_2), + EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro2,1", M_MBP_2), + EFIFB_DMI_SYSTEM_ID("Apple Computer, Inc.", "MacBookPro3,1", M_MBP_SR), + EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro3,1", M_MBP_SR), + EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro4,1", M_MBP_4), + EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro5,1", M_MBP_5_1), ++ EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro5,2", M_MBP_5_2), ++ EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro5,3", M_MBP_5_3), ++ EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro6,1", M_MBP_6_1), ++ EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro6,2", M_MBP_6_2), ++ EFIFB_DMI_SYSTEM_ID("Apple Inc.", "MacBookPro7,1", M_MBP_7_1), + {}, + }; + diff --git a/efifb-check-that-the-base-addr-is-plausible-on-pci-systems.patch b/efifb-check-that-the-base-addr-is-plausible-on-pci-systems.patch new file mode 100644 index 000000000..a96fd9805 --- /dev/null +++ b/efifb-check-that-the-base-addr-is-plausible-on-pci-systems.patch @@ -0,0 +1,135 @@ +commit 85a00d9bbfb4704fbf368944b1cb9fed8f1598c5 +Author: Peter Jones +Date: Wed Sep 22 13:05:04 2010 -0700 + + efifb: check that the base address is plausible on pci systems + + Some Apple machines have identical DMI data but different memory + configurations for the video. Given that, check that the address in our + table is actually within the range of a PCI BAR on a VGA device in the + machine. + + This also fixes up the return value from set_system(), which has always + been wrong, but never resulted in bad behavior since there's only ever + been one matching entry in the dmi table. + + The patch + + 1) stops people's machines from crashing when we get their display wrong, + which seems to be unfortunately inevitable, + + 2) allows us to support identical dmi data with differing video memory + configurations + + This also adds me as the efifb maintainer, since I've effectively been + acting as such for quite some time. + + Signed-off-by: Peter Jones + Signed-off-by: Andrew Morton + Signed-off-by: Linus Torvalds + +diff --git a/MAINTAINERS b/MAINTAINERS +index 726433a..4d4881d 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -2199,6 +2199,12 @@ W: http://acpi4asus.sf.net + S: Maintained + F: drivers/platform/x86/eeepc-laptop.c + ++EFIFB FRAMEBUFFER DRIVER ++L: linux-fbdev@vger.kernel.org ++M: Peter Jones ++S: Maintained ++F: drivers/video/efifb.c ++ + EFS FILESYSTEM + W: http://aeschi.ch.eu.org/efs/ + S: Orphan +diff --git a/drivers/video/efifb.c b/drivers/video/efifb.c +index 815f84b..c082b61 100644 +--- a/drivers/video/efifb.c ++++ b/drivers/video/efifb.c +@@ -13,7 +13,7 @@ + #include + #include + #include +- ++#include + #include