summaryrefslogtreecommitdiffstats
path: root/freed-ora/tags/f13/2.6.33.5-131.fc13/drm-nouveau-updates.patch
diff options
context:
space:
mode:
authorAlexandre Oliva <lxoliva@fsfla.org>2010-08-14 04:03:56 +0000
committerAlexandre Oliva <lxoliva@fsfla.org>2010-08-14 04:03:56 +0000
commitcb3073a52ff53c702442b12ce3ba46c8ac860d1b (patch)
tree429a21d53929335b2dd411c50d8acda4fd739cfe /freed-ora/tags/f13/2.6.33.5-131.fc13/drm-nouveau-updates.patch
parenteaf70c475fea4240c562209affe5c163c3f6bd0c (diff)
downloadlinux-libre-raptor-cb3073a52ff53c702442b12ce3ba46c8ac860d1b.tar.gz
linux-libre-raptor-cb3073a52ff53c702442b12ce3ba46c8ac860d1b.zip
Rename F-13 to f13.
Diffstat (limited to 'freed-ora/tags/f13/2.6.33.5-131.fc13/drm-nouveau-updates.patch')
-rw-r--r--freed-ora/tags/f13/2.6.33.5-131.fc13/drm-nouveau-updates.patch7669
1 files changed, 7669 insertions, 0 deletions
diff --git a/freed-ora/tags/f13/2.6.33.5-131.fc13/drm-nouveau-updates.patch b/freed-ora/tags/f13/2.6.33.5-131.fc13/drm-nouveau-updates.patch
new file mode 100644
index 000000000..a1d112521
--- /dev/null
+++ b/freed-ora/tags/f13/2.6.33.5-131.fc13/drm-nouveau-updates.patch
@@ -0,0 +1,7669 @@
+* drivers/gpu/drm/nouveau/nv50_graph.c: Adjusted, was already deblobbed.
+
+From a4cbb7f1379aa9817c85840c6d079dd222641dbd Mon Sep 17 00:00:00 2001
+From: Marcin Slusarz <marcin.slusarz@gmail.com>
+Date: Wed, 17 Feb 2010 19:04:00 +0100
+Subject: [PATCH] drm-nouveau-updates
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+drm/nouveau: fix pramdac_table range checking
+
+get_tmds_index_reg reads some value from stack when mlv happens
+to be equal to size of pramdac_table array. Fix it.
+
+Reported-by: Dan Carpenter <error27@gmail.com>
+Signed-off-by: Marcin Slusarz <marcin.slusarz@gmail.com>
+Signed-off-by: Francisco Jerez <currojerez@riseup.net>
+
+drm/nouveau: fix nouveau_i2c_find bounds checking
+
+Reported-by: Dan Carpenter <error27@gmail.com>
+Signed-off-by: Marcin Slusarz <marcin.slusarz@gmail.com>
+Signed-off-by: Francisco Jerez <currojerez@riseup.net>
+
+drm/nouveau: fix i2ctable bounds checking
+
+i2c_entries seems to be the number of i2c entries,
+so with index equal to this number, we could read
+invalid data from i2ctable. Fix it.
+
+Signed-off-by: Marcin Slusarz <marcin.slusarz@gmail.com>
+Signed-off-by: Francisco Jerez <currojerez@riseup.net>
+
+drm/nouveau: allow retrieval of vbios image from debugfs
+
+It's very useful to be able to access this without additional tools for
+debugging purposes.
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nouveau: rename parsed_dcb_gpio to dcb_gpio_table
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nouveau: merge parsed_dcb and bios_parsed_dcb into dcb_table
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nouveau: merge nvbios and nouveau_bios_info
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nouveau: reorganise bios header, add dcb connector type enums
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nouveau: parse dcb gpio/connector tables after encoders
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nouveau: check for known dcb connector types
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nouveau: construct a connector table for cards that lack a real one
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nouveau: use dcb connector table for creating drm connectors
+
+This makes this code common to both the nv04 and nv50 paths.
+
+For the moment, we keep the previous behaviour with HDMI/eDP connectors
+and report them as DVI-D/DP instead. This will be fixed once the rest
+of the code has been fixed to deal with those types.
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nv50: enable hpd on any connector we know the gpio line for
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nouveau: use dcb connector types throughout the driver
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nv50: Implement ctxprog/state generation.
+
+This removes dependence on external firmware for NV50 generation cards.
+If the generated ctxprogs don't work for you for some reason, please
+report it.
+
+Signed-off-by: Marcin Kościelnicki <koriakin@0x04.net>
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nouveau: Fix noaccel/nofbaccel option descriptions.
+
+Signed-off-by: Marcin Kościelnicki <koriakin@0x04.net>
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nouveau: support version 0x20 displayport tables
+
+Not entirely identical to 0x21, the per-encoder table header lacks the
+third init table pointer. However, our current parsing of the table
+should work just fine.
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nouveau: report unknown connector state if lid closed
+
+This is in preference to disconnected. If there's no other outputs
+connected this will cause LVDS to be programmed even with the lid
+closed rather than having X fail to start because of no available
+outputs.
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nouveau: use ALIGN instead of open coding it
+
+CC: Ben Skeggs <bskeggs@redhat.com>
+Signed-off-by: Matt Turner <mattst88@gmail.com>
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nouveau: protect channel create/destroy and irq handler with a spinlock
+
+The nv50 pgraph handler (for example) could reenable pgraph fifo access
+and that would be bad when pgraph context is being unloaded (we need the
+guarantee a ctxprog isn't running).
+
+Signed-off-by: Maarten Maathuis <madman2003@gmail.com>
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nv50: Remove redundant/incorrect ctxvals initialisation.
+
+11c/004 offset corresponds to PGRAPH reg 0x400828, and is initialised
+earlier anyway by both our ctxprog generator and blob ctxvals. It's
+actually incorrect with the generator, since we use different layout
+on pre-NVA0.
+
+Signed-off-by: Marcin Kościelnicki <koriakin@0x04.net>
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nouveau: Fix fbcon corruption with font width not divisible by 8
+
+NV50 is nice and has a switch that autoaligns stuff for us. Pre-NV50,
+we need to align input bitmap width manually.
+
+Signed-off-by: Marcin Kościelnicki <koriakin@0x04.net>
+Signed-off-by: Francisco Jerez <currojerez@riseup.net>
+
+drm/nv50: Make ctxprog wait until interrupt handler is done.
+
+This will fix races between generated ctxprogs and interrupt handler.
+
+Signed-off-by: Marcin Kościelnicki <koriakin@0x04.net>
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nv50: Improve PGRAPH interrupt handling.
+
+This makes nouveau recognise and report more kinds of PGRAPH errors, as
+well as prevent GPU lockups resulting from some of them.
+
+Lots of guesswork was involved and some part of this is probably
+incorrect. Some potential-lockuop situations are handled by just
+resetting a whole PGRAPH subunit, which doesn't sound like a "proper"
+solution, but seems to work just fine... for now.
+
+Signed-off-by: Marcin Kościelnicki <koriakin@0x04.net>
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nouveau: add option to allow override of dcb connector table types
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nouveau: Gigabyte NX85T connector table lies, it has DVI-I not HDMI
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nv04-nv40: Fix up the programmed horizontal sync pulse delay.
+
+The calculated values were a little bit off (~16 clocks), the only
+effect it could have had is a slightly offset image with respect to
+the blob on analog outputs (bug 26790).
+
+Signed-off-by: Francisco Jerez <currojerez@riseup.net>
+
+drm/nouveau: print a message very early during suspend
+
+- In case of suspend lockups it's nice to know it happened in nouveau.
+
+Signed-off-by: Maarten Maathuis <madman2003@gmail.com>
+
+drm/nv50: add a memory barrier to pushbuf submission
+
+- This is useful for vram pushbuffers that are write combined.
+- pre-nv50 has one too (in WRITE_PUT).
+
+Signed-off-by: Maarten Maathuis <madman2003@gmail.com>
+
+drm/nv50: fix connector table parsing for some cards
+
+The connector table index in the DCB entry for each output type is an
+index into the connector table, and does *not* necessarily match up
+with what was previously called "index" in the connector table entries
+themselves.
+
+Not real sure what that index is exactly, renamed to "index2" as we
+still use it to prevent creating multiple TV connectors.
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nouveau: Never evict VRAM buffers to system.
+
+VRAM->system is a synchronous operation: it involves scheduling a
+VRAM->TT DMA transfer and stalling the CPU until it's finished so that
+we can unbind the new memory from the translation tables. VRAM->TT can
+always be performed asynchronously, even if TT is already full and we
+have to move something out of it.
+
+Additionally, allowing VRAM->system behaves badly under heavy memory
+pressure because once we run out of TT, stuff starts to be moved back
+and forth between VRAM and system, and the TT contents are hardly
+renewed.
+
+Signed-off-by: Francisco Jerez <currojerez@riseup.net>
+
+drm/nouveau: add module option to disable TV detection
+
+Intended to be used as a workaround in cases where we falsely detect
+that a TV is connected when it's not.
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nv50: add more 0x100c80 flushy magic
+
+Fixes the !vbo_fifo path in the 3D driver on certain chipsets. Still not
+really any good idea of what exactly the magic achieves, but it makes
+things work.
+
+While we're at it, in the PCIEGART path, flush on unbinding also.
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nouveau: bail out of auxch transaction if we repeatedly recieve defers
+
+There's one known case where we never stop recieving DEFER, and loop here
+forever. Lets not do that..
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nv50: fix fbcon when framebuffer above 4GiB mark
+
+This can't actually happen right now, but lets fix it anyway.
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nv50: Fix NEWCTX_DONE flag number
+
+Signed-off-by: Marcin Kościelnicki <koriakin@0x04.net>
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nouveau: remove some unused members from drm_nouveau_private
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nouveau: detect vram amount once, and save the value
+
+As opposed to repeatedly reading the amount back from the GPU every
+time we need to know the VRAM size.
+
+We should now fail to load gracefully on detecting no VRAM, rather than
+something potentially messy happening.
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nv40: rework lvds table parsing
+
+All indications seem to be that the version 0x30 table should be handled
+the same way as 0x40 (as used on G80), at least for the parts that we
+currently try use.
+
+This commit cleans up the parsing to make it clearer about what we're
+actually trying to achieve, and unifies the 0x30/0x40 parsing.
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nv40: add LVDS table quirk for Dell Latitude D620
+
+Should fix:
+ https://bugzilla.redhat.com/show_bug.cgi?id=505132
+ https://bugzilla.redhat.com/show_bug.cgi?id=543091
+ https://bugzilla.redhat.com/show_bug.cgi?id=530425
+ https://bugs.edge.launchpad.net/ubuntu/+source/xserver-xorg-video-nouveau/
+ +bug/539730
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nv50: fix instmem init on IGPs if stolen mem crosses 4GiB mark
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nouveau: fixup the init failure paths some more
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nv50: move pdisp init earlier, and cleanup if it fails
+
+Moving it earlier is to avoid some extra cleanup if it fails.
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nv50: Allow using the NVA3 new compute class.
+
+Signed-off-by: Marcin Kościelnicki <koriakin@0x04.net>
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nouveau: Make use of TTM busy_placements.
+
+Previously we were filling it the same as "placements", but in some
+cases there're valid alternatives that we were ignoring completely.
+Keeping a back-up memory type helps on several low-mem situations.
+
+Signed-off-by: Francisco Jerez <currojerez@riseup.net>
+
+drm/nv50: preserve an unknown SOR_MODECTRL value for DP encoders
+
+This value interacts with some registers we don't currently know how to
+program properly ourselves. The default of 5 that we were using matches
+what the VBIOS on early DP cards do, but later ones use 6, which would
+cause nouveau to program an incorrect mode on these chips.
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nv50: punt hotplug irq handling out to workqueue
+
+On DP outputs we'll likely end up running vbios init tables here, which
+may sleep.
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nv50: partially revert ec99dbe438787d62ecde3a22f8ce3f880a4f4e14
+
+The commit mentioned above breaks the DP SOR_MODE_CTRL detection as once
+nv50_display_init() has been called all the MODE_CTRL registers are reset.
+
+This wasn't noticed when initially writing the DP SOR_MODE_CTRL patch
+as it was done on another machine, without ec99dbe..4e14 applied.
+
+This commit moves the nv50_display_init() call to back where it was,
+after the KMS setup, and adds the additional cleanup needed.
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nv50: another dodgy DP hack
+
+Allows *some* DP cards to keep working in some corner cases that most
+people shouldn't hit. I hit it all the time with development, so this
+can stay for now.
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nv50: Add NVA3 support in ctxprog/ctxvals generator.
+
+Signed-off-by: Marcin Kościelnicki <koriakin@0x04.net>
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nv40: Init some tiling-related PGRAPH state.
+
+Fixes garbled 3D on an nv46 card.
+
+Reported-by: Francesco Marella <francesco.marella@gmail.com>
+Signed-off-by: Francisco Jerez <currojerez@riseup.net>
+
+drm/nouveau: store raw gpio table entry in bios gpio structs
+
+And use our own version of the GPIO table for the INIT_GPIO opcode.
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nv50: parse/use some more de-magiced parts of gpio table entries
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nv50: implement gpio set/get routines
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+Revert "drm/nouveau: report unknown connector state if lid closed"
+
+Included in upstream stable point-release.
+
+This reverts commit b30083bdb990bcc2829fce83d871a86059ff4fc1.
+
+drm/nouveau: fix a nouveau_bo dereference after it's been destroyed
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nouveau: bios parser fixes for eDP boards
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nouveau: dump pll limits entries when debugging is on
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nv50: output calculated crtc pll when debugging on
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nv50: fix suspend/resume with DP outputs
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nv50: store full dcb i2c entry from vbios
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nv50: fix monitor detection on certain chipsets
+
+There appears to be some kind of switch on certain chips to control whether
+the DP auxch or traditional i2c bus will be operational on a connector,
+this commit hopefully fixes nouveau to do the right thing.
+
+Likely only relevent on chips with DP outputs.
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nv50: send hotplug event to userspace
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nv50: support fractional feedback divider on newer chips
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nouveau: don't execute INIT_GPIO unless we're really running the table
+
+This resulted in accidently switching off the eDP panel on certain laptops
+since the default state in the GPIO table was off.
+
+Fixes rh#582621
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+
+drm/nv50: fix iommu errors caused by device reading from address 0
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+---
+ drivers/gpu/drm/nouveau/Makefile | 7 +-
+ drivers/gpu/drm/nouveau/nouveau_bios.c | 672 +++++---
+ drivers/gpu/drm/nouveau/nouveau_bios.h | 132 +-
+ drivers/gpu/drm/nouveau/nouveau_bo.c | 68 +-
+ drivers/gpu/drm/nouveau/nouveau_calc.c | 4 +-
+ drivers/gpu/drm/nouveau/nouveau_channel.c | 15 +-
+ drivers/gpu/drm/nouveau/nouveau_connector.c | 163 ++-
+ drivers/gpu/drm/nouveau/nouveau_connector.h | 3 +-
+ drivers/gpu/drm/nouveau/nouveau_debugfs.c | 18 +-
+ drivers/gpu/drm/nouveau/nouveau_dma.c | 5 +
+ drivers/gpu/drm/nouveau/nouveau_dp.c | 8 +-
+ drivers/gpu/drm/nouveau/nouveau_drv.c | 14 +-
+ drivers/gpu/drm/nouveau/nouveau_drv.h | 59 +-
+ drivers/gpu/drm/nouveau/nouveau_encoder.h | 3 +
+ drivers/gpu/drm/nouveau/nouveau_gem.c | 55 +-
+ drivers/gpu/drm/nouveau/nouveau_hw.c | 6 +-
+ drivers/gpu/drm/nouveau/nouveau_i2c.c | 23 +-
+ drivers/gpu/drm/nouveau/nouveau_irq.c | 615 +++++++-
+ drivers/gpu/drm/nouveau/nouveau_mem.c | 124 +-
+ drivers/gpu/drm/nouveau/nouveau_reg.h | 1 +
+ drivers/gpu/drm/nouveau/nouveau_sgdma.c | 18 +
+ drivers/gpu/drm/nouveau/nouveau_state.c | 20 +-
+ drivers/gpu/drm/nouveau/nv04_crtc.c | 6 +-
+ drivers/gpu/drm/nouveau/nv04_dac.c | 8 +-
+ drivers/gpu/drm/nouveau/nv04_dfp.c | 4 +-
+ drivers/gpu/drm/nouveau/nv04_display.c | 49 +-
+ drivers/gpu/drm/nouveau/nv04_fbcon.c | 6 +-
+ drivers/gpu/drm/nouveau/nv04_fifo.c | 5 +
+ drivers/gpu/drm/nouveau/nv04_tv.c | 2 +-
+ drivers/gpu/drm/nouveau/nv17_tv.c | 6 +-
+ drivers/gpu/drm/nouveau/nv40_fifo.c | 7 +-
+ drivers/gpu/drm/nouveau/nv40_graph.c | 21 +
+ drivers/gpu/drm/nouveau/nv50_calc.c | 88 +
+ drivers/gpu/drm/nouveau/nv50_crtc.c | 46 +-
+ drivers/gpu/drm/nouveau/nv50_dac.c | 4 +-
+ drivers/gpu/drm/nouveau/nv50_display.c | 111 +-
+ drivers/gpu/drm/nouveau/nv50_display.h | 1 +
+ drivers/gpu/drm/nouveau/nv50_fb.c | 38 +
+ drivers/gpu/drm/nouveau/nv50_fbcon.c | 17 +-
+ drivers/gpu/drm/nouveau/nv50_fifo.c | 5 +
+ drivers/gpu/drm/nouveau/nv50_gpio.c | 76 +
+ drivers/gpu/drm/nouveau/nv50_graph.c | 103 +-
+ drivers/gpu/drm/nouveau/nv50_grctx.c | 2383 +++++++++++++++++++++++++++
+ drivers/gpu/drm/nouveau/nv50_instmem.c | 18 +-
+ drivers/gpu/drm/nouveau/nv50_sor.c | 30 +-
+ 45 files changed, 4304 insertions(+), 763 deletions(-)
+ create mode 100644 drivers/gpu/drm/nouveau/nv50_calc.c
+ create mode 100644 drivers/gpu/drm/nouveau/nv50_fb.c
+ create mode 100644 drivers/gpu/drm/nouveau/nv50_gpio.c
+ create mode 100644 drivers/gpu/drm/nouveau/nv50_grctx.c
+
+diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile
+index 48c290b..acd31ed 100644
+--- a/drivers/gpu/drm/nouveau/Makefile
++++ b/drivers/gpu/drm/nouveau/Makefile
+@@ -12,17 +12,18 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \
+ nouveau_dp.o nouveau_grctx.o \
+ nv04_timer.o \
+ nv04_mc.o nv40_mc.o nv50_mc.o \
+- nv04_fb.o nv10_fb.o nv40_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_graph.o nv10_graph.o nv20_graph.o \
+ nv40_graph.o nv50_graph.o \
+- nv40_grctx.o \
++ nv40_grctx.o nv50_grctx.o \
+ nv04_instmem.o nv50_instmem.o \
+ nv50_crtc.o nv50_dac.o nv50_sor.o \
+ nv50_cursor.o nv50_display.o nv50_fbcon.o \
+ nv04_dac.o nv04_dfp.o nv04_tv.o nv17_tv.o nv17_tv_modes.o \
+ nv04_crtc.o nv04_display.o nv04_cursor.o nv04_fbcon.o \
+- nv17_gpio.o
++ nv17_gpio.o nv50_gpio.o \
++ nv50_calc.o
+
+ nouveau-$(CONFIG_DRM_NOUVEAU_DEBUG) += nouveau_debugfs.o
+ nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o
+diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c
+index 0e9cd1d..5472c76 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_bios.c
++++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
+@@ -26,6 +26,7 @@
+ #define NV_DEBUG_NOTRACE
+ #include "nouveau_drv.h"
+ #include "nouveau_hw.h"
++#include "nouveau_encoder.h"
+
+ /* these defines are made up */
+ #define NV_CIO_CRE_44_HEADA 0x0
+@@ -311,11 +312,11 @@ valid_reg(struct nvbios *bios, uint32_t reg)
+
+ /* C51 has misaligned regs on purpose. Marvellous */
+ if (reg & 0x2 ||
+- (reg & 0x1 && dev_priv->VBIOS.pub.chip_version != 0x51))
++ (reg & 0x1 && dev_priv->vbios.chip_version != 0x51))
+ NV_ERROR(dev, "======= misaligned reg 0x%08X =======\n", reg);
+
+ /* warn on C51 regs that haven't been verified accessible in tracing */
+- if (reg & 0x1 && dev_priv->VBIOS.pub.chip_version == 0x51 &&
++ if (reg & 0x1 && dev_priv->vbios.chip_version == 0x51 &&
+ reg != 0x130d && reg != 0x1311 && reg != 0x60081d)
+ NV_WARN(dev, "=== C51 misaligned reg 0x%08X not verified ===\n",
+ reg);
+@@ -420,7 +421,7 @@ bios_wr32(struct nvbios *bios, uint32_t reg, uint32_t data)
+ LOG_OLD_VALUE(bios_rd32(bios, reg));
+ BIOSLOG(bios, " Write: Reg: 0x%08X, Data: 0x%08X\n", reg, data);
+
+- if (dev_priv->VBIOS.execute) {
++ if (dev_priv->vbios.execute) {
+ still_alive();
+ nv_wr32(bios->dev, reg, data);
+ }
+@@ -647,7 +648,7 @@ nv50_pll_set(struct drm_device *dev, uint32_t reg, uint32_t clk)
+ reg0 = (reg0 & 0xfff8ffff) | (pll.log2P << 16);
+ reg1 = (reg1 & 0xffff0000) | (pll.N1 << 8) | pll.M1;
+
+- if (dev_priv->VBIOS.execute) {
++ if (dev_priv->vbios.execute) {
+ still_alive();
+ nv_wr32(dev, reg + 4, reg1);
+ nv_wr32(dev, reg + 0, reg0);
+@@ -689,7 +690,7 @@ setPLL(struct nvbios *bios, uint32_t reg, uint32_t clk)
+ static int dcb_entry_idx_from_crtchead(struct drm_device *dev)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nvbios *bios = &dev_priv->VBIOS;
++ struct nvbios *bios = &dev_priv->vbios;
+
+ /*
+ * For the results of this function to be correct, CR44 must have been
+@@ -700,7 +701,7 @@ static int dcb_entry_idx_from_crtchead(struct drm_device *dev)
+
+ uint8_t dcb_entry = NVReadVgaCrtc5758(dev, bios->state.crtchead, 0);
+
+- if (dcb_entry > bios->bdcb.dcb.entries) {
++ if (dcb_entry > bios->dcb.entries) {
+ NV_ERROR(dev, "CR58 doesn't have a valid DCB entry currently "
+ "(%02X)\n", dcb_entry);
+ dcb_entry = 0x7f; /* unused / invalid marker */
+@@ -713,25 +714,26 @@ static struct nouveau_i2c_chan *
+ init_i2c_device_find(struct drm_device *dev, int i2c_index)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct bios_parsed_dcb *bdcb = &dev_priv->VBIOS.bdcb;
++ struct dcb_table *dcb = &dev_priv->vbios.dcb;
+
+ if (i2c_index == 0xff) {
+ /* note: dcb_entry_idx_from_crtchead needs pre-script set-up */
+ int idx = dcb_entry_idx_from_crtchead(dev), shift = 0;
+- int default_indices = bdcb->i2c_default_indices;
++ int default_indices = dcb->i2c_default_indices;
+
+- if (idx != 0x7f && bdcb->dcb.entry[idx].i2c_upper_default)
++ if (idx != 0x7f && dcb->entry[idx].i2c_upper_default)
+ shift = 4;
+
+ i2c_index = (default_indices >> shift) & 0xf;
+ }
+ if (i2c_index == 0x80) /* g80+ */
+- i2c_index = bdcb->i2c_default_indices & 0xf;
++ i2c_index = dcb->i2c_default_indices & 0xf;
+
+ return nouveau_i2c_find(dev, i2c_index);
+ }
+
+-static uint32_t get_tmds_index_reg(struct drm_device *dev, uint8_t mlv)
++static uint32_t
++get_tmds_index_reg(struct drm_device *dev, uint8_t mlv)
+ {
+ /*
+ * For mlv < 0x80, it is an index into a table of TMDS base addresses.
+@@ -744,6 +746,7 @@ static uint32_t get_tmds_index_reg(struct drm_device *dev, uint8_t mlv)
+ */
+
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nvbios *bios = &dev_priv->vbios;
+ const int pramdac_offset[13] = {
+ 0, 0, 0x8, 0, 0x2000, 0, 0, 0, 0x2008, 0, 0, 0, 0x2000 };
+ const uint32_t pramdac_table[4] = {
+@@ -756,13 +759,12 @@ static uint32_t get_tmds_index_reg(struct drm_device *dev, uint8_t mlv)
+ dcb_entry = dcb_entry_idx_from_crtchead(dev);
+ if (dcb_entry == 0x7f)
+ return 0;
+- dacoffset = pramdac_offset[
+- dev_priv->VBIOS.bdcb.dcb.entry[dcb_entry].or];
++ dacoffset = pramdac_offset[bios->dcb.entry[dcb_entry].or];
+ if (mlv == 0x81)
+ dacoffset ^= 8;
+ return 0x6808b0 + dacoffset;
+ } else {
+- if (mlv > ARRAY_SIZE(pramdac_table)) {
++ if (mlv >= ARRAY_SIZE(pramdac_table)) {
+ NV_ERROR(dev, "Magic Lookup Value too big (%02X)\n",
+ mlv);
+ return 0;
+@@ -1066,6 +1068,126 @@ init_io_flag_condition(struct nvbios *bios, uint16_t offset,
+ }
+
+ static int
++init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_DP_CONDITION opcode: 0x3A ('')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (8 bit): "sub" opcode
++ * offset + 2 (8 bit): unknown
++ *
++ */
++
++ struct bit_displayport_encoder_table *dpe = NULL;
++ struct dcb_entry *dcb = bios->display.output;
++ struct drm_device *dev = bios->dev;
++ uint8_t cond = bios->data[offset + 1];
++ int dummy;
++
++ BIOSLOG(bios, "0x%04X: subop 0x%02X\n", offset, cond);
++
++ if (!iexec->execute)
++ return 3;
++
++ dpe = nouveau_bios_dp_table(dev, dcb, &dummy);
++ if (!dpe) {
++ NV_ERROR(dev, "0x%04X: INIT_3A: no encoder table!!\n", offset);
++ return -EINVAL;
++ }
++
++ switch (cond) {
++ case 0:
++ {
++ struct dcb_connector_table_entry *ent =
++ &bios->dcb.connector.entry[dcb->connector];
++
++ if (ent->type != DCB_CONNECTOR_eDP)
++ iexec->execute = false;
++ }
++ break;
++ case 1:
++ case 2:
++ if (!(dpe->unknown & cond))
++ iexec->execute = false;
++ break;
++ case 5:
++ {
++ struct nouveau_i2c_chan *auxch;
++ int ret;
++
++ auxch = nouveau_i2c_find(dev, bios->display.output->i2c_index);
++ if (!auxch)
++ return -ENODEV;
++
++ ret = nouveau_dp_auxch(auxch, 9, 0xd, &cond, 1);
++ if (ret)
++ return ret;
++
++ if (cond & 1)
++ iexec->execute = false;
++ }
++ break;
++ default:
++ NV_WARN(dev, "0x%04X: unknown INIT_3A op: %d\n", offset, cond);
++ break;
++ }
++
++ if (iexec->execute)
++ BIOSLOG(bios, "0x%04X: continuing to execute\n", offset);
++ else
++ BIOSLOG(bios, "0x%04X: skipping following commands\n", offset);
++
++ return 3;
++}
++
++static int
++init_op_3b(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_3B opcode: 0x3B ('')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (8 bit): crtc index
++ *
++ */
++
++ uint8_t or = ffs(bios->display.output->or) - 1;
++ uint8_t index = bios->data[offset + 1];
++ uint8_t data;
++
++ if (!iexec->execute)
++ return 2;
++
++ data = bios_idxprt_rd(bios, 0x3d4, index);
++ bios_idxprt_wr(bios, 0x3d4, index, data & ~(1 << or));
++ return 2;
++}
++
++static int
++init_op_3c(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_3C opcode: 0x3C ('')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (8 bit): crtc index
++ *
++ */
++
++ uint8_t or = ffs(bios->display.output->or) - 1;
++ uint8_t index = bios->data[offset + 1];
++ uint8_t data;
++
++ if (!iexec->execute)
++ return 2;
++
++ data = bios_idxprt_rd(bios, 0x3d4, index);
++ bios_idxprt_wr(bios, 0x3d4, index, data | (1 << or));
++ return 2;
++}
++
++static int
+ init_idx_addr_latched(struct nvbios *bios, uint16_t offset,
+ struct init_exec *iexec)
+ {
+@@ -2572,48 +2694,37 @@ init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+ * each GPIO according to various values listed in each entry
+ */
+
+- const uint32_t nv50_gpio_reg[4] = { 0xe104, 0xe108, 0xe280, 0xe284 };
++ struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
+ const uint32_t nv50_gpio_ctl[2] = { 0xe100, 0xe28c };
+- const uint8_t *gpio_table = &bios->data[bios->bdcb.gpio_table_ptr];
+- const uint8_t *gpio_entry;
+ int i;
+
+- if (!iexec->execute)
+- return 1;
+-
+- if (bios->bdcb.version != 0x40) {
+- NV_ERROR(bios->dev, "DCB table not version 4.0\n");
+- return 0;
+- }
+-
+- if (!bios->bdcb.gpio_table_ptr) {
+- NV_WARN(bios->dev, "Invalid pointer to INIT_8E table\n");
+- return 0;
++ if (dev_priv->card_type != NV_50) {
++ NV_ERROR(bios->dev, "INIT_GPIO on unsupported chipset\n");
++ return -ENODEV;
+ }
+
+- gpio_entry = gpio_table + gpio_table[1];
+- for (i = 0; i < gpio_table[2]; i++, gpio_entry += gpio_table[3]) {
+- uint32_t entry = ROM32(gpio_entry[0]), r, s, v;
+- int line = (entry & 0x0000001f);
++ if (!iexec->execute)
++ return 1;
+
+- BIOSLOG(bios, "0x%04X: Entry: 0x%08X\n", offset, entry);
++ for (i = 0; i < bios->dcb.gpio.entries; i++) {
++ struct dcb_gpio_entry *gpio = &bios->dcb.gpio.entry[i];
++ uint32_t r, s, v;
+
+- if ((entry & 0x0000ff00) == 0x0000ff00)
+- continue;
++ BIOSLOG(bios, "0x%04X: Entry: 0x%08X\n", offset, gpio->entry);
+
+- r = nv50_gpio_reg[line >> 3];
+- s = (line & 0x07) << 2;
+- v = bios_rd32(bios, r) & ~(0x00000003 << s);
+- if (entry & 0x01000000)
+- v |= (((entry & 0x60000000) >> 29) ^ 2) << s;
+- else
+- v |= (((entry & 0x18000000) >> 27) ^ 2) << s;
+- bios_wr32(bios, r, v);
++ 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);
+
+- r = nv50_gpio_ctl[line >> 4];
+- s = (line & 0x0f);
++ /* The NVIDIA binary driver doesn't appear to actually do
++ * any of this, my VBIOS does however.
++ */
++ /* Not a clue, needs de-magicing */
++ r = nv50_gpio_ctl[gpio->line >> 4];
++ s = (gpio->line & 0x0f);
+ v = bios_rd32(bios, r) & ~(0x00010001 << s);
+- switch ((entry & 0x06000000) >> 25) {
++ switch ((gpio->entry & 0x06000000) >> 25) {
+ case 1:
+ v |= (0x00000001 << s);
+ break;
+@@ -2947,6 +3058,9 @@ static struct init_tbl_entry itbl_entry[] = {
+ { "INIT_COPY" , 0x37, init_copy },
+ { "INIT_NOT" , 0x38, init_not },
+ { "INIT_IO_FLAG_CONDITION" , 0x39, init_io_flag_condition },
++ { "INIT_DP_CONDITION" , 0x3A, init_dp_condition },
++ { "INIT_OP_3B" , 0x3B, init_op_3b },
++ { "INIT_OP_3C" , 0x3C, init_op_3c },
+ { "INIT_INDEX_ADDRESS_LATCHED" , 0x49, init_idx_addr_latched },
+ { "INIT_IO_RESTRICT_PLL2" , 0x4A, init_io_restrict_pll2 },
+ { "INIT_PLL2" , 0x4B, init_pll2 },
+@@ -3123,7 +3237,7 @@ run_digital_op_script(struct drm_device *dev, uint16_t scriptptr,
+ struct dcb_entry *dcbent, int head, bool dl)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nvbios *bios = &dev_priv->VBIOS;
++ struct nvbios *bios = &dev_priv->vbios;
+ struct init_exec iexec = {true, false};
+
+ NV_TRACE(dev, "0x%04X: Parsing digital output script table\n",
+@@ -3140,7 +3254,7 @@ run_digital_op_script(struct drm_device *dev, uint16_t scriptptr,
+ static int call_lvds_manufacturer_script(struct drm_device *dev, struct dcb_entry *dcbent, int head, enum LVDS_script script)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nvbios *bios = &dev_priv->VBIOS;
++ struct nvbios *bios = &dev_priv->vbios;
+ uint8_t sub = bios->data[bios->fp.xlated_entry + script] + (bios->fp.link_c_increment && dcbent->or & OUTPUT_C ? 1 : 0);
+ uint16_t scriptofs = ROM16(bios->data[bios->init_script_tbls_ptr + sub * 2]);
+
+@@ -3194,10 +3308,9 @@ static int run_lvds_table(struct drm_device *dev, struct dcb_entry *dcbent, int
+ * of a list of pxclks and script pointers.
+ */
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nvbios *bios = &dev_priv->VBIOS;
++ struct nvbios *bios = &dev_priv->vbios;
+ unsigned int outputset = (dcbent->or == 4) ? 1 : 0;
+ uint16_t scriptptr = 0, clktable;
+- uint8_t clktableptr = 0;
+
+ /*
+ * For now we assume version 3.0 table - g80 support will need some
+@@ -3216,26 +3329,29 @@ static int run_lvds_table(struct drm_device *dev, struct dcb_entry *dcbent, int
+ scriptptr = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 11 + outputset * 2]);
+ break;
+ case LVDS_RESET:
++ clktable = bios->fp.lvdsmanufacturerpointer + 15;
++ if (dcbent->or == 4)
++ clktable += 8;
++
+ if (dcbent->lvdsconf.use_straps_for_mode) {
+ if (bios->fp.dual_link)
+- clktableptr += 2;
+- if (bios->fp.BITbit1)
+- clktableptr++;
++ clktable += 4;
++ if (bios->fp.if_is_24bit)
++ clktable += 2;
+ } else {
+ /* using EDID */
+- uint8_t fallback = bios->data[bios->fp.lvdsmanufacturerpointer + 4];
+- int fallbackcmpval = (dcbent->or == 4) ? 4 : 1;
++ int cmpval_24bit = (dcbent->or == 4) ? 4 : 1;
+
+ if (bios->fp.dual_link) {
+- clktableptr += 2;
+- fallbackcmpval *= 2;
++ clktable += 4;
++ cmpval_24bit <<= 1;
+ }
+- if (fallbackcmpval & fallback)
+- clktableptr++;
++
++ if (bios->fp.strapless_is_24bit & cmpval_24bit)
++ clktable += 2;
+ }
+
+- /* adding outputset * 8 may not be correct */
+- clktable = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 15 + clktableptr * 2 + outputset * 8]);
++ clktable = ROM16(bios->data[clktable]);
+ if (!clktable) {
+ NV_ERROR(dev, "Pixel clock comparison table not found\n");
+ return -ENOENT;
+@@ -3261,7 +3377,7 @@ int call_lvds_script(struct drm_device *dev, struct dcb_entry *dcbent, int head,
+ */
+
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nvbios *bios = &dev_priv->VBIOS;
++ struct nvbios *bios = &dev_priv->vbios;
+ uint8_t lvds_ver = bios->data[bios->fp.lvdsmanufacturerpointer];
+ uint32_t sel_clk_binding, sel_clk;
+ int ret;
+@@ -3395,7 +3511,7 @@ static int parse_fp_mode_table(struct drm_device *dev, struct nvbios *bios)
+ #ifndef __powerpc__
+ NV_ERROR(dev, "Pointer to flat panel table invalid\n");
+ #endif
+- bios->pub.digital_min_front_porch = 0x4b;
++ bios->digital_min_front_porch = 0x4b;
+ return 0;
+ }
+
+@@ -3428,7 +3544,7 @@ static int parse_fp_mode_table(struct drm_device *dev, struct nvbios *bios)
+ * fptable[4] is the minimum
+ * RAMDAC_FP_HCRTC -> RAMDAC_FP_HSYNC_START gap
+ */
+- bios->pub.digital_min_front_porch = fptable[4];
++ bios->digital_min_front_porch = fptable[4];
+ ofs = -7;
+ break;
+ default:
+@@ -3467,7 +3583,7 @@ static int parse_fp_mode_table(struct drm_device *dev, struct nvbios *bios)
+
+ /* nv4x cards need both a strap value and fpindex of 0xf to use DDC */
+ if (lth.lvds_ver > 0x10)
+- bios->pub.fp_no_ddc = fpstrapping != 0xf || fpindex != 0xf;
++ bios->fp_no_ddc = fpstrapping != 0xf || fpindex != 0xf;
+
+ /*
+ * If either the strap or xlated fpindex value are 0xf there is no
+@@ -3491,7 +3607,7 @@ static int parse_fp_mode_table(struct drm_device *dev, struct nvbios *bios)
+ bool nouveau_bios_fp_mode(struct drm_device *dev, struct drm_display_mode *mode)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nvbios *bios = &dev_priv->VBIOS;
++ struct nvbios *bios = &dev_priv->vbios;
+ uint8_t *mode_entry = &bios->data[bios->fp.mode_ptr];
+
+ if (!mode) /* just checking whether we can produce a mode */
+@@ -3562,11 +3678,11 @@ int nouveau_bios_parse_lvds_table(struct drm_device *dev, int pxclk, bool *dl, b
+ * until later, when this function should be called with non-zero pxclk
+ */
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nvbios *bios = &dev_priv->VBIOS;
++ struct nvbios *bios = &dev_priv->vbios;
+ int fpstrapping = get_fp_strap(dev, bios), lvdsmanufacturerindex = 0;
+ struct lvdstableheader lth;
+ uint16_t lvdsofs;
+- int ret, chip_version = bios->pub.chip_version;
++ int ret, chip_version = bios->chip_version;
+
+ ret = parse_lvds_manufacturer_table_header(dev, bios, &lth);
+ if (ret)
+@@ -3637,37 +3753,40 @@ int nouveau_bios_parse_lvds_table(struct drm_device *dev, int pxclk, bool *dl, b
+ *if_is_24bit = bios->data[lvdsofs] & 16;
+ break;
+ case 0x30:
+- /*
+- * My money would be on there being a 24 bit interface bit in
+- * this table, but I have no example of a laptop bios with a
+- * 24 bit panel to confirm that. Hence we shout loudly if any
+- * bit other than bit 0 is set (I've not even seen bit 1)
+- */
+- if (bios->data[lvdsofs] > 1)
+- NV_ERROR(dev,
+- "You have a very unusual laptop display; please report it\n");
++ case 0x40:
+ /*
+ * No sign of the "power off for reset" or "reset for panel
+ * on" bits, but it's safer to assume we should
+ */
+ bios->fp.power_off_for_reset = true;
+ bios->fp.reset_after_pclk_change = true;
++
+ /*
+ * It's ok lvdsofs is wrong for nv4x edid case; dual_link is
+- * over-written, and BITbit1 isn't used
++ * over-written, and if_is_24bit isn't used
+ */
+ bios->fp.dual_link = bios->data[lvdsofs] & 1;
+- bios->fp.BITbit1 = bios->data[lvdsofs] & 2;
+- bios->fp.duallink_transition_clk = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 5]) * 10;
+- break;
+- case 0x40:
+- bios->fp.dual_link = bios->data[lvdsofs] & 1;
+ bios->fp.if_is_24bit = bios->data[lvdsofs] & 2;
+ bios->fp.strapless_is_24bit = bios->data[bios->fp.lvdsmanufacturerpointer + 4];
+ bios->fp.duallink_transition_clk = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 5]) * 10;
+ break;
+ }
+
++ /* Dell Latitude D620 reports a too-high value for the dual-link
++ * transition freq, causing us to program the panel incorrectly.
++ *
++ * It doesn't appear the VBIOS actually uses its transition freq
++ * (90000kHz), instead it uses the "Number of LVDS channels" field
++ * out of the panel ID structure (http://www.spwg.org/).
++ *
++ * For the moment, a quirk will do :)
++ */
++ if ((dev->pdev->device == 0x01d7) &&
++ (dev->pdev->subsystem_vendor == 0x1028) &&
++ (dev->pdev->subsystem_device == 0x01c2)) {
++ bios->fp.duallink_transition_clk = 80000;
++ }
++
+ /* set dual_link flag for EDID case */
+ if (pxclk && (chip_version < 0x25 || chip_version > 0x28))
+ bios->fp.dual_link = (pxclk >= bios->fp.duallink_transition_clk);
+@@ -3682,7 +3801,7 @@ bios_output_config_match(struct drm_device *dev, struct dcb_entry *dcbent,
+ uint16_t record, int record_len, int record_nr)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nvbios *bios = &dev_priv->VBIOS;
++ struct nvbios *bios = &dev_priv->vbios;
+ uint32_t entry;
+ uint16_t table;
+ int i, v;
+@@ -3716,7 +3835,7 @@ nouveau_bios_dp_table(struct drm_device *dev, struct dcb_entry *dcbent,
+ int *length)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nvbios *bios = &dev_priv->VBIOS;
++ struct nvbios *bios = &dev_priv->vbios;
+ uint8_t *table;
+
+ if (!bios->display.dp_table_ptr) {
+@@ -3725,7 +3844,7 @@ nouveau_bios_dp_table(struct drm_device *dev, struct dcb_entry *dcbent,
+ }
+ table = &bios->data[bios->display.dp_table_ptr];
+
+- if (table[0] != 0x21) {
++ if (table[0] != 0x20 && table[0] != 0x21) {
+ NV_ERROR(dev, "DisplayPort table version 0x%02x unknown\n",
+ table[0]);
+ return NULL;
+@@ -3765,7 +3884,7 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent,
+ */
+
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nvbios *bios = &dev_priv->VBIOS;
++ struct nvbios *bios = &dev_priv->vbios;
+ uint8_t *table = &bios->data[bios->display.script_table_ptr];
+ uint8_t *otable = NULL;
+ uint16_t script;
+@@ -3918,8 +4037,8 @@ int run_tmds_table(struct drm_device *dev, struct dcb_entry *dcbent, int head, i
+ */
+
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nvbios *bios = &dev_priv->VBIOS;
+- int cv = bios->pub.chip_version;
++ struct nvbios *bios = &dev_priv->vbios;
++ int cv = bios->chip_version;
+ uint16_t clktable = 0, scriptptr;
+ uint32_t sel_clk_binding, sel_clk;
+
+@@ -3978,8 +4097,8 @@ int get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims
+ */
+
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nvbios *bios = &dev_priv->VBIOS;
+- int cv = bios->pub.chip_version, pllindex = 0;
++ struct nvbios *bios = &dev_priv->vbios;
++ int cv = bios->chip_version, pllindex = 0;
+ uint8_t pll_lim_ver = 0, headerlen = 0, recordlen = 0, entries = 0;
+ uint32_t crystal_strap_mask, crystal_straps;
+
+@@ -4293,31 +4412,32 @@ int get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims
+ break;
+ }
+
+-#if 0 /* for easy debugging */
+- ErrorF("pll.vco1.minfreq: %d\n", pll_lim->vco1.minfreq);
+- ErrorF("pll.vco1.maxfreq: %d\n", pll_lim->vco1.maxfreq);
+- ErrorF("pll.vco2.minfreq: %d\n", pll_lim->vco2.minfreq);
+- ErrorF("pll.vco2.maxfreq: %d\n", pll_lim->vco2.maxfreq);
+-
+- ErrorF("pll.vco1.min_inputfreq: %d\n", pll_lim->vco1.min_inputfreq);
+- ErrorF("pll.vco1.max_inputfreq: %d\n", pll_lim->vco1.max_inputfreq);
+- ErrorF("pll.vco2.min_inputfreq: %d\n", pll_lim->vco2.min_inputfreq);
+- ErrorF("pll.vco2.max_inputfreq: %d\n", pll_lim->vco2.max_inputfreq);
+-
+- ErrorF("pll.vco1.min_n: %d\n", pll_lim->vco1.min_n);
+- ErrorF("pll.vco1.max_n: %d\n", pll_lim->vco1.max_n);
+- ErrorF("pll.vco1.min_m: %d\n", pll_lim->vco1.min_m);
+- ErrorF("pll.vco1.max_m: %d\n", pll_lim->vco1.max_m);
+- ErrorF("pll.vco2.min_n: %d\n", pll_lim->vco2.min_n);
+- ErrorF("pll.vco2.max_n: %d\n", pll_lim->vco2.max_n);
+- ErrorF("pll.vco2.min_m: %d\n", pll_lim->vco2.min_m);
+- ErrorF("pll.vco2.max_m: %d\n", pll_lim->vco2.max_m);
+-
+- ErrorF("pll.max_log2p: %d\n", pll_lim->max_log2p);
+- ErrorF("pll.log2p_bias: %d\n", pll_lim->log2p_bias);
+-
+- ErrorF("pll.refclk: %d\n", pll_lim->refclk);
+-#endif
++ NV_DEBUG(dev, "pll.vco1.minfreq: %d\n", pll_lim->vco1.minfreq);
++ NV_DEBUG(dev, "pll.vco1.maxfreq: %d\n", pll_lim->vco1.maxfreq);
++ NV_DEBUG(dev, "pll.vco1.min_inputfreq: %d\n", pll_lim->vco1.min_inputfreq);
++ NV_DEBUG(dev, "pll.vco1.max_inputfreq: %d\n", pll_lim->vco1.max_inputfreq);
++ NV_DEBUG(dev, "pll.vco1.min_n: %d\n", pll_lim->vco1.min_n);
++ NV_DEBUG(dev, "pll.vco1.max_n: %d\n", pll_lim->vco1.max_n);
++ NV_DEBUG(dev, "pll.vco1.min_m: %d\n", pll_lim->vco1.min_m);
++ NV_DEBUG(dev, "pll.vco1.max_m: %d\n", pll_lim->vco1.max_m);
++ if (pll_lim->vco2.maxfreq) {
++ NV_DEBUG(dev, "pll.vco2.minfreq: %d\n", pll_lim->vco2.minfreq);
++ NV_DEBUG(dev, "pll.vco2.maxfreq: %d\n", pll_lim->vco2.maxfreq);
++ NV_DEBUG(dev, "pll.vco2.min_inputfreq: %d\n", pll_lim->vco2.min_inputfreq);
++ NV_DEBUG(dev, "pll.vco2.max_inputfreq: %d\n", pll_lim->vco2.max_inputfreq);
++ NV_DEBUG(dev, "pll.vco2.min_n: %d\n", pll_lim->vco2.min_n);
++ NV_DEBUG(dev, "pll.vco2.max_n: %d\n", pll_lim->vco2.max_n);
++ NV_DEBUG(dev, "pll.vco2.min_m: %d\n", pll_lim->vco2.min_m);
++ NV_DEBUG(dev, "pll.vco2.max_m: %d\n", pll_lim->vco2.max_m);
++ }
++ if (!pll_lim->max_p) {
++ NV_DEBUG(dev, "pll.max_log2p: %d\n", pll_lim->max_log2p);
++ NV_DEBUG(dev, "pll.log2p_bias: %d\n", pll_lim->log2p_bias);
++ } else {
++ NV_DEBUG(dev, "pll.min_p: %d\n", pll_lim->min_p);
++ NV_DEBUG(dev, "pll.max_p: %d\n", pll_lim->max_p);
++ }
++ NV_DEBUG(dev, "pll.refclk: %d\n", pll_lim->refclk);
+
+ return 0;
+ }
+@@ -4332,7 +4452,7 @@ static void parse_bios_version(struct drm_device *dev, struct nvbios *bios, uint
+ */
+
+ bios->major_version = bios->data[offset + 3];
+- bios->pub.chip_version = bios->data[offset + 2];
++ bios->chip_version = bios->data[offset + 2];
+ NV_TRACE(dev, "Bios version %02x.%02x.%02x.%02x\n",
+ bios->data[offset + 3], bios->data[offset + 2],
+ bios->data[offset + 1], bios->data[offset]);
+@@ -4402,7 +4522,7 @@ static int parse_bit_A_tbl_entry(struct drm_device *dev, struct nvbios *bios, st
+ }
+
+ /* First entry is normal dac, 2nd tv-out perhaps? */
+- bios->pub.dactestval = ROM32(bios->data[load_table_ptr + headerlen]) & 0x3ff;
++ bios->dactestval = ROM32(bios->data[load_table_ptr + headerlen]) & 0x3ff;
+
+ return 0;
+ }
+@@ -4526,8 +4646,8 @@ static int parse_bit_i_tbl_entry(struct drm_device *dev, struct nvbios *bios, st
+ return -ENOSYS;
+ }
+
+- bios->pub.dactestval = ROM32(bios->data[daccmpoffset + dacheaderlen]);
+- bios->pub.tvdactestval = ROM32(bios->data[daccmpoffset + dacheaderlen + 4]);
++ bios->dactestval = ROM32(bios->data[daccmpoffset + dacheaderlen]);
++ bios->tvdactestval = ROM32(bios->data[daccmpoffset + dacheaderlen + 4]);
+
+ return 0;
+ }
+@@ -4796,11 +4916,11 @@ static int parse_bmp_structure(struct drm_device *dev, struct nvbios *bios, unsi
+ uint16_t legacy_scripts_offset, legacy_i2c_offset;
+
+ /* load needed defaults in case we can't parse this info */
+- bios->bdcb.dcb.i2c[0].write = NV_CIO_CRE_DDC_WR__INDEX;
+- bios->bdcb.dcb.i2c[0].read = NV_CIO_CRE_DDC_STATUS__INDEX;
+- bios->bdcb.dcb.i2c[1].write = NV_CIO_CRE_DDC0_WR__INDEX;
+- bios->bdcb.dcb.i2c[1].read = NV_CIO_CRE_DDC0_STATUS__INDEX;
+- bios->pub.digital_min_front_porch = 0x4b;
++ bios->dcb.i2c[0].write = NV_CIO_CRE_DDC_WR__INDEX;
++ bios->dcb.i2c[0].read = NV_CIO_CRE_DDC_STATUS__INDEX;
++ bios->dcb.i2c[1].write = NV_CIO_CRE_DDC0_WR__INDEX;
++ bios->dcb.i2c[1].read = NV_CIO_CRE_DDC0_STATUS__INDEX;
++ bios->digital_min_front_porch = 0x4b;
+ bios->fmaxvco = 256000;
+ bios->fminvco = 128000;
+ bios->fp.duallink_transition_clk = 90000;
+@@ -4907,10 +5027,10 @@ 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];
+- bios->bdcb.dcb.i2c[0].write = bios->data[legacy_i2c_offset + 4];
+- bios->bdcb.dcb.i2c[0].read = bios->data[legacy_i2c_offset + 5];
+- bios->bdcb.dcb.i2c[1].write = bios->data[legacy_i2c_offset + 6];
+- bios->bdcb.dcb.i2c[1].read = bios->data[legacy_i2c_offset + 7];
++ bios->dcb.i2c[0].write = bios->data[legacy_i2c_offset + 4];
++ bios->dcb.i2c[0].read = bios->data[legacy_i2c_offset + 5];
++ bios->dcb.i2c[1].write = bios->data[legacy_i2c_offset + 6];
++ bios->dcb.i2c[1].read = bios->data[legacy_i2c_offset + 7];
+
+ if (bmplength > 74) {
+ bios->fmaxvco = ROM32(bmp[67]);
+@@ -4984,7 +5104,8 @@ read_dcb_i2c_entry(struct drm_device *dev, int dcb_version, uint8_t *i2ctable, i
+ else
+ NV_WARN(dev,
+ "DCB I2C table has more entries than indexable "
+- "(%d entries, max index 15)\n", i2ctable[2]);
++ "(%d entries, max %d)\n", i2ctable[2],
++ DCB_MAX_NUM_I2C_ENTRIES);
+ entry_len = i2ctable[3];
+ /* [4] is i2c_default_indices, read in parse_dcb_table() */
+ }
+@@ -5000,8 +5121,8 @@ read_dcb_i2c_entry(struct drm_device *dev, int dcb_version, uint8_t *i2ctable, i
+
+ if (index == 0xf)
+ return 0;
+- if (index > i2c_entries) {
+- NV_ERROR(dev, "DCB I2C index too big (%d > %d)\n",
++ if (index >= i2c_entries) {
++ NV_ERROR(dev, "DCB I2C index too big (%d >= %d)\n",
+ index, i2ctable[2]);
+ return -ENOENT;
+ }
+@@ -5023,8 +5144,12 @@ read_dcb_i2c_entry(struct drm_device *dev, int dcb_version, uint8_t *i2ctable, i
+ rdofs = wrofs = 0;
+ }
+
+- if (dcb_i2c_ver >= 0x40 && port_type != 5 && port_type != 6)
+- NV_WARN(dev, "DCB I2C table has port type %d\n", port_type);
++ if (dcb_i2c_ver >= 0x40) {
++ if (port_type != 5 && port_type != 6)
++ NV_WARN(dev, "DCB I2C table has port type %d\n", port_type);
++
++ i2c->entry = ROM32(i2ctable[headerlen + recordoffset + entry_len * index]);
++ }
+
+ i2c->port_type = port_type;
+ i2c->read = i2ctable[headerlen + recordoffset + rdofs + entry_len * index];
+@@ -5036,7 +5161,7 @@ read_dcb_i2c_entry(struct drm_device *dev, int dcb_version, uint8_t *i2ctable, i
+ static struct dcb_gpio_entry *
+ new_gpio_entry(struct nvbios *bios)
+ {
+- struct parsed_dcb_gpio *gpio = &bios->bdcb.gpio;
++ struct dcb_gpio_table *gpio = &bios->dcb.gpio;
+
+ return &gpio->entry[gpio->entries++];
+ }
+@@ -5045,14 +5170,14 @@ struct dcb_gpio_entry *
+ nouveau_bios_gpio_entry(struct drm_device *dev, enum dcb_gpio_tag tag)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nvbios *bios = &dev_priv->VBIOS;
++ struct nvbios *bios = &dev_priv->vbios;
+ int i;
+
+- for (i = 0; i < bios->bdcb.gpio.entries; i++) {
+- if (bios->bdcb.gpio.entry[i].tag != tag)
++ for (i = 0; i < bios->dcb.gpio.entries; i++) {
++ if (bios->dcb.gpio.entry[i].tag != tag)
+ continue;
+
+- return &bios->bdcb.gpio.entry[i];
++ return &bios->dcb.gpio.entry[i];
+ }
+
+ return NULL;
+@@ -5075,32 +5200,32 @@ parse_dcb30_gpio_entry(struct nvbios *bios, uint16_t offset)
+ gpio->tag = tag;
+ gpio->line = line;
+ gpio->invert = flags != 4;
++ gpio->entry = ent;
+ }
+
+ static void
+ parse_dcb40_gpio_entry(struct nvbios *bios, uint16_t offset)
+ {
++ uint32_t entry = ROM32(bios->data[offset]);
+ struct dcb_gpio_entry *gpio;
+- uint32_t ent = ROM32(bios->data[offset]);
+- uint8_t line = ent & 0x1f,
+- tag = ent >> 8 & 0xff;
+
+- if (tag == 0xff)
++ if ((entry & 0x0000ff00) == 0x0000ff00)
+ return;
+
+ gpio = new_gpio_entry(bios);
+-
+- /* Currently unused, we may need more fields parsed at some
+- * point. */
+- gpio->tag = tag;
+- gpio->line = line;
++ gpio->tag = (entry & 0x0000ff00) >> 8;
++ gpio->line = (entry & 0x0000001f) >> 0;
++ gpio->state_default = (entry & 0x01000000) >> 24;
++ gpio->state[0] = (entry & 0x18000000) >> 27;
++ gpio->state[1] = (entry & 0x60000000) >> 29;
++ gpio->entry = entry;
+ }
+
+ static void
+ parse_dcb_gpio_table(struct nvbios *bios)
+ {
+ struct drm_device *dev = bios->dev;
+- uint16_t gpio_table_ptr = bios->bdcb.gpio_table_ptr;
++ uint16_t gpio_table_ptr = bios->dcb.gpio_table_ptr;
+ uint8_t *gpio_table = &bios->data[gpio_table_ptr];
+ int header_len = gpio_table[1],
+ entries = gpio_table[2],
+@@ -5108,7 +5233,7 @@ parse_dcb_gpio_table(struct nvbios *bios)
+ void (*parse_entry)(struct nvbios *, uint16_t) = NULL;
+ int i;
+
+- if (bios->bdcb.version >= 0x40) {
++ if (bios->dcb.version >= 0x40) {
+ if (gpio_table_ptr && entry_len != 4) {
+ NV_WARN(dev, "Invalid DCB GPIO table entry length.\n");
+ return;
+@@ -5116,7 +5241,7 @@ parse_dcb_gpio_table(struct nvbios *bios)
+
+ parse_entry = parse_dcb40_gpio_entry;
+
+- } else if (bios->bdcb.version >= 0x30) {
++ } else if (bios->dcb.version >= 0x30) {
+ if (gpio_table_ptr && entry_len != 2) {
+ NV_WARN(dev, "Invalid DCB GPIO table entry length.\n");
+ return;
+@@ -5124,7 +5249,7 @@ parse_dcb_gpio_table(struct nvbios *bios)
+
+ parse_entry = parse_dcb30_gpio_entry;
+
+- } else if (bios->bdcb.version >= 0x22) {
++ } else if (bios->dcb.version >= 0x22) {
+ /*
+ * DCBs older than v3.0 don't really have a GPIO
+ * table, instead they keep some GPIO info at fixed
+@@ -5158,30 +5283,82 @@ struct dcb_connector_table_entry *
+ nouveau_bios_connector_entry(struct drm_device *dev, int index)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nvbios *bios = &dev_priv->VBIOS;
++ struct nvbios *bios = &dev_priv->vbios;
+ struct dcb_connector_table_entry *cte;
+
+- if (index >= bios->bdcb.connector.entries)
++ if (index >= bios->dcb.connector.entries)
+ return NULL;
+
+- cte = &bios->bdcb.connector.entry[index];
++ cte = &bios->dcb.connector.entry[index];
+ if (cte->type == 0xff)
+ return NULL;
+
+ return cte;
+ }
+
++static enum dcb_connector_type
++divine_connector_type(struct nvbios *bios, int index)
++{
++ struct dcb_table *dcb = &bios->dcb;
++ unsigned encoders = 0, type = DCB_CONNECTOR_NONE;
++ int i;
++
++ for (i = 0; i < dcb->entries; i++) {
++ if (dcb->entry[i].connector == index)
++ encoders |= (1 << dcb->entry[i].type);
++ }
++
++ if (encoders & (1 << OUTPUT_DP)) {
++ if (encoders & (1 << OUTPUT_TMDS))
++ type = DCB_CONNECTOR_DP;
++ else
++ type = DCB_CONNECTOR_eDP;
++ } else
++ if (encoders & (1 << OUTPUT_TMDS)) {
++ if (encoders & (1 << OUTPUT_ANALOG))
++ type = DCB_CONNECTOR_DVI_I;
++ else
++ type = DCB_CONNECTOR_DVI_D;
++ } else
++ if (encoders & (1 << OUTPUT_ANALOG)) {
++ type = DCB_CONNECTOR_VGA;
++ } else
++ if (encoders & (1 << OUTPUT_LVDS)) {
++ type = DCB_CONNECTOR_LVDS;
++ } else
++ if (encoders & (1 << OUTPUT_TV)) {
++ type = DCB_CONNECTOR_TV_0;
++ }
++
++ return type;
++}
++
++static void
++apply_dcb_connector_quirks(struct nvbios *bios, int idx)
++{
++ struct dcb_connector_table_entry *cte = &bios->dcb.connector.entry[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 (cte->type == DCB_CONNECTOR_HDMI_1)
++ cte->type = DCB_CONNECTOR_DVI_I;
++ }
++}
++
+ static void
+ parse_dcb_connector_table(struct nvbios *bios)
+ {
+ struct drm_device *dev = bios->dev;
+- struct dcb_connector_table *ct = &bios->bdcb.connector;
++ struct dcb_connector_table *ct = &bios->dcb.connector;
+ struct dcb_connector_table_entry *cte;
+- uint8_t *conntab = &bios->data[bios->bdcb.connector_table_ptr];
++ uint8_t *conntab = &bios->data[bios->dcb.connector_table_ptr];
+ uint8_t *entry;
+ int i;
+
+- if (!bios->bdcb.connector_table_ptr) {
++ if (!bios->dcb.connector_table_ptr) {
+ NV_DEBUG_KMS(dev, "No DCB connector table present\n");
+ return;
+ }
+@@ -5199,12 +5376,14 @@ parse_dcb_connector_table(struct nvbios *bios)
+ entry = conntab + conntab[1];
+ cte = &ct->entry[0];
+ for (i = 0; i < conntab[2]; i++, entry += conntab[3], cte++) {
++ cte->index = i;
+ if (conntab[3] == 2)
+ cte->entry = ROM16(entry[0]);
+ else
+ cte->entry = ROM32(entry[0]);
++
+ cte->type = (cte->entry & 0x000000ff) >> 0;
+- cte->index = (cte->entry & 0x00000f00) >> 8;
++ cte->index2 = (cte->entry & 0x00000f00) >> 8;
+ switch (cte->entry & 0x00033000) {
+ case 0x00001000:
+ cte->gpio_tag = 0x07;
+@@ -5226,12 +5405,43 @@ parse_dcb_connector_table(struct nvbios *bios)
+ if (cte->type == 0xff)
+ continue;
+
++ apply_dcb_connector_quirks(bios, i);
++
+ NV_INFO(dev, " %d: 0x%08x: type 0x%02x idx %d tag 0x%02x\n",
+ i, cte->entry, cte->type, cte->index, cte->gpio_tag);
++
++ /* check for known types, fallback to guessing the type
++ * from attached encoders if we hit an unknown.
++ */
++ switch (cte->type) {
++ case DCB_CONNECTOR_VGA:
++ case DCB_CONNECTOR_TV_0:
++ case DCB_CONNECTOR_TV_1:
++ case DCB_CONNECTOR_TV_3:
++ case DCB_CONNECTOR_DVI_I:
++ case DCB_CONNECTOR_DVI_D:
++ case DCB_CONNECTOR_LVDS:
++ case DCB_CONNECTOR_DP:
++ case DCB_CONNECTOR_eDP:
++ case DCB_CONNECTOR_HDMI_0:
++ case DCB_CONNECTOR_HDMI_1:
++ break;
++ default:
++ cte->type = divine_connector_type(bios, cte->index);
++ NV_WARN(dev, "unknown type, using 0x%02x\n", cte->type);
++ break;
++ }
++
++ if (nouveau_override_conntype) {
++ int type = divine_connector_type(bios, cte->index);
++ if (type != cte->type)
++ NV_WARN(dev, " -> type 0x%02x\n", cte->type);
++ }
++
+ }
+ }
+
+-static struct dcb_entry *new_dcb_entry(struct parsed_dcb *dcb)
++static struct dcb_entry *new_dcb_entry(struct dcb_table *dcb)
+ {
+ struct dcb_entry *entry = &dcb->entry[dcb->entries];
+
+@@ -5241,7 +5451,7 @@ static struct dcb_entry *new_dcb_entry(struct parsed_dcb *dcb)
+ return entry;
+ }
+
+-static void fabricate_vga_output(struct parsed_dcb *dcb, int i2c, int heads)
++static void fabricate_vga_output(struct dcb_table *dcb, int i2c, int heads)
+ {
+ struct dcb_entry *entry = new_dcb_entry(dcb);
+
+@@ -5252,7 +5462,7 @@ static void fabricate_vga_output(struct parsed_dcb *dcb, int i2c, int heads)
+ /* "or" mostly unused in early gen crt modesetting, 0 is fine */
+ }
+
+-static void fabricate_dvi_i_output(struct parsed_dcb *dcb, bool twoHeads)
++static void fabricate_dvi_i_output(struct dcb_table *dcb, bool twoHeads)
+ {
+ struct dcb_entry *entry = new_dcb_entry(dcb);
+
+@@ -5279,7 +5489,7 @@ static void fabricate_dvi_i_output(struct parsed_dcb *dcb, bool twoHeads)
+ #endif
+ }
+
+-static void fabricate_tv_output(struct parsed_dcb *dcb, bool twoHeads)
++static void fabricate_tv_output(struct dcb_table *dcb, bool twoHeads)
+ {
+ struct dcb_entry *entry = new_dcb_entry(dcb);
+
+@@ -5290,13 +5500,13 @@ static void fabricate_tv_output(struct parsed_dcb *dcb, bool twoHeads)
+ }
+
+ static bool
+-parse_dcb20_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb,
++parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb,
+ uint32_t conn, uint32_t conf, struct dcb_entry *entry)
+ {
+ entry->type = conn & 0xf;
+ entry->i2c_index = (conn >> 4) & 0xf;
+ entry->heads = (conn >> 8) & 0xf;
+- if (bdcb->version >= 0x40)
++ if (dcb->version >= 0x40)
+ entry->connector = (conn >> 12) & 0xf;
+ entry->bus = (conn >> 16) & 0xf;
+ entry->location = (conn >> 20) & 0x3;
+@@ -5314,7 +5524,7 @@ parse_dcb20_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb,
+ * Although the rest of a CRT conf dword is usually
+ * zeros, mac biosen have stuff there so we must mask
+ */
+- entry->crtconf.maxfreq = (bdcb->version < 0x30) ?
++ entry->crtconf.maxfreq = (dcb->version < 0x30) ?
+ (conf & 0xffff) * 10 :
+ (conf & 0xff) * 10000;
+ break;
+@@ -5323,7 +5533,7 @@ parse_dcb20_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb,
+ uint32_t mask;
+ if (conf & 0x1)
+ entry->lvdsconf.use_straps_for_mode = true;
+- if (bdcb->version < 0x22) {
++ if (dcb->version < 0x22) {
+ mask = ~0xd;
+ /*
+ * The laptop in bug 14567 lies and claims to not use
+@@ -5347,7 +5557,7 @@ parse_dcb20_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb,
+ * Until we even try to use these on G8x, it's
+ * useless reporting unknown bits. They all are.
+ */
+- if (bdcb->version >= 0x40)
++ if (dcb->version >= 0x40)
+ break;
+
+ NV_ERROR(dev, "Unknown LVDS configuration bits, "
+@@ -5357,7 +5567,7 @@ parse_dcb20_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb,
+ }
+ case OUTPUT_TV:
+ {
+- if (bdcb->version >= 0x30)
++ if (dcb->version >= 0x30)
+ entry->tvconf.has_component_output = conf & (0x8 << 4);
+ else
+ entry->tvconf.has_component_output = false;
+@@ -5384,8 +5594,10 @@ parse_dcb20_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb,
+ break;
+ case 0xe:
+ /* weird g80 mobile type that "nv" treats as a terminator */
+- bdcb->dcb.entries--;
++ dcb->entries--;
+ return false;
++ default:
++ break;
+ }
+
+ /* unsure what DCB version introduces this, 3.0? */
+@@ -5396,7 +5608,7 @@ parse_dcb20_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb,
+ }
+
+ static bool
+-parse_dcb15_entry(struct drm_device *dev, struct parsed_dcb *dcb,
++parse_dcb15_entry(struct drm_device *dev, struct dcb_table *dcb,
+ uint32_t conn, uint32_t conf, struct dcb_entry *entry)
+ {
+ switch (conn & 0x0000000f) {
+@@ -5462,27 +5674,27 @@ parse_dcb15_entry(struct drm_device *dev, struct parsed_dcb *dcb,
+ return true;
+ }
+
+-static bool parse_dcb_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb,
++static bool parse_dcb_entry(struct drm_device *dev, struct dcb_table *dcb,
+ uint32_t conn, uint32_t conf)
+ {
+- struct dcb_entry *entry = new_dcb_entry(&bdcb->dcb);
++ struct dcb_entry *entry = new_dcb_entry(dcb);
+ bool ret;
+
+- if (bdcb->version >= 0x20)
+- ret = parse_dcb20_entry(dev, bdcb, conn, conf, entry);
++ if (dcb->version >= 0x20)
++ ret = parse_dcb20_entry(dev, dcb, conn, conf, entry);
+ else
+- ret = parse_dcb15_entry(dev, &bdcb->dcb, conn, conf, entry);
++ ret = parse_dcb15_entry(dev, dcb, conn, conf, entry);
+ if (!ret)
+ return ret;
+
+- read_dcb_i2c_entry(dev, bdcb->version, bdcb->i2c_table,
+- entry->i2c_index, &bdcb->dcb.i2c[entry->i2c_index]);
++ read_dcb_i2c_entry(dev, dcb->version, dcb->i2c_table,
++ entry->i2c_index, &dcb->i2c[entry->i2c_index]);
+
+ return true;
+ }
+
+ static
+-void merge_like_dcb_entries(struct drm_device *dev, struct parsed_dcb *dcb)
++void merge_like_dcb_entries(struct drm_device *dev, struct dcb_table *dcb)
+ {
+ /*
+ * DCB v2.0 lists each output combination separately.
+@@ -5534,8 +5746,7 @@ static int
+ parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct bios_parsed_dcb *bdcb = &bios->bdcb;
+- struct parsed_dcb *dcb;
++ struct dcb_table *dcb = &bios->dcb;
+ uint16_t dcbptr = 0, i2ctabptr = 0;
+ uint8_t *dcbtable;
+ uint8_t headerlen = 0x4, entries = DCB_MAX_NUM_ENTRIES;
+@@ -5543,9 +5754,6 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads)
+ int recordlength = 8, confofs = 4;
+ int i;
+
+- dcb = bios->pub.dcb = &bdcb->dcb;
+- dcb->entries = 0;
+-
+ /* get the offset from 0x36 */
+ if (dev_priv->card_type > NV_04) {
+ dcbptr = ROM16(bios->data[0x36]);
+@@ -5567,21 +5775,21 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads)
+ dcbtable = &bios->data[dcbptr];
+
+ /* get DCB version */
+- bdcb->version = dcbtable[0];
++ dcb->version = dcbtable[0];
+ NV_TRACE(dev, "Found Display Configuration Block version %d.%d\n",
+- bdcb->version >> 4, bdcb->version & 0xf);
++ dcb->version >> 4, dcb->version & 0xf);
+
+- if (bdcb->version >= 0x20) { /* NV17+ */
++ if (dcb->version >= 0x20) { /* NV17+ */
+ uint32_t sig;
+
+- if (bdcb->version >= 0x30) { /* NV40+ */
++ if (dcb->version >= 0x30) { /* NV40+ */
+ headerlen = dcbtable[1];
+ entries = dcbtable[2];
+ recordlength = dcbtable[3];
+ i2ctabptr = ROM16(dcbtable[4]);
+ sig = ROM32(dcbtable[6]);
+- bdcb->gpio_table_ptr = ROM16(dcbtable[10]);
+- bdcb->connector_table_ptr = ROM16(dcbtable[20]);
++ dcb->gpio_table_ptr = ROM16(dcbtable[10]);
++ dcb->connector_table_ptr = ROM16(dcbtable[20]);
+ } else {
+ i2ctabptr = ROM16(dcbtable[2]);
+ sig = ROM32(dcbtable[4]);
+@@ -5593,7 +5801,7 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads)
+ "signature (%08X)\n", sig);
+ return -EINVAL;
+ }
+- } else if (bdcb->version >= 0x15) { /* some NV11 and NV20 */
++ } else if (dcb->version >= 0x15) { /* some NV11 and NV20 */
+ char sig[8] = { 0 };
+
+ strncpy(sig, (char *)&dcbtable[-7], 7);
+@@ -5641,14 +5849,11 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads)
+ if (!i2ctabptr)
+ NV_WARN(dev, "No pointer to DCB I2C port table\n");
+ else {
+- bdcb->i2c_table = &bios->data[i2ctabptr];
+- if (bdcb->version >= 0x30)
+- bdcb->i2c_default_indices = bdcb->i2c_table[4];
++ dcb->i2c_table = &bios->data[i2ctabptr];
++ if (dcb->version >= 0x30)
++ dcb->i2c_default_indices = dcb->i2c_table[4];
+ }
+
+- parse_dcb_gpio_table(bios);
+- parse_dcb_connector_table(bios);
+-
+ if (entries > DCB_MAX_NUM_ENTRIES)
+ entries = DCB_MAX_NUM_ENTRIES;
+
+@@ -5673,7 +5878,7 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads)
+ NV_TRACEWARN(dev, "Raw DCB entry %d: %08x %08x\n",
+ dcb->entries, connection, config);
+
+- if (!parse_dcb_entry(dev, bdcb, connection, config))
++ if (!parse_dcb_entry(dev, dcb, connection, config))
+ break;
+ }
+
+@@ -5681,18 +5886,22 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads)
+ * apart for v2.1+ not being known for requiring merging, this
+ * guarantees dcbent->index is the index of the entry in the rom image
+ */
+- if (bdcb->version < 0x21)
++ if (dcb->version < 0x21)
+ merge_like_dcb_entries(dev, dcb);
+
+- return dcb->entries ? 0 : -ENXIO;
++ if (!dcb->entries)
++ return -ENXIO;
++
++ parse_dcb_gpio_table(bios);
++ parse_dcb_connector_table(bios);
++ return 0;
+ }
+
+ static void
+ fixup_legacy_connector(struct nvbios *bios)
+ {
+- struct bios_parsed_dcb *bdcb = &bios->bdcb;
+- struct parsed_dcb *dcb = &bdcb->dcb;
+- int high = 0, i;
++ struct dcb_table *dcb = &bios->dcb;
++ int i, i2c, i2c_conn[DCB_MAX_NUM_I2C_ENTRIES] = { };
+
+ /*
+ * DCB 3.0 also has the table in most cases, but there are some cards
+@@ -5700,9 +5909,11 @@ fixup_legacy_connector(struct nvbios *bios)
+ * indices are all 0. We don't need the connector indices on pre-G80
+ * chips (yet?) so limit the use to DCB 4.0 and above.
+ */
+- if (bdcb->version >= 0x40)
++ if (dcb->version >= 0x40)
+ return;
+
++ dcb->connector.entries = 0;
++
+ /*
+ * No known connector info before v3.0, so make it up. the rule here
+ * is: anything on the same i2c bus is considered to be on the same
+@@ -5710,37 +5921,38 @@ fixup_legacy_connector(struct nvbios *bios)
+ * its own unique connector index.
+ */
+ for (i = 0; i < dcb->entries; i++) {
+- if (dcb->entry[i].i2c_index == 0xf)
+- continue;
+-
+ /*
+ * Ignore the I2C index for on-chip TV-out, as there
+ * are cards with bogus values (nv31m in bug 23212),
+ * and it's otherwise useless.
+ */
+ if (dcb->entry[i].type == OUTPUT_TV &&
+- dcb->entry[i].location == DCB_LOC_ON_CHIP) {
++ dcb->entry[i].location == DCB_LOC_ON_CHIP)
+ dcb->entry[i].i2c_index = 0xf;
++ i2c = dcb->entry[i].i2c_index;
++
++ if (i2c_conn[i2c]) {
++ dcb->entry[i].connector = i2c_conn[i2c] - 1;
+ continue;
+ }
+
+- dcb->entry[i].connector = dcb->entry[i].i2c_index;
+- if (dcb->entry[i].connector > high)
+- high = dcb->entry[i].connector;
++ dcb->entry[i].connector = dcb->connector.entries++;
++ if (i2c != 0xf)
++ i2c_conn[i2c] = dcb->connector.entries;
+ }
+
+- for (i = 0; i < dcb->entries; i++) {
+- if (dcb->entry[i].i2c_index != 0xf)
+- continue;
+-
+- dcb->entry[i].connector = ++high;
++ /* Fake the connector table as well as just connector indices */
++ for (i = 0; i < dcb->connector.entries; i++) {
++ dcb->connector.entry[i].index = i;
++ dcb->connector.entry[i].type = divine_connector_type(bios, i);
++ dcb->connector.entry[i].gpio_tag = 0xff;
+ }
+ }
+
+ static void
+ fixup_legacy_i2c(struct nvbios *bios)
+ {
+- struct parsed_dcb *dcb = &bios->bdcb.dcb;
++ struct dcb_table *dcb = &bios->dcb;
+ int i;
+
+ for (i = 0; i < dcb->entries; i++) {
+@@ -5826,7 +6038,7 @@ static int load_nv17_hw_sequencer_ucode(struct drm_device *dev,
+ uint8_t *nouveau_bios_embedded_edid(struct drm_device *dev)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nvbios *bios = &dev_priv->VBIOS;
++ struct nvbios *bios = &dev_priv->vbios;
+ const uint8_t edid_sig[] = {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 };
+ uint16_t offset = 0;
+@@ -5859,7 +6071,7 @@ nouveau_bios_run_init_table(struct drm_device *dev, uint16_t table,
+ struct dcb_entry *dcbent)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nvbios *bios = &dev_priv->VBIOS;
++ struct nvbios *bios = &dev_priv->vbios;
+ struct init_exec iexec = { true, false };
+
+ mutex_lock(&bios->lock);
+@@ -5872,7 +6084,7 @@ nouveau_bios_run_init_table(struct drm_device *dev, uint16_t table,
+ static bool NVInitVBIOS(struct drm_device *dev)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nvbios *bios = &dev_priv->VBIOS;
++ struct nvbios *bios = &dev_priv->vbios;
+
+ memset(bios, 0, sizeof(struct nvbios));
+ mutex_init(&bios->lock);
+@@ -5888,7 +6100,7 @@ static bool NVInitVBIOS(struct drm_device *dev)
+ static int nouveau_parse_vbios_struct(struct drm_device *dev)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nvbios *bios = &dev_priv->VBIOS;
++ struct nvbios *bios = &dev_priv->vbios;
+ const uint8_t bit_signature[] = { 0xff, 0xb8, 'B', 'I', 'T' };
+ const uint8_t bmp_signature[] = { 0xff, 0x7f, 'N', 'V', 0x0 };
+ int offset;
+@@ -5915,7 +6127,7 @@ int
+ nouveau_run_vbios_init(struct drm_device *dev)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nvbios *bios = &dev_priv->VBIOS;
++ struct nvbios *bios = &dev_priv->vbios;
+ int i, ret = 0;
+
+ NVLockVgaCrtcs(dev, false);
+@@ -5946,9 +6158,9 @@ nouveau_run_vbios_init(struct drm_device *dev)
+ }
+
+ if (dev_priv->card_type >= NV_50) {
+- for (i = 0; i < bios->bdcb.dcb.entries; i++) {
++ for (i = 0; i < bios->dcb.entries; i++) {
+ nouveau_bios_run_display_table(dev,
+- &bios->bdcb.dcb.entry[i],
++ &bios->dcb.entry[i],
+ 0, 0);
+ }
+ }
+@@ -5962,11 +6174,11 @@ static void
+ nouveau_bios_i2c_devices_takedown(struct drm_device *dev)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nvbios *bios = &dev_priv->VBIOS;
++ struct nvbios *bios = &dev_priv->vbios;
+ struct dcb_i2c_entry *entry;
+ int i;
+
+- entry = &bios->bdcb.dcb.i2c[0];
++ entry = &bios->dcb.i2c[0];
+ for (i = 0; i < DCB_MAX_NUM_I2C_ENTRIES; i++, entry++)
+ nouveau_i2c_fini(dev, entry);
+ }
+@@ -5975,13 +6187,11 @@ int
+ nouveau_bios_init(struct drm_device *dev)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nvbios *bios = &dev_priv->VBIOS;
++ struct nvbios *bios = &dev_priv->vbios;
+ uint32_t saved_nv_pextdev_boot_0;
+ bool was_locked;
+ int ret;
+
+- dev_priv->vbios = &bios->pub;
+-
+ if (!NVInitVBIOS(dev))
+ return -ENODEV;
+
+@@ -6023,10 +6233,8 @@ nouveau_bios_init(struct drm_device *dev)
+ bios_wr32(bios, NV_PEXTDEV_BOOT_0, saved_nv_pextdev_boot_0);
+
+ ret = nouveau_run_vbios_init(dev);
+- if (ret) {
+- dev_priv->vbios = NULL;
++ if (ret)
+ return ret;
+- }
+
+ /* feature_byte on BMP is poor, but init always sets CR4B */
+ was_locked = NVLockVgaCrtcs(dev, false);
+diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h
+index fd94bd6..adf4ec2 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_bios.h
++++ b/drivers/gpu/drm/nouveau/nouveau_bios.h
+@@ -34,9 +34,72 @@
+
+ #define DCB_LOC_ON_CHIP 0
+
++struct dcb_i2c_entry {
++ uint32_t entry;
++ uint8_t port_type;
++ uint8_t read, write;
++ struct nouveau_i2c_chan *chan;
++};
++
++enum dcb_gpio_tag {
++ DCB_GPIO_TVDAC0 = 0xc,
++ DCB_GPIO_TVDAC1 = 0x2d,
++};
++
++struct dcb_gpio_entry {
++ enum dcb_gpio_tag tag;
++ int line;
++ bool invert;
++ uint32_t entry;
++ uint8_t state_default;
++ uint8_t state[2];
++};
++
++struct dcb_gpio_table {
++ int entries;
++ struct dcb_gpio_entry entry[DCB_MAX_NUM_GPIO_ENTRIES];
++};
++
++enum dcb_connector_type {
++ DCB_CONNECTOR_VGA = 0x00,
++ DCB_CONNECTOR_TV_0 = 0x10,
++ DCB_CONNECTOR_TV_1 = 0x11,
++ DCB_CONNECTOR_TV_3 = 0x13,
++ DCB_CONNECTOR_DVI_I = 0x30,
++ DCB_CONNECTOR_DVI_D = 0x31,
++ DCB_CONNECTOR_LVDS = 0x40,
++ DCB_CONNECTOR_DP = 0x46,
++ DCB_CONNECTOR_eDP = 0x47,
++ DCB_CONNECTOR_HDMI_0 = 0x60,
++ DCB_CONNECTOR_HDMI_1 = 0x61,
++ DCB_CONNECTOR_NONE = 0xff
++};
++
++struct dcb_connector_table_entry {
++ uint8_t index;
++ uint32_t entry;
++ enum dcb_connector_type type;
++ uint8_t index2;
++ uint8_t gpio_tag;
++};
++
++struct dcb_connector_table {
++ int entries;
++ struct dcb_connector_table_entry entry[DCB_MAX_NUM_CONNECTOR_ENTRIES];
++};
++
++enum dcb_type {
++ OUTPUT_ANALOG = 0,
++ OUTPUT_TV = 1,
++ OUTPUT_TMDS = 2,
++ OUTPUT_LVDS = 3,
++ OUTPUT_DP = 6,
++ OUTPUT_ANY = -1
++};
++
+ struct dcb_entry {
+ int index; /* may not be raw dcb index if merging has happened */
+- uint8_t type;
++ enum dcb_type type;
+ uint8_t i2c_index;
+ uint8_t heads;
+ uint8_t connector;
+@@ -71,69 +134,22 @@ struct dcb_entry {
+ bool i2c_upper_default;
+ };
+
+-struct dcb_i2c_entry {
+- uint8_t port_type;
+- uint8_t read, write;
+- struct nouveau_i2c_chan *chan;
+-};
++struct dcb_table {
++ uint8_t version;
+
+-struct parsed_dcb {
+ int entries;
+ struct dcb_entry entry[DCB_MAX_NUM_ENTRIES];
+- struct dcb_i2c_entry i2c[DCB_MAX_NUM_I2C_ENTRIES];
+-};
+-
+-enum dcb_gpio_tag {
+- DCB_GPIO_TVDAC0 = 0xc,
+- DCB_GPIO_TVDAC1 = 0x2d,
+-};
+-
+-struct dcb_gpio_entry {
+- enum dcb_gpio_tag tag;
+- int line;
+- bool invert;
+-};
+-
+-struct parsed_dcb_gpio {
+- int entries;
+- struct dcb_gpio_entry entry[DCB_MAX_NUM_GPIO_ENTRIES];
+-};
+-
+-struct dcb_connector_table_entry {
+- uint32_t entry;
+- uint8_t type;
+- uint8_t index;
+- uint8_t gpio_tag;
+-};
+-
+-struct dcb_connector_table {
+- int entries;
+- struct dcb_connector_table_entry entry[DCB_MAX_NUM_CONNECTOR_ENTRIES];
+-};
+-
+-struct bios_parsed_dcb {
+- uint8_t version;
+-
+- struct parsed_dcb dcb;
+
+ uint8_t *i2c_table;
+ uint8_t i2c_default_indices;
++ struct dcb_i2c_entry i2c[DCB_MAX_NUM_I2C_ENTRIES];
+
+ uint16_t gpio_table_ptr;
+- struct parsed_dcb_gpio gpio;
++ struct dcb_gpio_table gpio;
+ uint16_t connector_table_ptr;
+ struct dcb_connector_table connector;
+ };
+
+-enum nouveau_encoder_type {
+- OUTPUT_ANALOG = 0,
+- OUTPUT_TV = 1,
+- OUTPUT_TMDS = 2,
+- OUTPUT_LVDS = 3,
+- OUTPUT_DP = 6,
+- OUTPUT_ANY = -1
+-};
+-
+ enum nouveau_or {
+ OUTPUT_A = (1 << 0),
+ OUTPUT_B = (1 << 1),
+@@ -190,8 +206,8 @@ struct pll_lims {
+ int refclk;
+ };
+
+-struct nouveau_bios_info {
+- struct parsed_dcb *dcb;
++struct nvbios {
++ struct drm_device *dev;
+
+ uint8_t chip_version;
+
+@@ -199,11 +215,6 @@ struct nouveau_bios_info {
+ uint32_t tvdactestval;
+ uint8_t digital_min_front_porch;
+ bool fp_no_ddc;
+-};
+-
+-struct nvbios {
+- struct drm_device *dev;
+- struct nouveau_bios_info pub;
+
+ struct mutex lock;
+
+@@ -234,7 +245,7 @@ struct nvbios {
+ uint16_t some_script_ptr; /* BIT I + 14 */
+ uint16_t init96_tbl_ptr; /* BIT I + 16 */
+
+- struct bios_parsed_dcb bdcb;
++ struct dcb_table dcb;
+
+ struct {
+ int crtchead;
+@@ -260,7 +271,6 @@ struct nvbios {
+ bool reset_after_pclk_change;
+ bool dual_link;
+ bool link_c_increment;
+- bool BITbit1;
+ bool if_is_24bit;
+ int duallink_transition_clk;
+ uint8_t strapless_is_24bit;
+diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c
+index 028719f..8fac10d 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_bo.c
++++ b/drivers/gpu/drm/nouveau/nouveau_bo.c
+@@ -71,7 +71,7 @@ nouveau_bo_fixup_align(struct drm_device *dev,
+ * many small buffers.
+ */
+ if (dev_priv->card_type == NV_50) {
+- uint32_t block_size = nouveau_mem_fb_amount(dev) >> 15;
++ uint32_t block_size = dev_priv->vram_size >> 15;
+ int i;
+
+ switch (tile_flags) {
+@@ -153,17 +153,17 @@ nouveau_bo_new(struct drm_device *dev, struct nouveau_channel *chan,
+
+ nvbo->placement.fpfn = 0;
+ nvbo->placement.lpfn = mappable ? dev_priv->fb_mappable_pages : 0;
+- nouveau_bo_placement_set(nvbo, flags);
++ nouveau_bo_placement_set(nvbo, flags, 0);
+
+ nvbo->channel = chan;
+ ret = ttm_bo_init(&dev_priv->ttm.bdev, &nvbo->bo, size,
+ ttm_bo_type_device, &nvbo->placement, align, 0,
+ false, NULL, size, nouveau_bo_del_ttm);
+- nvbo->channel = NULL;
+ if (ret) {
+ /* ttm will call nouveau_bo_del_ttm if it fails.. */
+ return ret;
+ }
++ nvbo->channel = NULL;
+
+ spin_lock(&dev_priv->ttm.bo_list_lock);
+ list_add_tail(&nvbo->head, &dev_priv->ttm.bo_list);
+@@ -172,26 +172,33 @@ nouveau_bo_new(struct drm_device *dev, struct nouveau_channel *chan,
+ return 0;
+ }
+
++static void
++set_placement_list(uint32_t *pl, unsigned *n, uint32_t type, uint32_t flags)
++{
++ *n = 0;
++
++ if (type & TTM_PL_FLAG_VRAM)
++ pl[(*n)++] = TTM_PL_FLAG_VRAM | flags;
++ if (type & TTM_PL_FLAG_TT)
++ pl[(*n)++] = TTM_PL_FLAG_TT | flags;
++ if (type & TTM_PL_FLAG_SYSTEM)
++ pl[(*n)++] = TTM_PL_FLAG_SYSTEM | flags;
++}
++
+ void
+-nouveau_bo_placement_set(struct nouveau_bo *nvbo, uint32_t memtype)
++nouveau_bo_placement_set(struct nouveau_bo *nvbo, uint32_t type, uint32_t busy)
+ {
+- int n = 0;
+-
+- if (memtype & TTM_PL_FLAG_VRAM)
+- nvbo->placements[n++] = TTM_PL_FLAG_VRAM | TTM_PL_MASK_CACHING;
+- if (memtype & TTM_PL_FLAG_TT)
+- nvbo->placements[n++] = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING;
+- if (memtype & TTM_PL_FLAG_SYSTEM)
+- nvbo->placements[n++] = TTM_PL_FLAG_SYSTEM | TTM_PL_MASK_CACHING;
+- nvbo->placement.placement = nvbo->placements;
+- nvbo->placement.busy_placement = nvbo->placements;
+- nvbo->placement.num_placement = n;
+- nvbo->placement.num_busy_placement = n;
+-
+- if (nvbo->pin_refcnt) {
+- while (n--)
+- nvbo->placements[n] |= TTM_PL_FLAG_NO_EVICT;
+- }
++ struct ttm_placement *pl = &nvbo->placement;
++ uint32_t flags = TTM_PL_MASK_CACHING |
++ (nvbo->pin_refcnt ? TTM_PL_FLAG_NO_EVICT : 0);
++
++ pl->placement = nvbo->placements;
++ set_placement_list(nvbo->placements, &pl->num_placement,
++ type, flags);
++
++ pl->busy_placement = nvbo->busy_placements;
++ set_placement_list(nvbo->busy_placements, &pl->num_busy_placement,
++ type | busy, flags);
+ }
+
+ int
+@@ -199,7 +206,7 @@ nouveau_bo_pin(struct nouveau_bo *nvbo, uint32_t memtype)
+ {
+ struct drm_nouveau_private *dev_priv = nouveau_bdev(nvbo->bo.bdev);
+ struct ttm_buffer_object *bo = &nvbo->bo;
+- int ret, i;
++ int ret;
+
+ if (nvbo->pin_refcnt && !(memtype & (1 << bo->mem.mem_type))) {
+ NV_ERROR(nouveau_bdev(bo->bdev)->dev,
+@@ -215,9 +222,7 @@ nouveau_bo_pin(struct nouveau_bo *nvbo, uint32_t memtype)
+ if (ret)
+ goto out;
+
+- nouveau_bo_placement_set(nvbo, memtype);
+- for (i = 0; i < nvbo->placement.num_placement; i++)
+- nvbo->placements[i] |= TTM_PL_FLAG_NO_EVICT;
++ nouveau_bo_placement_set(nvbo, memtype, 0);
+
+ ret = ttm_bo_validate(bo, &nvbo->placement, false, false);
+ if (ret == 0) {
+@@ -244,7 +249,7 @@ nouveau_bo_unpin(struct nouveau_bo *nvbo)
+ {
+ struct drm_nouveau_private *dev_priv = nouveau_bdev(nvbo->bo.bdev);
+ struct ttm_buffer_object *bo = &nvbo->bo;
+- int ret, i;
++ int ret;
+
+ if (--nvbo->pin_refcnt)
+ return 0;
+@@ -253,8 +258,7 @@ nouveau_bo_unpin(struct nouveau_bo *nvbo)
+ if (ret)
+ return ret;
+
+- for (i = 0; i < nvbo->placement.num_placement; i++)
+- nvbo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT;
++ nouveau_bo_placement_set(nvbo, bo->mem.placement, 0);
+
+ ret = ttm_bo_validate(bo, &nvbo->placement, false, false);
+ if (ret == 0) {
+@@ -395,8 +399,8 @@ nouveau_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
+ man->io_addr = NULL;
+ man->io_offset = drm_get_resource_start(dev, 1);
+ man->io_size = drm_get_resource_len(dev, 1);
+- if (man->io_size > nouveau_mem_fb_amount(dev))
+- man->io_size = nouveau_mem_fb_amount(dev);
++ if (man->io_size > dev_priv->vram_size)
++ man->io_size = dev_priv->vram_size;
+
+ man->gpu_offset = dev_priv->vm_vram_base;
+ break;
+@@ -439,11 +443,11 @@ nouveau_bo_evict_flags(struct ttm_buffer_object *bo, struct ttm_placement *pl)
+
+ switch (bo->mem.mem_type) {
+ case TTM_PL_VRAM:
+- nouveau_bo_placement_set(nvbo, TTM_PL_FLAG_TT |
++ nouveau_bo_placement_set(nvbo, TTM_PL_FLAG_TT,
+ TTM_PL_FLAG_SYSTEM);
+ break;
+ default:
+- nouveau_bo_placement_set(nvbo, TTM_PL_FLAG_SYSTEM);
++ nouveau_bo_placement_set(nvbo, TTM_PL_FLAG_SYSTEM, 0);
+ break;
+ }
+
+diff --git a/drivers/gpu/drm/nouveau/nouveau_calc.c b/drivers/gpu/drm/nouveau/nouveau_calc.c
+index ee2b845..88f9bc0 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_calc.c
++++ b/drivers/gpu/drm/nouveau/nouveau_calc.c
+@@ -274,7 +274,7 @@ getMNP_single(struct drm_device *dev, struct pll_lims *pll_lim, int clk,
+ * returns calculated clock
+ */
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- int cv = dev_priv->vbios->chip_version;
++ int cv = dev_priv->vbios.chip_version;
+ int minvco = pll_lim->vco1.minfreq, maxvco = pll_lim->vco1.maxfreq;
+ int minM = pll_lim->vco1.min_m, maxM = pll_lim->vco1.max_m;
+ int minN = pll_lim->vco1.min_n, maxN = pll_lim->vco1.max_n;
+@@ -373,7 +373,7 @@ getMNP_double(struct drm_device *dev, struct pll_lims *pll_lim, int clk,
+ * returns calculated clock
+ */
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- int chip_version = dev_priv->vbios->chip_version;
++ int chip_version = dev_priv->vbios.chip_version;
+ int minvco1 = pll_lim->vco1.minfreq, maxvco1 = pll_lim->vco1.maxfreq;
+ int minvco2 = pll_lim->vco2.minfreq, maxvco2 = pll_lim->vco2.maxfreq;
+ int minU1 = pll_lim->vco1.min_inputfreq, minU2 = pll_lim->vco2.min_inputfreq;
+diff --git a/drivers/gpu/drm/nouveau/nouveau_channel.c b/drivers/gpu/drm/nouveau/nouveau_channel.c
+index adac0f8..1fc57ef 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_channel.c
++++ b/drivers/gpu/drm/nouveau/nouveau_channel.c
+@@ -142,7 +142,6 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret,
+ GFP_KERNEL);
+ if (!dev_priv->fifos[channel])
+ return -ENOMEM;
+- dev_priv->fifo_alloc_count++;
+ chan = dev_priv->fifos[channel];
+ INIT_LIST_HEAD(&chan->nvsw.vbl_wait);
+ INIT_LIST_HEAD(&chan->fence.pending);
+@@ -280,9 +279,18 @@ nouveau_channel_free(struct nouveau_channel *chan)
+ */
+ nouveau_fence_fini(chan);
+
+- /* Ensure the channel is no longer active on the GPU */
++ /* This will prevent pfifo from switching channels. */
+ pfifo->reassign(dev, false);
+
++ /* We want to give pgraph a chance to idle and get rid of all potential
++ * errors. We need to do this before the lock, otherwise the irq handler
++ * is unable to process them.
++ */
++ if (pgraph->channel(dev) == chan)
++ nouveau_wait_for_idle(dev);
++
++ spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
++
+ pgraph->fifo_access(dev, false);
+ if (pgraph->channel(dev) == chan)
+ pgraph->unload_context(dev);
+@@ -298,6 +306,8 @@ nouveau_channel_free(struct nouveau_channel *chan)
+
+ pfifo->reassign(dev, true);
+
++ spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
++
+ /* Release the channel's resources */
+ nouveau_gpuobj_ref_del(dev, &chan->pushbuf);
+ if (chan->pushbuf_bo) {
+@@ -310,7 +320,6 @@ nouveau_channel_free(struct nouveau_channel *chan)
+ iounmap(chan->user);
+
+ dev_priv->fifos[chan->id] = NULL;
+- dev_priv->fifo_alloc_count--;
+ kfree(chan);
+ }
+
+diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
+index d2f6335..8fc0145 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
++++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
+@@ -218,7 +218,7 @@ nouveau_connector_set_encoder(struct drm_connector *connector,
+ connector->interlace_allowed = true;
+ }
+
+- if (connector->connector_type == DRM_MODE_CONNECTOR_DVII) {
++ if (nv_connector->dcb->type == DCB_CONNECTOR_DVI_I) {
+ drm_connector_property_set_value(connector,
+ dev->mode_config.dvi_i_subconnector_property,
+ nv_encoder->dcb->type == OUTPUT_TMDS ?
+@@ -236,7 +236,7 @@ nouveau_connector_detect(struct drm_connector *connector)
+ struct nouveau_i2c_chan *i2c;
+ int type, flags;
+
+- if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
++ if (nv_connector->dcb->type == DCB_CONNECTOR_LVDS)
+ nv_encoder = find_encoder_by_type(connector, OUTPUT_LVDS);
+ if (nv_encoder && nv_connector->native_mode) {
+ #ifdef CONFIG_ACPI
+@@ -279,7 +279,7 @@ nouveau_connector_detect(struct drm_connector *connector)
+ * same i2c channel so the value returned from ddc_detect
+ * isn't necessarily correct.
+ */
+- if (connector->connector_type == DRM_MODE_CONNECTOR_DVII) {
++ if (nv_connector->dcb->type == DCB_CONNECTOR_DVI_I) {
+ if (nv_connector->edid->input & DRM_EDID_INPUT_DIGITAL)
+ type = OUTPUT_TMDS;
+ else
+@@ -300,7 +300,7 @@ nouveau_connector_detect(struct drm_connector *connector)
+
+ detect_analog:
+ nv_encoder = find_encoder_by_type(connector, OUTPUT_ANALOG);
+- if (!nv_encoder)
++ if (!nv_encoder && !nouveau_tv_disable)
+ nv_encoder = find_encoder_by_type(connector, OUTPUT_TV);
+ if (nv_encoder) {
+ struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
+@@ -321,11 +321,11 @@ detect_analog:
+ static void
+ nouveau_connector_force(struct drm_connector *connector)
+ {
+- struct drm_device *dev = connector->dev;
++ struct nouveau_connector *nv_connector = nouveau_connector(connector);
+ struct nouveau_encoder *nv_encoder;
+ int type;
+
+- if (connector->connector_type == DRM_MODE_CONNECTOR_DVII) {
++ if (nv_connector->dcb->type == DCB_CONNECTOR_DVI_I) {
+ if (connector->force == DRM_FORCE_ON_DIGITAL)
+ type = OUTPUT_TMDS;
+ else
+@@ -335,7 +335,7 @@ nouveau_connector_force(struct drm_connector *connector)
+
+ nv_encoder = find_encoder_by_type(connector, type);
+ if (!nv_encoder) {
+- NV_ERROR(dev, "can't find encoder to force %s on!\n",
++ NV_ERROR(connector->dev, "can't find encoder to force %s on!\n",
+ drm_get_connector_name(connector));
+ connector->status = connector_status_disconnected;
+ return;
+@@ -369,7 +369,7 @@ nouveau_connector_set_property(struct drm_connector *connector,
+ }
+
+ /* LVDS always needs gpu scaling */
+- if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS &&
++ if (nv_connector->dcb->type == DCB_CONNECTOR_LVDS &&
+ value == DRM_MODE_SCALE_NONE)
+ return -EINVAL;
+
+@@ -535,7 +535,7 @@ nouveau_connector_get_modes(struct drm_connector *connector)
+ /* If we're not LVDS, destroy the previous native mode, the attached
+ * monitor could have changed.
+ */
+- if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS &&
++ if (nv_connector->dcb->type != DCB_CONNECTOR_LVDS &&
+ nv_connector->native_mode) {
+ drm_mode_destroy(dev, nv_connector->native_mode);
+ nv_connector->native_mode = NULL;
+@@ -563,7 +563,7 @@ nouveau_connector_get_modes(struct drm_connector *connector)
+ ret = get_slave_funcs(nv_encoder)->
+ get_modes(to_drm_encoder(nv_encoder), connector);
+
+- if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
++ if (nv_encoder->dcb->type == OUTPUT_LVDS)
+ ret += nouveau_connector_scaler_modes_add(connector);
+
+ return ret;
+@@ -613,6 +613,9 @@ nouveau_connector_mode_valid(struct drm_connector *connector,
+
+ clock *= 3;
+ break;
++ default:
++ BUG_ON(1);
++ return MODE_BAD;
+ }
+
+ if (clock < min_clock)
+@@ -680,7 +683,7 @@ nouveau_connector_create_lvds(struct drm_device *dev,
+ /* Firstly try getting EDID over DDC, if allowed and I2C channel
+ * is available.
+ */
+- if (!dev_priv->VBIOS.pub.fp_no_ddc && nv_encoder->dcb->i2c_index < 0xf)
++ if (!dev_priv->vbios.fp_no_ddc && nv_encoder->dcb->i2c_index < 0xf)
+ i2c = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index);
+
+ if (i2c) {
+@@ -695,7 +698,7 @@ nouveau_connector_create_lvds(struct drm_device *dev,
+ */
+ if (!nv_connector->edid && nouveau_bios_fp_mode(dev, &native) &&
+ (nv_encoder->dcb->lvdsconf.use_straps_for_mode ||
+- dev_priv->VBIOS.pub.fp_no_ddc)) {
++ dev_priv->vbios.fp_no_ddc)) {
+ nv_connector->native_mode = drm_mode_duplicate(dev, &native);
+ goto out;
+ }
+@@ -704,7 +707,7 @@ nouveau_connector_create_lvds(struct drm_device *dev,
+ * stored for the panel stored in them.
+ */
+ if (!nv_connector->edid && !nv_connector->native_mode &&
+- !dev_priv->VBIOS.pub.fp_no_ddc) {
++ !dev_priv->vbios.fp_no_ddc) {
+ struct edid *edid =
+ (struct edid *)nouveau_bios_embedded_edid(dev);
+ if (edid) {
+@@ -739,46 +742,66 @@ out:
+ }
+
+ int
+-nouveau_connector_create(struct drm_device *dev, int index, int type)
++nouveau_connector_create(struct drm_device *dev,
++ struct dcb_connector_table_entry *dcb)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_connector *nv_connector = NULL;
+ struct drm_connector *connector;
+ struct drm_encoder *encoder;
+- int ret;
++ int ret, type;
+
+ NV_DEBUG_KMS(dev, "\n");
+
+- nv_connector = kzalloc(sizeof(*nv_connector), GFP_KERNEL);
+- if (!nv_connector)
+- return -ENOMEM;
+- nv_connector->dcb = nouveau_bios_connector_entry(dev, index);
+- connector = &nv_connector->base;
+-
+- switch (type) {
+- case DRM_MODE_CONNECTOR_VGA:
++ switch (dcb->type) {
++ case DCB_CONNECTOR_NONE:
++ return 0;
++ case DCB_CONNECTOR_VGA:
+ NV_INFO(dev, "Detected a VGA connector\n");
++ type = DRM_MODE_CONNECTOR_VGA;
+ break;
+- case DRM_MODE_CONNECTOR_DVID:
+- NV_INFO(dev, "Detected a DVI-D connector\n");
++ case DCB_CONNECTOR_TV_0:
++ case DCB_CONNECTOR_TV_1:
++ case DCB_CONNECTOR_TV_3:
++ NV_INFO(dev, "Detected a TV connector\n");
++ type = DRM_MODE_CONNECTOR_TV;
+ break;
+- case DRM_MODE_CONNECTOR_DVII:
++ case DCB_CONNECTOR_DVI_I:
+ NV_INFO(dev, "Detected a DVI-I connector\n");
++ type = DRM_MODE_CONNECTOR_DVII;
+ break;
+- case DRM_MODE_CONNECTOR_LVDS:
+- NV_INFO(dev, "Detected a LVDS connector\n");
++ case DCB_CONNECTOR_DVI_D:
++ NV_INFO(dev, "Detected a DVI-D connector\n");
++ type = DRM_MODE_CONNECTOR_DVID;
+ break;
+- case DRM_MODE_CONNECTOR_TV:
+- NV_INFO(dev, "Detected a TV connector\n");
++ case DCB_CONNECTOR_HDMI_0:
++ case DCB_CONNECTOR_HDMI_1:
++ NV_INFO(dev, "Detected a HDMI connector\n");
++ type = DRM_MODE_CONNECTOR_HDMIA;
+ break;
+- case DRM_MODE_CONNECTOR_DisplayPort:
++ case DCB_CONNECTOR_LVDS:
++ NV_INFO(dev, "Detected a LVDS connector\n");
++ type = DRM_MODE_CONNECTOR_LVDS;
++ break;
++ case DCB_CONNECTOR_DP:
+ NV_INFO(dev, "Detected a DisplayPort connector\n");
++ type = DRM_MODE_CONNECTOR_DisplayPort;
+ break;
+- default:
+- NV_ERROR(dev, "Unknown connector, this is not good.\n");
++ case DCB_CONNECTOR_eDP:
++ NV_INFO(dev, "Detected an eDP connector\n");
++ type = DRM_MODE_CONNECTOR_eDP;
+ break;
++ default:
++ NV_ERROR(dev, "unknown connector type: 0x%02x!!\n", dcb->type);
++ return -EINVAL;
+ }
+
++ nv_connector = kzalloc(sizeof(*nv_connector), GFP_KERNEL);
++ if (!nv_connector)
++ return -ENOMEM;
++ nv_connector->dcb = dcb;
++ connector = &nv_connector->base;
++
+ /* defaults, will get overridden in detect() */
+ connector->interlace_allowed = false;
+ connector->doublescan_allowed = false;
+@@ -786,55 +809,65 @@ nouveau_connector_create(struct drm_device *dev, int index, int type)
+ drm_connector_init(dev, connector, &nouveau_connector_funcs, type);
+ drm_connector_helper_add(connector, &nouveau_connector_helper_funcs);
+
++ /* attach encoders */
++ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++
++ if (nv_encoder->dcb->connector != dcb->index)
++ continue;
++
++ if (get_slave_funcs(nv_encoder))
++ get_slave_funcs(nv_encoder)->create_resources(encoder, connector);
++
++ drm_mode_connector_attach_encoder(connector, encoder);
++ }
++
++ if (!connector->encoder_ids[0]) {
++ NV_WARN(dev, " no encoders, ignoring\n");
++ drm_connector_cleanup(connector);
++ kfree(connector);
++ return 0;
++ }
++
+ /* Init DVI-I specific properties */
+- if (type == DRM_MODE_CONNECTOR_DVII) {
++ if (dcb->type == DCB_CONNECTOR_DVI_I) {
+ drm_mode_create_dvi_i_properties(dev);
+ drm_connector_attach_property(connector, dev->mode_config.dvi_i_subconnector_property, 0);
+ drm_connector_attach_property(connector, dev->mode_config.dvi_i_select_subconnector_property, 0);
+ }
+
+- if (type != DRM_MODE_CONNECTOR_LVDS)
++ if (dcb->type != DCB_CONNECTOR_LVDS)
+ nv_connector->use_dithering = false;
+
+- if (type == DRM_MODE_CONNECTOR_DVID ||
+- type == DRM_MODE_CONNECTOR_DVII ||
+- type == DRM_MODE_CONNECTOR_LVDS ||
+- type == DRM_MODE_CONNECTOR_DisplayPort) {
+- nv_connector->scaling_mode = DRM_MODE_SCALE_FULLSCREEN;
+-
+- drm_connector_attach_property(connector, dev->mode_config.scaling_mode_property,
+- nv_connector->scaling_mode);
+- drm_connector_attach_property(connector, dev->mode_config.dithering_mode_property,
+- nv_connector->use_dithering ? DRM_MODE_DITHERING_ON
+- : DRM_MODE_DITHERING_OFF);
+-
+- } else {
+- nv_connector->scaling_mode = DRM_MODE_SCALE_NONE;
+-
+- if (type == DRM_MODE_CONNECTOR_VGA &&
+- dev_priv->card_type >= NV_50) {
++ switch (dcb->type) {
++ case DCB_CONNECTOR_VGA:
++ if (dev_priv->card_type >= NV_50) {
+ drm_connector_attach_property(connector,
+ dev->mode_config.scaling_mode_property,
+ nv_connector->scaling_mode);
+ }
+- }
+-
+- /* attach encoders */
+- list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+-
+- if (nv_encoder->dcb->connector != index)
+- continue;
+-
+- if (get_slave_funcs(nv_encoder))
+- get_slave_funcs(nv_encoder)->create_resources(encoder, connector);
++ /* fall-through */
++ case DCB_CONNECTOR_TV_0:
++ case DCB_CONNECTOR_TV_1:
++ case DCB_CONNECTOR_TV_3:
++ nv_connector->scaling_mode = DRM_MODE_SCALE_NONE;
++ break;
++ default:
++ nv_connector->scaling_mode = DRM_MODE_SCALE_FULLSCREEN;
+
+- drm_mode_connector_attach_encoder(connector, encoder);
++ drm_connector_attach_property(connector,
++ dev->mode_config.scaling_mode_property,
++ nv_connector->scaling_mode);
++ drm_connector_attach_property(connector,
++ dev->mode_config.dithering_mode_property,
++ nv_connector->use_dithering ?
++ DRM_MODE_DITHERING_ON : DRM_MODE_DITHERING_OFF);
++ break;
+ }
+
+ drm_sysfs_connector_add(connector);
+
+- if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
++ if (dcb->type == DCB_CONNECTOR_LVDS) {
+ ret = nouveau_connector_create_lvds(dev, connector);
+ if (ret) {
+ connector->funcs->destroy(connector);
+diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h
+index 728b809..4ef38ab 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_connector.h
++++ b/drivers/gpu/drm/nouveau/nouveau_connector.h
+@@ -49,6 +49,7 @@ static inline struct nouveau_connector *nouveau_connector(
+ return container_of(con, struct nouveau_connector, base);
+ }
+
+-int nouveau_connector_create(struct drm_device *dev, int i2c_index, int type);
++int nouveau_connector_create(struct drm_device *,
++ struct dcb_connector_table_entry *);
+
+ #endif /* __NOUVEAU_CONNECTOR_H__ */
+diff --git a/drivers/gpu/drm/nouveau/nouveau_debugfs.c b/drivers/gpu/drm/nouveau/nouveau_debugfs.c
+index 89e36ee..a251886 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_debugfs.c
++++ b/drivers/gpu/drm/nouveau/nouveau_debugfs.c
+@@ -137,16 +137,28 @@ nouveau_debugfs_memory_info(struct seq_file *m, void *data)
+ {
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_minor *minor = node->minor;
+- struct drm_device *dev = minor->dev;
++ struct drm_nouveau_private *dev_priv = minor->dev->dev_private;
++
++ seq_printf(m, "VRAM total: %dKiB\n", (int)(dev_priv->vram_size >> 10));
++ return 0;
++}
++
++static int
++nouveau_debugfs_vbios_image(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 i;
+
+- seq_printf(m, "VRAM total: %dKiB\n",
+- (int)(nouveau_mem_fb_amount(dev) >> 10));
++ for (i = 0; i < dev_priv->vbios.length; i++)
++ seq_printf(m, "%c", dev_priv->vbios.data[i]);
+ return 0;
+ }
+
+ static struct drm_info_list nouveau_debugfs_list[] = {
+ { "chipset", nouveau_debugfs_chipset_info, 0, NULL },
+ { "memory", nouveau_debugfs_memory_info, 0, NULL },
++ { "vbios.rom", nouveau_debugfs_vbios_image, 0, NULL },
+ };
+ #define NOUVEAU_DEBUGFS_ENTRIES ARRAY_SIZE(nouveau_debugfs_list)
+
+diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.c b/drivers/gpu/drm/nouveau/nouveau_dma.c
+index c8482a1..65c441a 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_dma.c
++++ b/drivers/gpu/drm/nouveau/nouveau_dma.c
+@@ -190,6 +190,11 @@ nv50_dma_push(struct nouveau_channel *chan, struct nouveau_bo *bo,
+ nouveau_bo_wr32(pb, ip++, upper_32_bits(offset) | length << 8);
+
+ chan->dma.ib_put = (chan->dma.ib_put + 1) & chan->dma.ib_max;
++
++ DRM_MEMORYBARRIER();
++ /* Flush writes. */
++ nouveau_bo_rd32(pb, 0);
++
+ nvchan_wr32(chan, 0x8c, chan->dma.ib_put);
+ chan->dma.ib_free--;
+ }
+diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c
+index f954ad9..deeb21c 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_dp.c
++++ b/drivers/gpu/drm/nouveau/nouveau_dp.c
+@@ -483,7 +483,7 @@ nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr,
+ ctrl |= (cmd << NV50_AUXCH_CTRL_CMD_SHIFT);
+ ctrl |= ((data_nr - 1) << NV50_AUXCH_CTRL_LEN_SHIFT);
+
+- for (;;) {
++ for (i = 0; i < 16; i++) {
+ 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);
+@@ -502,6 +502,12 @@ nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr,
+ break;
+ }
+
++ if (i == 16) {
++ NV_ERROR(dev, "auxch DEFER too many times, bailing\n");
++ ret = -EREMOTEIO;
++ goto out;
++ }
++
+ if (cmd & 1) {
+ if ((stat & NV50_AUXCH_STAT_COUNT) != data_nr) {
+ ret = -EREMOTEIO;
+diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c
+index da3b93b..60a709c 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_drv.c
++++ b/drivers/gpu/drm/nouveau/nouveau_drv.c
+@@ -75,14 +75,22 @@ MODULE_PARM_DESC(ignorelid, "Ignore ACPI lid status");
+ int nouveau_ignorelid = 0;
+ module_param_named(ignorelid, nouveau_ignorelid, int, 0400);
+
+-MODULE_PARM_DESC(noagp, "Disable all acceleration");
++MODULE_PARM_DESC(noaccel, "Disable all acceleration");
+ int nouveau_noaccel = 0;
+ module_param_named(noaccel, nouveau_noaccel, int, 0400);
+
+-MODULE_PARM_DESC(noagp, "Disable fbcon acceleration");
++MODULE_PARM_DESC(nofbaccel, "Disable fbcon acceleration");
+ int nouveau_nofbaccel = 0;
+ module_param_named(nofbaccel, nouveau_nofbaccel, 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);
++
++MODULE_PARM_DESC(tv_disable, "Disable TV-out detection\n");
++int nouveau_tv_disable = 0;
++module_param_named(tv_disable, nouveau_tv_disable, int, 0400);
++
+ MODULE_PARM_DESC(tv_norm, "Default TV norm.\n"
+ "\t\tSupported: PAL, PAL-M, PAL-N, PAL-Nc, NTSC-M, NTSC-J,\n"
+ "\t\t\thd480i, hd480p, hd576i, hd576p, hd720p, hd1080i.\n"
+@@ -154,9 +162,11 @@ nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state)
+ if (pm_state.event == PM_EVENT_PRETHAW)
+ return 0;
+
++ NV_INFO(dev, "Disabling fbcon acceleration...\n");
+ fbdev_flags = dev_priv->fbdev_info->flags;
+ dev_priv->fbdev_info->flags |= FBINFO_HWACCEL_DISABLED;
+
++ NV_INFO(dev, "Unpinning framebuffer(s)...\n");
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ struct nouveau_framebuffer *nouveau_fb;
+
+diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
+index 5be0cca..b908f77 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
++++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
+@@ -76,6 +76,7 @@ struct nouveau_bo {
+ struct ttm_buffer_object bo;
+ struct ttm_placement placement;
+ u32 placements[3];
++ u32 busy_placements[3];
+ struct ttm_bo_kmap_obj kmap;
+ struct list_head head;
+
+@@ -519,6 +520,7 @@ struct drm_nouveau_private {
+
+ struct workqueue_struct *wq;
+ struct work_struct irq_work;
++ struct work_struct hpd_work;
+
+ struct list_head vbl_waiting;
+
+@@ -533,12 +535,14 @@ struct drm_nouveau_private {
+
+ struct fb_info *fbdev_info;
+
+- int fifo_alloc_count;
+ struct nouveau_channel *fifos[NOUVEAU_MAX_CHANNEL_NR];
+
+ struct nouveau_engine engine;
+ struct nouveau_channel *channel;
+
++ /* For PFIFO and PGRAPH. */
++ spinlock_t context_switch_lock;
++
+ /* RAMIN configuration, RAMFC, RAMHT and RAMRO offsets */
+ struct nouveau_gpuobj *ramht;
+ uint32_t ramin_rsvd_vram;
+@@ -550,12 +554,6 @@ struct drm_nouveau_private {
+ uint32_t ramro_offset;
+ uint32_t ramro_size;
+
+- /* base physical adresses */
+- uint64_t fb_phys;
+- uint64_t fb_available_size;
+- uint64_t fb_mappable_pages;
+- uint64_t fb_aper_free;
+-
+ struct {
+ enum {
+ NOUVEAU_GART_NONE = 0,
+@@ -569,10 +567,6 @@ struct drm_nouveau_private {
+ struct nouveau_gpuobj *sg_ctxdma;
+ struct page *sg_dummy_page;
+ dma_addr_t sg_dummy_bus;
+-
+- /* nottm hack */
+- struct drm_ttm_backend *sg_be;
+- unsigned long sg_handle;
+ } gart_info;
+
+ /* nv10-nv40 tiling regions */
+@@ -581,6 +575,16 @@ struct drm_nouveau_private {
+ spinlock_t lock;
+ } tile;
+
++ /* VRAM/fb configuration */
++ uint64_t vram_size;
++ uint64_t vram_sys_base;
++
++ uint64_t fb_phys;
++ uint64_t fb_available_size;
++ uint64_t fb_mappable_pages;
++ uint64_t fb_aper_free;
++ int fb_mtrr;
++
+ /* G8x/G9x virtual address space */
+ uint64_t vm_gart_base;
+ uint64_t vm_gart_size;
+@@ -589,10 +593,6 @@ struct drm_nouveau_private {
+ uint64_t vm_end;
+ struct nouveau_gpuobj *vm_vram_pt[NV50_VM_VRAM_NR];
+ int vm_vram_pt_nr;
+- uint64_t vram_sys_base;
+-
+- /* the mtrr covering the FB */
+- int fb_mtrr;
+
+ struct mem_block *ramin_heap;
+
+@@ -602,8 +602,7 @@ struct drm_nouveau_private {
+
+ struct list_head gpuobj_list;
+
+- struct nvbios VBIOS;
+- struct nouveau_bios_info *vbios;
++ struct nvbios vbios;
+
+ struct nv04_mode_state mode_reg;
+ struct nv04_mode_state saved_reg;
+@@ -612,11 +611,7 @@ struct drm_nouveau_private {
+ uint32_t dac_users[4];
+
+ struct nouveau_suspend_resume {
+- uint32_t fifo_mode;
+- uint32_t graph_ctx_control;
+- uint32_t graph_state;
+ uint32_t *ramin_copy;
+- uint64_t ramin_size;
+ } susres;
+
+ struct backlight_device *backlight;
+@@ -680,6 +675,7 @@ extern int nouveau_uscript_tmds;
+ extern int nouveau_vram_pushbuf;
+ extern int nouveau_vram_notify;
+ extern int nouveau_fbpercrtc;
++extern int nouveau_tv_disable;
+ extern char *nouveau_tv_norm;
+ extern int nouveau_reg_debug;
+ extern char *nouveau_vbios;
+@@ -687,6 +683,7 @@ extern int nouveau_ctxfw;
+ extern int nouveau_ignorelid;
+ extern int nouveau_nofbaccel;
+ extern int nouveau_noaccel;
++extern int nouveau_override_conntype;
+
+ /* nouveau_state.c */
+ extern void nouveau_preclose(struct drm_device *dev, struct drm_file *);
+@@ -711,7 +708,7 @@ extern struct mem_block *nouveau_mem_alloc_block(struct mem_block *,
+ struct drm_file *, int tail);
+ extern void nouveau_mem_takedown(struct mem_block **heap);
+ extern void nouveau_mem_free_block(struct mem_block *);
+-extern uint64_t nouveau_mem_fb_amount(struct drm_device *);
++extern 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_agp(struct drm_device *);
+@@ -928,6 +925,10 @@ 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 *);
++
+ /* nv04_fifo.c */
+ extern int nv04_fifo_init(struct drm_device *);
+ extern void nv04_fifo_disable(struct drm_device *);
+@@ -1027,6 +1028,7 @@ extern void nv50_graph_destroy_context(struct nouveau_channel *);
+ extern int nv50_graph_load_context(struct nouveau_channel *);
+ extern int nv50_graph_unload_context(struct drm_device *);
+ extern void nv50_graph_context_switch(struct drm_device *);
++extern int nv50_grctx_init(struct nouveau_grctx *);
+
+ /* nouveau_grctx.c */
+ extern int nouveau_grctx_prog_load(struct drm_device *);
+@@ -1119,7 +1121,8 @@ extern int nouveau_bo_pin(struct nouveau_bo *, uint32_t flags);
+ extern int nouveau_bo_unpin(struct nouveau_bo *);
+ extern int nouveau_bo_map(struct nouveau_bo *);
+ extern void nouveau_bo_unmap(struct nouveau_bo *);
+-extern void nouveau_bo_placement_set(struct nouveau_bo *, uint32_t memtype);
++extern void nouveau_bo_placement_set(struct nouveau_bo *, uint32_t type,
++ uint32_t busy);
+ 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);
+@@ -1163,6 +1166,16 @@ extern int nouveau_gem_ioctl_info(struct drm_device *, void *,
+ 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);
+
++/* nv50_gpio.c */
++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);
++
++/* nv50_calc. */
++int nv50_calc_pll(struct drm_device *, struct pll_lims *, int clk,
++ int *N1, int *M1, int *N2, int *M2, int *P);
++int nv50_calc_pll2(struct drm_device *, struct pll_lims *,
++ int clk, int *N, int *fN, int *M, int *P);
++
+ #ifndef ioread32_native
+ #ifdef __BIG_ENDIAN
+ #define ioread16_native ioread16be
+diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h
+index bc4a240..e1df820 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_encoder.h
++++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h
+@@ -47,6 +47,9 @@ struct nouveau_encoder {
+
+ union {
+ struct {
++ int mc_unknown;
++ uint32_t unk0;
++ uint32_t unk1;
+ int dpcd_version;
+ int link_nr;
+ int link_bw;
+diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c
+index 8265fed..0846a1e 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_gem.c
++++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
+@@ -182,40 +182,35 @@ nouveau_gem_set_domain(struct drm_gem_object *gem, uint32_t read_domains,
+ {
+ struct nouveau_bo *nvbo = gem->driver_private;
+ struct ttm_buffer_object *bo = &nvbo->bo;
+- uint64_t flags;
++ uint32_t domains = valid_domains &
++ (write_domains ? write_domains : read_domains);
++ uint32_t pref_flags = 0, valid_flags = 0;
+
+- if (!valid_domains || (!read_domains && !write_domains))
++ if (!domains)
+ return -EINVAL;
+
+- if (write_domains) {
+- if ((valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) &&
+- (write_domains & NOUVEAU_GEM_DOMAIN_VRAM))
+- flags = TTM_PL_FLAG_VRAM;
+- else
+- if ((valid_domains & NOUVEAU_GEM_DOMAIN_GART) &&
+- (write_domains & NOUVEAU_GEM_DOMAIN_GART))
+- flags = TTM_PL_FLAG_TT;
+- else
+- return -EINVAL;
+- } else {
+- if ((valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) &&
+- (read_domains & NOUVEAU_GEM_DOMAIN_VRAM) &&
+- bo->mem.mem_type == TTM_PL_VRAM)
+- flags = TTM_PL_FLAG_VRAM;
+- else
+- if ((valid_domains & NOUVEAU_GEM_DOMAIN_GART) &&
+- (read_domains & NOUVEAU_GEM_DOMAIN_GART) &&
+- bo->mem.mem_type == TTM_PL_TT)
+- flags = TTM_PL_FLAG_TT;
+- else
+- if ((valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) &&
+- (read_domains & NOUVEAU_GEM_DOMAIN_VRAM))
+- flags = TTM_PL_FLAG_VRAM;
+- else
+- flags = TTM_PL_FLAG_TT;
+- }
++ if (valid_domains & NOUVEAU_GEM_DOMAIN_VRAM)
++ valid_flags |= TTM_PL_FLAG_VRAM;
++
++ if (valid_domains & NOUVEAU_GEM_DOMAIN_GART)
++ valid_flags |= TTM_PL_FLAG_TT;
++
++ if ((domains & NOUVEAU_GEM_DOMAIN_VRAM) &&
++ bo->mem.mem_type == TTM_PL_VRAM)
++ pref_flags |= TTM_PL_FLAG_VRAM;
++
++ else if ((domains & NOUVEAU_GEM_DOMAIN_GART) &&
++ bo->mem.mem_type == TTM_PL_TT)
++ pref_flags |= TTM_PL_FLAG_TT;
++
++ else if (domains & NOUVEAU_GEM_DOMAIN_VRAM)
++ pref_flags |= TTM_PL_FLAG_VRAM;
++
++ else
++ pref_flags |= TTM_PL_FLAG_TT;
++
++ nouveau_bo_placement_set(nvbo, pref_flags, valid_flags);
+
+- nouveau_bo_placement_set(nvbo, flags);
+ return 0;
+ }
+
+diff --git a/drivers/gpu/drm/nouveau/nouveau_hw.c b/drivers/gpu/drm/nouveau/nouveau_hw.c
+index dc46792..7855b35 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_hw.c
++++ b/drivers/gpu/drm/nouveau/nouveau_hw.c
+@@ -160,7 +160,7 @@ static void
+ setPLL_single(struct drm_device *dev, uint32_t reg, struct nouveau_pll_vals *pv)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- int chip_version = dev_priv->vbios->chip_version;
++ int chip_version = dev_priv->vbios.chip_version;
+ uint32_t oldpll = NVReadRAMDAC(dev, 0, reg);
+ int oldN = (oldpll >> 8) & 0xff, oldM = oldpll & 0xff;
+ uint32_t pll = (oldpll & 0xfff80000) | pv->log2P << 16 | pv->NM1;
+@@ -216,7 +216,7 @@ setPLL_double_highregs(struct drm_device *dev, uint32_t reg1,
+ struct nouveau_pll_vals *pv)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- int chip_version = dev_priv->vbios->chip_version;
++ int chip_version = dev_priv->vbios.chip_version;
+ bool nv3035 = chip_version == 0x30 || chip_version == 0x35;
+ uint32_t reg2 = reg1 + ((reg1 == NV_RAMDAC_VPLL2) ? 0x5c : 0x70);
+ uint32_t oldpll1 = NVReadRAMDAC(dev, 0, reg1);
+@@ -374,7 +374,7 @@ nouveau_hw_setpll(struct drm_device *dev, uint32_t reg1,
+ struct nouveau_pll_vals *pv)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- int cv = dev_priv->vbios->chip_version;
++ int cv = dev_priv->vbios.chip_version;
+
+ if (cv == 0x30 || cv == 0x31 || cv == 0x35 || cv == 0x36 ||
+ cv >= 0x40) {
+diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c
+index 70e994d..316a3c7 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_i2c.c
++++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c
+@@ -254,16 +254,27 @@ struct nouveau_i2c_chan *
+ nouveau_i2c_find(struct drm_device *dev, int index)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nvbios *bios = &dev_priv->VBIOS;
++ struct dcb_i2c_entry *i2c = &dev_priv->vbios.dcb.i2c[index];
+
+- if (index > DCB_MAX_NUM_I2C_ENTRIES)
++ if (index >= DCB_MAX_NUM_I2C_ENTRIES)
+ return NULL;
+
+- if (!bios->bdcb.dcb.i2c[index].chan) {
+- if (nouveau_i2c_init(dev, &bios->bdcb.dcb.i2c[index], index))
+- return NULL;
++ if (dev_priv->chipset >= NV_50 && (i2c->entry & 0x00000100)) {
++ uint32_t reg = 0xe500, val;
++
++ if (i2c->port_type == 6) {
++ reg += i2c->read * 0x50;
++ val = 0x2002;
++ } else {
++ reg += ((i2c->entry & 0x1e00) >> 9) * 0x50;
++ val = 0xe001;
++ }
++
++ nv_wr32(dev, reg, (nv_rd32(dev, reg) & ~0xf003) | val);
+ }
+
+- return bios->bdcb.dcb.i2c[index].chan;
++ if (!i2c->chan && nouveau_i2c_init(dev, i2c, index))
++ return NULL;
++ return i2c->chan;
+ }
+
+diff --git a/drivers/gpu/drm/nouveau/nouveau_irq.c b/drivers/gpu/drm/nouveau/nouveau_irq.c
+index 447f9f6..13e73ce 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_irq.c
++++ b/drivers/gpu/drm/nouveau/nouveau_irq.c
+@@ -51,6 +51,7 @@ nouveau_irq_preinstall(struct drm_device *dev)
+
+ 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);
+ }
+ }
+@@ -311,6 +312,31 @@ nouveau_print_bitfield_names_(uint32_t value,
+ #define nouveau_print_bitfield_names(val, namelist) \
+ nouveau_print_bitfield_names_((val), (namelist), ARRAY_SIZE(namelist))
+
++struct nouveau_enum_names {
++ uint32_t value;
++ const char *name;
++};
++
++static void
++nouveau_print_enum_names_(uint32_t value,
++ const struct nouveau_enum_names *namelist,
++ const int namelist_len)
++{
++ /*
++ * Caller must have already printed the KERN_* log level for us.
++ * Also the caller is responsible for adding the newline.
++ */
++ int i;
++ for (i = 0; i < namelist_len; ++i) {
++ if (value == namelist[i].value) {
++ printk("%s", namelist[i].name);
++ return;
++ }
++ }
++ printk("unknown value 0x%08x", value);
++}
++#define nouveau_print_enum_names(val, namelist) \
++ nouveau_print_enum_names_((val), (namelist), ARRAY_SIZE(namelist))
+
+ static int
+ nouveau_graph_chid_from_grctx(struct drm_device *dev)
+@@ -427,14 +453,16 @@ nouveau_graph_dump_trap_info(struct drm_device *dev, const char *id,
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ uint32_t nsource = trap->nsource, nstatus = trap->nstatus;
+
+- NV_INFO(dev, "%s - nSource:", id);
+- nouveau_print_bitfield_names(nsource, nsource_names);
+- printk(", nStatus:");
+- if (dev_priv->card_type < NV_10)
+- nouveau_print_bitfield_names(nstatus, nstatus_names);
+- else
+- nouveau_print_bitfield_names(nstatus, nstatus_names_nv10);
+- printk("\n");
++ if (dev_priv->card_type < NV_50) {
++ NV_INFO(dev, "%s - nSource:", id);
++ nouveau_print_bitfield_names(nsource, nsource_names);
++ printk(", nStatus:");
++ if (dev_priv->card_type < NV_10)
++ nouveau_print_bitfield_names(nstatus, nstatus_names);
++ else
++ nouveau_print_bitfield_names(nstatus, nstatus_names_nv10);
++ printk("\n");
++ }
+
+ NV_INFO(dev, "%s - Ch %d/%d Class 0x%04x Mthd 0x%04x "
+ "Data 0x%08x:0x%08x\n",
+@@ -578,27 +606,502 @@ nouveau_pgraph_irq_handler(struct drm_device *dev)
+ }
+
+ 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" },
++ { 4, "QUADON_ACTIVE" },
++ { 8, "TIMEOUT" },
++ { 0x10, "INVALID_OPCODE" },
++ { 0x40, "BREAKPOINT" },
++};
++
++static void
++nv50_pgraph_mp_trap(struct drm_device *dev, int tpid, int display)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint32_t units = nv_rd32(dev, 0x1540);
++ uint32_t addr, mp10, status, pc, oplow, ophigh;
++ int i;
++ int mps = 0;
++ for (i = 0; i < 4; i++) {
++ if (!(units & 1 << (i+24)))
++ continue;
++ if (dev_priv->chipset < 0xa0)
++ addr = 0x408200 + (tpid << 12) + (i << 7);
++ else
++ addr = 0x408100 + (tpid << 11) + (i << 7);
++ mp10 = nv_rd32(dev, addr + 0x10);
++ status = nv_rd32(dev, addr + 0x14);
++ if (!status)
++ continue;
++ if (display) {
++ nv_rd32(dev, addr + 0x20);
++ pc = nv_rd32(dev, addr + 0x24);
++ oplow = nv_rd32(dev, addr + 0x70);
++ ophigh= nv_rd32(dev, addr + 0x74);
++ NV_INFO(dev, "PGRAPH_TRAP_MP_EXEC - "
++ "TP %d MP %d: ", tpid, i);
++ nouveau_print_enum_names(status,
++ nv50_mp_exec_error_names);
++ printk(" at %06x warp %d, opcode %08x %08x\n",
++ pc&0xffffff, pc >> 24,
++ oplow, ophigh);
++ }
++ nv_wr32(dev, addr + 0x10, mp10);
++ nv_wr32(dev, addr + 0x14, 0);
++ mps++;
++ }
++ if (!mps && display)
++ NV_INFO(dev, "PGRAPH_TRAP_MP_EXEC - TP %d: "
++ "No MPs claiming errors?\n", tpid);
++}
++
++static void
++nv50_pgraph_tp_trap(struct drm_device *dev, int type, uint32_t ustatus_old,
++ uint32_t ustatus_new, int display, const char *name)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ int tps = 0;
++ uint32_t units = nv_rd32(dev, 0x1540);
++ int i, r;
++ uint32_t ustatus_addr, ustatus;
++ for (i = 0; i < 16; i++) {
++ if (!(units & (1 << i)))
++ continue;
++ if (dev_priv->chipset < 0xa0)
++ ustatus_addr = ustatus_old + (i << 12);
++ else
++ ustatus_addr = ustatus_new + (i << 11);
++ ustatus = nv_rd32(dev, ustatus_addr) & 0x7fffffff;
++ if (!ustatus)
++ continue;
++ tps++;
++ switch (type) {
++ case 6: /* texture error... unknown for now */
++ nv50_pfb_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)
++ NV_ERROR(dev, "\t0x%08x: 0x%08x\n", r,
++ nv_rd32(dev, r));
++ }
++ break;
++ case 7: /* MP error */
++ if (ustatus & 0x00010000) {
++ nv50_pgraph_mp_trap(dev, i, display);
++ ustatus &= ~0x00010000;
++ }
++ break;
++ case 8: /* TPDMA error */
++ {
++ uint32_t e0c = nv_rd32(dev, ustatus_addr + 4);
++ uint32_t e10 = nv_rd32(dev, ustatus_addr + 8);
++ uint32_t e14 = nv_rd32(dev, ustatus_addr + 0xc);
++ uint32_t e18 = nv_rd32(dev, ustatus_addr + 0x10);
++ 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);
++ /* 2d engine destination */
++ if (ustatus & 0x00000010) {
++ if (display) {
++ NV_INFO(dev, "PGRAPH_TRAP_TPDMA_2D - TP %d - Unknown fault at address %02x%08x\n",
++ i, e14, e10);
++ NV_INFO(dev, "PGRAPH_TRAP_TPDMA_2D - TP %d - e0c: %08x, e18: %08x, e1c: %08x, e20: %08x, e24: %08x\n",
++ i, e0c, e18, e1c, e20, e24);
++ }
++ ustatus &= ~0x00000010;
++ }
++ /* Render target */
++ if (ustatus & 0x00000040) {
++ if (display) {
++ NV_INFO(dev, "PGRAPH_TRAP_TPDMA_RT - TP %d - Unknown fault at address %02x%08x\n",
++ i, e14, e10);
++ NV_INFO(dev, "PGRAPH_TRAP_TPDMA_RT - TP %d - e0c: %08x, e18: %08x, e1c: %08x, e20: %08x, e24: %08x\n",
++ i, e0c, e18, e1c, e20, e24);
++ }
++ ustatus &= ~0x00000040;
++ }
++ /* CUDA memory: l[], g[] or stack. */
++ if (ustatus & 0x00000080) {
++ if (display) {
++ if (e18 & 0x80000000) {
++ /* g[] read fault? */
++ NV_INFO(dev, "PGRAPH_TRAP_TPDMA - TP %d - Global read fault at address %02x%08x\n",
++ i, e14, e10 | ((e18 >> 24) & 0x1f));
++ e18 &= ~0x1f000000;
++ } else if (e18 & 0xc) {
++ /* g[] write fault? */
++ NV_INFO(dev, "PGRAPH_TRAP_TPDMA - TP %d - Global write fault at address %02x%08x\n",
++ i, e14, e10 | ((e18 >> 7) & 0x1f));
++ e18 &= ~0x00000f80;
++ } else {
++ NV_INFO(dev, "PGRAPH_TRAP_TPDMA - TP %d - Unknown CUDA fault at address %02x%08x\n",
++ i, e14, e10);
++ }
++ NV_INFO(dev, "PGRAPH_TRAP_TPDMA - TP %d - e0c: %08x, e18: %08x, e1c: %08x, e20: %08x, e24: %08x\n",
++ i, e0c, e18, e1c, e20, e24);
++ }
++ ustatus &= ~0x00000080;
++ }
++ }
++ break;
++ }
++ if (ustatus) {
++ if (display)
++ NV_INFO(dev, "%s - TP%d: Unhandled ustatus 0x%08x\n", name, i, ustatus);
++ }
++ nv_wr32(dev, ustatus_addr, 0xc0000000);
++ }
++
++ if (!tps && display)
++ NV_INFO(dev, "%s - No TPs claiming errors?\n", name);
++}
++
++static void
++nv50_pgraph_trap_handler(struct drm_device *dev)
++{
++ struct nouveau_pgraph_trap trap;
++ uint32_t status = nv_rd32(dev, 0x400108);
++ uint32_t ustatus;
++ int display = nouveau_ratelimit();
++
++
++ if (!status && display) {
++ nouveau_graph_trap_info(dev, &trap);
++ nouveau_graph_dump_trap_info(dev, "PGRAPH_TRAP", &trap);
++ NV_INFO(dev, "PGRAPH_TRAP - no units reporting traps?\n");
++ }
++
++ /* DISPATCH: Relays commands to other units and handles NOTIFY,
++ * COND, QUERY. If you get a trap from it, the command is still stuck
++ * in DISPATCH and you need to do something about it. */
++ if (status & 0x001) {
++ ustatus = nv_rd32(dev, 0x400804) & 0x7fffffff;
++ if (!ustatus && display) {
++ NV_INFO(dev, "PGRAPH_TRAP_DISPATCH - no ustatus?\n");
++ }
++
++ /* Known to be triggered by screwed up NOTIFY and COND... */
++ if (ustatus & 0x00000001) {
++ nv50_pfb_vm_trap(dev, display, "PGRAPH_TRAP_DISPATCH_FAULT");
++ nv_wr32(dev, 0x400500, 0);
++ if (nv_rd32(dev, 0x400808) & 0x80000000) {
++ if (display) {
++ if (nouveau_graph_trapped_channel(dev, &trap.channel))
++ trap.channel = -1;
++ trap.class = nv_rd32(dev, 0x400814);
++ trap.mthd = nv_rd32(dev, 0x400808) & 0x1ffc;
++ trap.subc = (nv_rd32(dev, 0x400808) >> 16) & 0x7;
++ trap.data = nv_rd32(dev, 0x40080c);
++ trap.data2 = nv_rd32(dev, 0x400810);
++ nouveau_graph_dump_trap_info(dev,
++ "PGRAPH_TRAP_DISPATCH_FAULT", &trap);
++ NV_INFO(dev, "PGRAPH_TRAP_DISPATCH_FAULT - 400808: %08x\n", nv_rd32(dev, 0x400808));
++ NV_INFO(dev, "PGRAPH_TRAP_DISPATCH_FAULT - 400848: %08x\n", nv_rd32(dev, 0x400848));
++ }
++ nv_wr32(dev, 0x400808, 0);
++ } else if (display) {
++ NV_INFO(dev, "PGRAPH_TRAP_DISPATCH_FAULT - No stuck command?\n");
++ }
++ nv_wr32(dev, 0x4008e8, nv_rd32(dev, 0x4008e8) & 3);
++ nv_wr32(dev, 0x400848, 0);
++ ustatus &= ~0x00000001;
++ }
++ if (ustatus & 0x00000002) {
++ nv50_pfb_vm_trap(dev, display, "PGRAPH_TRAP_DISPATCH_QUERY");
++ nv_wr32(dev, 0x400500, 0);
++ if (nv_rd32(dev, 0x40084c) & 0x80000000) {
++ if (display) {
++ if (nouveau_graph_trapped_channel(dev, &trap.channel))
++ trap.channel = -1;
++ trap.class = nv_rd32(dev, 0x400814);
++ trap.mthd = nv_rd32(dev, 0x40084c) & 0x1ffc;
++ trap.subc = (nv_rd32(dev, 0x40084c) >> 16) & 0x7;
++ trap.data = nv_rd32(dev, 0x40085c);
++ trap.data2 = 0;
++ nouveau_graph_dump_trap_info(dev,
++ "PGRAPH_TRAP_DISPATCH_QUERY", &trap);
++ NV_INFO(dev, "PGRAPH_TRAP_DISPATCH_QUERY - 40084c: %08x\n", nv_rd32(dev, 0x40084c));
++ }
++ nv_wr32(dev, 0x40084c, 0);
++ } else if (display) {
++ NV_INFO(dev, "PGRAPH_TRAP_DISPATCH_QUERY - No stuck command?\n");
++ }
++ ustatus &= ~0x00000002;
++ }
++ if (ustatus && display)
++ NV_INFO(dev, "PGRAPH_TRAP_DISPATCH - Unhandled ustatus 0x%08x\n", ustatus);
++ nv_wr32(dev, 0x400804, 0xc0000000);
++ nv_wr32(dev, 0x400108, 0x001);
++ status &= ~0x001;
++ }
++
++ /* TRAPs other than dispatch use the "normal" trap regs. */
++ if (status && display) {
++ nouveau_graph_trap_info(dev, &trap);
++ nouveau_graph_dump_trap_info(dev,
++ "PGRAPH_TRAP", &trap);
++ }
++
++ /* M2MF: Memory to memory copy engine. */
++ if (status & 0x002) {
++ ustatus = nv_rd32(dev, 0x406800) & 0x7fffffff;
++ if (!ustatus && display) {
++ NV_INFO(dev, "PGRAPH_TRAP_M2MF - no ustatus?\n");
++ }
++ if (ustatus & 0x00000001) {
++ nv50_pfb_vm_trap(dev, display, "PGRAPH_TRAP_M2MF_NOTIFY");
++ ustatus &= ~0x00000001;
++ }
++ if (ustatus & 0x00000002) {
++ nv50_pfb_vm_trap(dev, display, "PGRAPH_TRAP_M2MF_IN");
++ ustatus &= ~0x00000002;
++ }
++ if (ustatus & 0x00000004) {
++ nv50_pfb_vm_trap(dev, display, "PGRAPH_TRAP_M2MF_OUT");
++ ustatus &= ~0x00000004;
++ }
++ NV_INFO (dev, "PGRAPH_TRAP_M2MF - %08x %08x %08x %08x\n",
++ nv_rd32(dev, 0x406804),
++ nv_rd32(dev, 0x406808),
++ nv_rd32(dev, 0x40680c),
++ nv_rd32(dev, 0x406810));
++ if (ustatus && display)
++ NV_INFO(dev, "PGRAPH_TRAP_M2MF - Unhandled ustatus 0x%08x\n", ustatus);
++ /* No sane way found yet -- just reset the bugger. */
++ nv_wr32(dev, 0x400040, 2);
++ nv_wr32(dev, 0x400040, 0);
++ nv_wr32(dev, 0x406800, 0xc0000000);
++ nv_wr32(dev, 0x400108, 0x002);
++ status &= ~0x002;
++ }
++
++ /* VFETCH: Fetches data from vertex buffers. */
++ if (status & 0x004) {
++ ustatus = nv_rd32(dev, 0x400c04) & 0x7fffffff;
++ if (!ustatus && display) {
++ NV_INFO(dev, "PGRAPH_TRAP_VFETCH - no ustatus?\n");
++ }
++ if (ustatus & 0x00000001) {
++ nv50_pfb_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),
++ nv_rd32(dev, 0x400c0c),
++ nv_rd32(dev, 0x400c10));
++ ustatus &= ~0x00000001;
++ }
++ if (ustatus && display)
++ NV_INFO(dev, "PGRAPH_TRAP_VFETCH - Unhandled ustatus 0x%08x\n", ustatus);
++ nv_wr32(dev, 0x400c04, 0xc0000000);
++ nv_wr32(dev, 0x400108, 0x004);
++ status &= ~0x004;
++ }
++
++ /* STRMOUT: DirectX streamout / OpenGL transform feedback. */
++ if (status & 0x008) {
++ ustatus = nv_rd32(dev, 0x401800) & 0x7fffffff;
++ if (!ustatus && display) {
++ NV_INFO(dev, "PGRAPH_TRAP_STRMOUT - no ustatus?\n");
++ }
++ if (ustatus & 0x00000001) {
++ nv50_pfb_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),
++ nv_rd32(dev, 0x40180c),
++ nv_rd32(dev, 0x401810));
++ ustatus &= ~0x00000001;
++ }
++ if (ustatus && display)
++ NV_INFO(dev, "PGRAPH_TRAP_STRMOUT - Unhandled ustatus 0x%08x\n", ustatus);
++ /* No sane way found yet -- just reset the bugger. */
++ nv_wr32(dev, 0x400040, 0x80);
++ nv_wr32(dev, 0x400040, 0);
++ nv_wr32(dev, 0x401800, 0xc0000000);
++ nv_wr32(dev, 0x400108, 0x008);
++ status &= ~0x008;
++ }
++
++ /* CCACHE: Handles code and c[] caches and fills them. */
++ if (status & 0x010) {
++ ustatus = nv_rd32(dev, 0x405018) & 0x7fffffff;
++ if (!ustatus && display) {
++ NV_INFO(dev, "PGRAPH_TRAP_CCACHE - no ustatus?\n");
++ }
++ if (ustatus & 0x00000001) {
++ nv50_pfb_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),
++ nv_rd32(dev, 0x405808),
++ nv_rd32(dev, 0x40580c),
++ nv_rd32(dev, 0x405810),
++ nv_rd32(dev, 0x405814),
++ nv_rd32(dev, 0x40581c));
++ ustatus &= ~0x00000001;
++ }
++ if (ustatus && display)
++ NV_INFO(dev, "PGRAPH_TRAP_CCACHE - Unhandled ustatus 0x%08x\n", ustatus);
++ nv_wr32(dev, 0x405018, 0xc0000000);
++ nv_wr32(dev, 0x400108, 0x010);
++ status &= ~0x010;
++ }
++
++ /* Unknown, not seen yet... 0x402000 is the only trap status reg
++ * 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");
++ ustatus = nv_rd32(dev, 0x402000) & 0x7fffffff;
++ if (display)
++ NV_INFO(dev, "PGRAPH_TRAP_UNKC04 - Unhandled ustatus 0x%08x\n", ustatus);
++ nv_wr32(dev, 0x402000, 0xc0000000);
++ /* no status modifiction on purpose */
++ }
++
++ /* TEXTURE: CUDA texturing units */
++ if (status & 0x040) {
++ nv50_pgraph_tp_trap (dev, 6, 0x408900, 0x408600, display,
++ "PGRAPH_TRAP_TEXTURE");
++ nv_wr32(dev, 0x400108, 0x040);
++ status &= ~0x040;
++ }
++
++ /* MP: CUDA execution engines. */
++ if (status & 0x080) {
++ nv50_pgraph_tp_trap (dev, 7, 0x408314, 0x40831c, display,
++ "PGRAPH_TRAP_MP");
++ nv_wr32(dev, 0x400108, 0x080);
++ status &= ~0x080;
++ }
++
++ /* TPDMA: Handles TP-initiated uncached memory accesses:
++ * l[], g[], stack, 2d surfaces, render targets. */
++ if (status & 0x100) {
++ nv50_pgraph_tp_trap (dev, 8, 0x408e08, 0x408708, display,
++ "PGRAPH_TRAP_TPDMA");
++ nv_wr32(dev, 0x400108, 0x100);
++ status &= ~0x100;
++ }
++
++ if (status) {
++ if (display)
++ NV_INFO(dev, "PGRAPH_TRAP - Unknown trap 0x%08x\n",
++ status);
++ nv_wr32(dev, 0x400108, status);
++ }
++}
++
++/* There must be a *lot* of these. Will take some time to gather them up. */
++static struct nouveau_enum_names nv50_data_error_names[] =
++{
++ { 4, "INVALID_VALUE" },
++ { 5, "INVALID_ENUM" },
++ { 8, "INVALID_OBJECT" },
++ { 0xc, "INVALID_BITFIELD" },
++ { 0x28, "MP_NO_REG_SPACE" },
++ { 0x2b, "MP_BLOCK_SIZE_MISMATCH" },
++};
++
++static void
+ nv50_pgraph_irq_handler(struct drm_device *dev)
+ {
++ struct nouveau_pgraph_trap trap;
++ int unhandled = 0;
+ uint32_t status;
+
+ while ((status = nv_rd32(dev, NV03_PGRAPH_INTR))) {
+- uint32_t nsource = nv_rd32(dev, NV03_PGRAPH_NSOURCE);
+-
++ /* NOTIFY: You've set a NOTIFY an a command and it's done. */
+ if (status & 0x00000001) {
+- nouveau_pgraph_intr_notify(dev, nsource);
++ nouveau_graph_trap_info(dev, &trap);
++ if (nouveau_ratelimit())
++ nouveau_graph_dump_trap_info(dev,
++ "PGRAPH_NOTIFY", &trap);
+ status &= ~0x00000001;
+ nv_wr32(dev, NV03_PGRAPH_INTR, 0x00000001);
+ }
+
+- if (status & 0x00000010) {
+- nouveau_pgraph_intr_error(dev, nsource |
+- NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD);
++ /* COMPUTE_QUERY: Purpose and exact cause unknown, happens
++ * when you write 0x200 to 0x50c0 method 0x31c. */
++ if (status & 0x00000002) {
++ nouveau_graph_trap_info(dev, &trap);
++ if (nouveau_ratelimit())
++ nouveau_graph_dump_trap_info(dev,
++ "PGRAPH_COMPUTE_QUERY", &trap);
++ status &= ~0x00000002;
++ nv_wr32(dev, NV03_PGRAPH_INTR, 0x00000002);
++ }
++
++ /* Unknown, never seen: 0x4 */
+
++ /* ILLEGAL_MTHD: You used a wrong method for this class. */
++ if (status & 0x00000010) {
++ nouveau_graph_trap_info(dev, &trap);
++ if (nouveau_pgraph_intr_swmthd(dev, &trap))
++ unhandled = 1;
++ if (unhandled && nouveau_ratelimit())
++ nouveau_graph_dump_trap_info(dev,
++ "PGRAPH_ILLEGAL_MTHD", &trap);
+ status &= ~0x00000010;
+ nv_wr32(dev, NV03_PGRAPH_INTR, 0x00000010);
+ }
+
++ /* ILLEGAL_CLASS: You used a wrong class. */
++ if (status & 0x00000020) {
++ nouveau_graph_trap_info(dev, &trap);
++ if (nouveau_ratelimit())
++ nouveau_graph_dump_trap_info(dev,
++ "PGRAPH_ILLEGAL_CLASS", &trap);
++ status &= ~0x00000020;
++ nv_wr32(dev, NV03_PGRAPH_INTR, 0x00000020);
++ }
++
++ /* DOUBLE_NOTIFY: You tried to set a NOTIFY on another NOTIFY. */
++ if (status & 0x00000040) {
++ nouveau_graph_trap_info(dev, &trap);
++ if (nouveau_ratelimit())
++ nouveau_graph_dump_trap_info(dev,
++ "PGRAPH_DOUBLE_NOTIFY", &trap);
++ status &= ~0x00000040;
++ nv_wr32(dev, NV03_PGRAPH_INTR, 0x00000040);
++ }
++
++ /* CONTEXT_SWITCH: PGRAPH needs us to load a new context */
+ if (status & 0x00001000) {
+ nv_wr32(dev, 0x400500, 0x00000000);
+ nv_wr32(dev, NV03_PGRAPH_INTR,
+@@ -613,49 +1116,59 @@ nv50_pgraph_irq_handler(struct drm_device *dev)
+ status &= ~NV_PGRAPH_INTR_CONTEXT_SWITCH;
+ }
+
+- if (status & 0x00100000) {
+- nouveau_pgraph_intr_error(dev, nsource |
+- NV03_PGRAPH_NSOURCE_DATA_ERROR);
++ /* BUFFER_NOTIFY: Your m2mf transfer finished */
++ if (status & 0x00010000) {
++ nouveau_graph_trap_info(dev, &trap);
++ if (nouveau_ratelimit())
++ nouveau_graph_dump_trap_info(dev,
++ "PGRAPH_BUFFER_NOTIFY", &trap);
++ status &= ~0x00010000;
++ nv_wr32(dev, NV03_PGRAPH_INTR, 0x00010000);
++ }
+
++ /* DATA_ERROR: Invalid value for this method, or invalid
++ * state in current PGRAPH context for this operation */
++ if (status & 0x00100000) {
++ nouveau_graph_trap_info(dev, &trap);
++ if (nouveau_ratelimit()) {
++ nouveau_graph_dump_trap_info(dev,
++ "PGRAPH_DATA_ERROR", &trap);
++ NV_INFO (dev, "PGRAPH_DATA_ERROR - ");
++ nouveau_print_enum_names(nv_rd32(dev, 0x400110),
++ nv50_data_error_names);
++ printk("\n");
++ }
+ status &= ~0x00100000;
+ nv_wr32(dev, NV03_PGRAPH_INTR, 0x00100000);
+ }
+
++ /* TRAP: Something bad happened in the middle of command
++ * execution. Has a billion types, subtypes, and even
++ * subsubtypes. */
+ if (status & 0x00200000) {
+- int r;
+-
+- nouveau_pgraph_intr_error(dev, nsource |
+- NV03_PGRAPH_NSOURCE_PROTECTION_ERROR);
+-
+- NV_ERROR(dev, "magic set 1:\n");
+- for (r = 0x408900; r <= 0x408910; r += 4)
+- NV_ERROR(dev, "\t0x%08x: 0x%08x\n", r,
+- nv_rd32(dev, r));
+- nv_wr32(dev, 0x408900,
+- nv_rd32(dev, 0x408904) | 0xc0000000);
+- for (r = 0x408e08; r <= 0x408e24; r += 4)
+- NV_ERROR(dev, "\t0x%08x: 0x%08x\n", r,
+- nv_rd32(dev, r));
+- nv_wr32(dev, 0x408e08,
+- nv_rd32(dev, 0x408e08) | 0xc0000000);
+-
+- NV_ERROR(dev, "magic set 2:\n");
+- for (r = 0x409900; r <= 0x409910; r += 4)
+- NV_ERROR(dev, "\t0x%08x: 0x%08x\n", r,
+- nv_rd32(dev, r));
+- nv_wr32(dev, 0x409900,
+- nv_rd32(dev, 0x409904) | 0xc0000000);
+- for (r = 0x409e08; r <= 0x409e24; r += 4)
+- NV_ERROR(dev, "\t0x%08x: 0x%08x\n", r,
+- nv_rd32(dev, r));
+- nv_wr32(dev, 0x409e08,
+- nv_rd32(dev, 0x409e08) | 0xc0000000);
+-
++ nv50_pgraph_trap_handler(dev);
+ status &= ~0x00200000;
+- nv_wr32(dev, NV03_PGRAPH_NSOURCE, nsource);
+ nv_wr32(dev, NV03_PGRAPH_INTR, 0x00200000);
+ }
+
++ /* Unknown, never seen: 0x00400000 */
++
++ /* SINGLE_STEP: Happens on every method if you turned on
++ * single stepping in 40008c */
++ if (status & 0x01000000) {
++ nouveau_graph_trap_info(dev, &trap);
++ if (nouveau_ratelimit())
++ nouveau_graph_dump_trap_info(dev,
++ "PGRAPH_SINGLE_STEP", &trap);
++ status &= ~0x01000000;
++ nv_wr32(dev, NV03_PGRAPH_INTR, 0x01000000);
++ }
++
++ /* 0x02000000 happens when you pause a ctxprog...
++ * but the only way this can happen that I know is by
++ * poking the relevant MMIO register, and we don't
++ * do that. */
++
+ if (status) {
+ NV_INFO(dev, "Unhandled PGRAPH_INTR - 0x%08x\n",
+ status);
+@@ -672,7 +1185,8 @@ nv50_pgraph_irq_handler(struct drm_device *dev)
+ }
+
+ nv_wr32(dev, NV03_PMC_INTR_0, NV_PMC_INTR_0_PGRAPH_PENDING);
+- nv_wr32(dev, 0x400824, nv_rd32(dev, 0x400824) & ~(1 << 31));
++ if (nv_rd32(dev, 0x400824) & (1 << 31))
++ nv_wr32(dev, 0x400824, nv_rd32(dev, 0x400824) & ~(1 << 31));
+ }
+
+ static void
+@@ -691,11 +1205,14 @@ nouveau_irq_handler(DRM_IRQ_ARGS)
+ struct drm_device *dev = (struct drm_device *)arg;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ uint32_t status, fbdev_flags = 0;
++ unsigned long flags;
+
+ status = nv_rd32(dev, NV03_PMC_INTR_0);
+ if (!status)
+ return IRQ_NONE;
+
++ spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
++
+ if (dev_priv->fbdev_info) {
+ fbdev_flags = dev_priv->fbdev_info->flags;
+ dev_priv->fbdev_info->flags |= FBINFO_HWACCEL_DISABLED;
+@@ -733,5 +1250,7 @@ nouveau_irq_handler(DRM_IRQ_ARGS)
+ if (dev_priv->fbdev_info)
+ dev_priv->fbdev_info->flags = fbdev_flags;
+
++ spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
++
+ return IRQ_HANDLED;
+ }
+diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c
+index 2dc09db..775a701 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_mem.c
++++ b/drivers/gpu/drm/nouveau/nouveau_mem.c
+@@ -347,6 +347,20 @@ nv50_mem_vm_bind_linear(struct drm_device *dev, uint64_t virt, uint32_t size,
+ return -EBUSY;
+ }
+
++ nv_wr32(dev, 0x100c80, 0x00040001);
++ if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
++ NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
++ NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
++ return -EBUSY;
++ }
++
++ nv_wr32(dev, 0x100c80, 0x00060001);
++ if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
++ NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
++ NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
++ return -EBUSY;
++ }
++
+ return 0;
+ }
+
+@@ -387,6 +401,20 @@ nv50_mem_vm_unbind(struct drm_device *dev, uint64_t virt, uint32_t size)
+ if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
+ NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
+ NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
++ return;
++ }
++
++ nv_wr32(dev, 0x100c80, 0x00040001);
++ if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
++ NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
++ NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
++ return;
++ }
++
++ nv_wr32(dev, 0x100c80, 0x00060001);
++ if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
++ NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
++ NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
+ }
+ }
+
+@@ -449,9 +477,30 @@ void nouveau_mem_close(struct drm_device *dev)
+ }
+ }
+
+-/*XXX won't work on BSD because of pci_read_config_dword */
+ static uint32_t
+-nouveau_mem_fb_amount_igp(struct drm_device *dev)
++nouveau_mem_detect_nv04(struct drm_device *dev)
++{
++ uint32_t boot0 = nv_rd32(dev, NV03_BOOT_0);
++
++ if (boot0 & 0x00000100)
++ return (((boot0 >> 12) & 0xf) * 2 + 2) * 1024 * 1024;
++
++ switch (boot0 & NV03_BOOT_0_RAM_AMOUNT) {
++ case NV04_BOOT_0_RAM_AMOUNT_32MB:
++ return 32 * 1024 * 1024;
++ case NV04_BOOT_0_RAM_AMOUNT_16MB:
++ return 16 * 1024 * 1024;
++ case NV04_BOOT_0_RAM_AMOUNT_8MB:
++ return 8 * 1024 * 1024;
++ case NV04_BOOT_0_RAM_AMOUNT_4MB:
++ return 4 * 1024 * 1024;
++ }
++
++ return 0;
++}
++
++static uint32_t
++nouveau_mem_detect_nforce(struct drm_device *dev)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct pci_dev *bridge;
+@@ -463,11 +512,11 @@ nouveau_mem_fb_amount_igp(struct drm_device *dev)
+ return 0;
+ }
+
+- if (dev_priv->flags&NV_NFORCE) {
++ if (dev_priv->flags & NV_NFORCE) {
+ pci_read_config_dword(bridge, 0x7C, &mem);
+ return (uint64_t)(((mem >> 6) & 31) + 1)*1024*1024;
+ } else
+- if (dev_priv->flags&NV_NFORCE2) {
++ if (dev_priv->flags & NV_NFORCE2) {
+ pci_read_config_dword(bridge, 0x84, &mem);
+ return (uint64_t)(((mem >> 4) & 127) + 1)*1024*1024;
+ }
+@@ -477,50 +526,32 @@ nouveau_mem_fb_amount_igp(struct drm_device *dev)
+ }
+
+ /* returns the amount of FB ram in bytes */
+-uint64_t nouveau_mem_fb_amount(struct drm_device *dev)
++int
++nouveau_mem_detect(struct drm_device *dev)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- uint32_t boot0;
+-
+- switch (dev_priv->card_type) {
+- case NV_04:
+- boot0 = nv_rd32(dev, NV03_BOOT_0);
+- if (boot0 & 0x00000100)
+- return (((boot0 >> 12) & 0xf) * 2 + 2) * 1024 * 1024;
+-
+- switch (boot0 & NV03_BOOT_0_RAM_AMOUNT) {
+- case NV04_BOOT_0_RAM_AMOUNT_32MB:
+- return 32 * 1024 * 1024;
+- case NV04_BOOT_0_RAM_AMOUNT_16MB:
+- return 16 * 1024 * 1024;
+- case NV04_BOOT_0_RAM_AMOUNT_8MB:
+- return 8 * 1024 * 1024;
+- case NV04_BOOT_0_RAM_AMOUNT_4MB:
+- return 4 * 1024 * 1024;
+- }
+- break;
+- case NV_10:
+- case NV_20:
+- case NV_30:
+- case NV_40:
+- case NV_50:
+- default:
+- if (dev_priv->flags & (NV_NFORCE | NV_NFORCE2)) {
+- return nouveau_mem_fb_amount_igp(dev);
+- } else {
+- uint64_t mem;
+- mem = (nv_rd32(dev, NV04_FIFO_DATA) &
+- NV10_FIFO_DATA_RAM_AMOUNT_MB_MASK) >>
+- NV10_FIFO_DATA_RAM_AMOUNT_MB_SHIFT;
+- return mem * 1024 * 1024;
+- }
+- break;
++
++ if (dev_priv->card_type == NV_04) {
++ dev_priv->vram_size = nouveau_mem_detect_nv04(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)
++ dev_priv->vram_sys_base = nv_rd32(dev, 0x100e10) << 12;
+ }
+
+- NV_ERROR(dev,
+- "Unable to detect video ram size. Please report your setup to "
+- DRIVER_EMAIL "\n");
+- return 0;
++ NV_INFO(dev, "Detected %dMiB VRAM\n", (int)(dev_priv->vram_size >> 20));
++ if (dev_priv->vram_sys_base) {
++ NV_INFO(dev, "Stolen system memory at: 0x%010llx\n",
++ dev_priv->vram_sys_base);
++ }
++
++ if (dev_priv->vram_size)
++ return 0;
++ return -ENOMEM;
+ }
+
+ #if __OS_HAS_AGP
+@@ -631,15 +662,12 @@ nouveau_mem_init(struct drm_device *dev)
+ spin_lock_init(&dev_priv->ttm.bo_list_lock);
+ spin_lock_init(&dev_priv->tile.lock);
+
+- dev_priv->fb_available_size = nouveau_mem_fb_amount(dev);
+-
++ 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;
+
+- NV_INFO(dev, "%d MiB VRAM\n", (int)(dev_priv->fb_available_size >> 20));
+-
+ /* remove reserved space at end of vram from available amount */
+ dev_priv->fb_available_size -= dev_priv->ramin_rsvd_vram;
+ dev_priv->fb_aper_free = dev_priv->fb_available_size;
+diff --git a/drivers/gpu/drm/nouveau/nouveau_reg.h b/drivers/gpu/drm/nouveau/nouveau_reg.h
+index aa9b310..6ca80a3 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_reg.h
++++ b/drivers/gpu/drm/nouveau/nouveau_reg.h
+@@ -826,6 +826,7 @@
+ #define NV50_SOR_DP_CTRL_TRAINING_PATTERN_2 0x02000000
+ #define NV50_SOR_DP_UNK118(i,l) (0x0061c118 + (i) * 0x800 + (l) * 0x80)
+ #define NV50_SOR_DP_UNK120(i,l) (0x0061c120 + (i) * 0x800 + (l) * 0x80)
++#define NV50_SOR_DP_UNK128(i,l) (0x0061c128 + (i) * 0x800 + (l) * 0x80)
+ #define NV50_SOR_DP_UNK130(i,l) (0x0061c130 + (i) * 0x800 + (l) * 0x80)
+
+ #define NV50_PDISPLAY_USER(i) ((i) * 0x1000 + 0x00640000)
+diff --git a/drivers/gpu/drm/nouveau/nouveau_sgdma.c b/drivers/gpu/drm/nouveau/nouveau_sgdma.c
+index ed15905..554fb45 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_sgdma.c
++++ b/drivers/gpu/drm/nouveau/nouveau_sgdma.c
+@@ -171,6 +171,24 @@ nouveau_sgdma_unbind(struct ttm_backend *be)
+ }
+ dev_priv->engine.instmem.finish_access(nvbe->dev);
+
++ if (dev_priv->card_type == NV_50) {
++ nv_wr32(dev, 0x100c80, 0x00050001);
++ if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
++ NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
++ NV_ERROR(dev, "0x100c80 = 0x%08x\n",
++ nv_rd32(dev, 0x100c80));
++ return -EBUSY;
++ }
++
++ nv_wr32(dev, 0x100c80, 0x00000001);
++ if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
++ NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
++ NV_ERROR(dev, "0x100c80 = 0x%08x\n",
++ nv_rd32(dev, 0x100c80));
++ return -EBUSY;
++ }
++ }
++
+ nvbe->bound = false;
+ return 0;
+ }
+diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
+index a8d77c8..a11cd7f 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_state.c
++++ b/drivers/gpu/drm/nouveau/nouveau_state.c
+@@ -34,7 +34,6 @@
+ #include "nouveau_drm.h"
+ #include "nv50_display.h"
+
+-static int nouveau_stub_init(struct drm_device *dev) { return 0; }
+ static void nouveau_stub_takedown(struct drm_device *dev) {}
+
+ static int nouveau_init_engine_ptrs(struct drm_device *dev)
+@@ -276,8 +275,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
+ engine->timer.init = nv04_timer_init;
+ engine->timer.read = nv04_timer_read;
+ engine->timer.takedown = nv04_timer_takedown;
+- engine->fb.init = nouveau_stub_init;
+- engine->fb.takedown = nouveau_stub_takedown;
++ engine->fb.init = nv50_fb_init;
++ engine->fb.takedown = nv50_fb_takedown;
+ engine->graph.grclass = nv50_graph_grclass;
+ engine->graph.init = nv50_graph_init;
+ engine->graph.takedown = nv50_graph_takedown;
+@@ -340,7 +339,7 @@ nouveau_card_init_channel(struct drm_device *dev)
+
+ gpuobj = NULL;
+ ret = nouveau_gpuobj_dma_new(dev_priv->channel, NV_CLASS_DMA_IN_MEMORY,
+- 0, nouveau_mem_fb_amount(dev),
++ 0, dev_priv->vram_size,
+ NV_DMA_ACCESS_RW, NV_DMA_TARGET_VIDMEM,
+ &gpuobj);
+ if (ret)
+@@ -391,6 +390,7 @@ nouveau_card_init(struct drm_device *dev)
+ goto out;
+ engine = &dev_priv->engine;
+ dev_priv->init_state = NOUVEAU_CARD_INIT_FAILED;
++ spin_lock_init(&dev_priv->context_switch_lock);
+
+ /* Parse BIOS tables / Run init tables if card not POSTed */
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+@@ -399,6 +399,10 @@ nouveau_card_init(struct drm_device *dev)
+ goto out;
+ }
+
++ ret = nouveau_mem_detect(dev);
++ if (ret)
++ goto out_bios;
++
+ ret = nouveau_gpuobj_early_init(dev);
+ if (ret)
+ goto out_bios;
+@@ -474,7 +478,7 @@ nouveau_card_init(struct drm_device *dev)
+ else
+ ret = nv04_display_create(dev);
+ if (ret)
+- goto out_irq;
++ goto out_channel;
+ }
+
+ ret = nouveau_backlight_init(dev);
+@@ -488,6 +492,11 @@ nouveau_card_init(struct drm_device *dev)
+
+ return 0;
+
++out_channel:
++ if (dev_priv->channel) {
++ nouveau_channel_free(dev_priv->channel);
++ dev_priv->channel = NULL;
++ }
+ out_irq:
+ drm_irq_uninstall(dev);
+ out_fifo:
+@@ -505,6 +514,7 @@ out_mc:
+ out_gpuobj:
+ nouveau_gpuobj_takedown(dev);
+ out_mem:
++ nouveau_sgdma_takedown(dev);
+ nouveau_mem_close(dev);
+ out_instmem:
+ engine->instmem.takedown(dev);
+diff --git a/drivers/gpu/drm/nouveau/nv04_crtc.c b/drivers/gpu/drm/nouveau/nv04_crtc.c
+index d2f143e..9986aba 100644
+--- a/drivers/gpu/drm/nouveau/nv04_crtc.c
++++ b/drivers/gpu/drm/nouveau/nv04_crtc.c
+@@ -230,9 +230,9 @@ nv_crtc_mode_set_vga(struct drm_crtc *crtc, struct drm_display_mode *mode)
+ struct drm_framebuffer *fb = crtc->fb;
+
+ /* Calculate our timings */
+- int horizDisplay = (mode->crtc_hdisplay >> 3) - 1;
+- int horizStart = (mode->crtc_hsync_start >> 3) - 1;
+- int horizEnd = (mode->crtc_hsync_end >> 3) - 1;
++ int horizDisplay = (mode->crtc_hdisplay >> 3) - 1;
++ int horizStart = (mode->crtc_hsync_start >> 3) + 1;
++ int horizEnd = (mode->crtc_hsync_end >> 3) + 1;
+ int horizTotal = (mode->crtc_htotal >> 3) - 5;
+ int horizBlankStart = (mode->crtc_hdisplay >> 3) - 1;
+ int horizBlankEnd = (mode->crtc_htotal >> 3) - 1;
+diff --git a/drivers/gpu/drm/nouveau/nv04_dac.c b/drivers/gpu/drm/nouveau/nv04_dac.c
+index 1d73b15..1cb19e3 100644
+--- a/drivers/gpu/drm/nouveau/nv04_dac.c
++++ b/drivers/gpu/drm/nouveau/nv04_dac.c
+@@ -230,13 +230,13 @@ uint32_t nv17_dac_sample_load(struct drm_encoder *encoder)
+ if (dcb->type == OUTPUT_TV) {
+ testval = RGB_TEST_DATA(0xa0, 0xa0, 0xa0);
+
+- if (dev_priv->vbios->tvdactestval)
+- testval = dev_priv->vbios->tvdactestval;
++ if (dev_priv->vbios.tvdactestval)
++ testval = dev_priv->vbios.tvdactestval;
+ } else {
+ testval = RGB_TEST_DATA(0x140, 0x140, 0x140); /* 0x94050140 */
+
+- if (dev_priv->vbios->dactestval)
+- testval = dev_priv->vbios->dactestval;
++ if (dev_priv->vbios.dactestval)
++ testval = dev_priv->vbios.dactestval;
+ }
+
+ saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset);
+diff --git a/drivers/gpu/drm/nouveau/nv04_dfp.c b/drivers/gpu/drm/nouveau/nv04_dfp.c
+index 483f875..41634d4 100644
+--- a/drivers/gpu/drm/nouveau/nv04_dfp.c
++++ b/drivers/gpu/drm/nouveau/nv04_dfp.c
+@@ -269,10 +269,10 @@ static void nv04_dfp_mode_set(struct drm_encoder *encoder,
+ regp->fp_horiz_regs[FP_TOTAL] = output_mode->htotal - 1;
+ if (!nv_gf4_disp_arch(dev) ||
+ (output_mode->hsync_start - output_mode->hdisplay) >=
+- dev_priv->vbios->digital_min_front_porch)
++ dev_priv->vbios.digital_min_front_porch)
+ regp->fp_horiz_regs[FP_CRTC] = output_mode->hdisplay;
+ else
+- regp->fp_horiz_regs[FP_CRTC] = output_mode->hsync_start - dev_priv->vbios->digital_min_front_porch - 1;
++ regp->fp_horiz_regs[FP_CRTC] = output_mode->hsync_start - dev_priv->vbios.digital_min_front_porch - 1;
+ regp->fp_horiz_regs[FP_SYNC_START] = output_mode->hsync_start - 1;
+ regp->fp_horiz_regs[FP_SYNC_END] = output_mode->hsync_end - 1;
+ regp->fp_horiz_regs[FP_VALID_START] = output_mode->hskew;
+diff --git a/drivers/gpu/drm/nouveau/nv04_display.c b/drivers/gpu/drm/nouveau/nv04_display.c
+index ef77215..c7898b4 100644
+--- a/drivers/gpu/drm/nouveau/nv04_display.c
++++ b/drivers/gpu/drm/nouveau/nv04_display.c
+@@ -93,10 +93,9 @@ int
+ nv04_display_create(struct drm_device *dev)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct parsed_dcb *dcb = dev_priv->vbios->dcb;
++ struct dcb_table *dcb = &dev_priv->vbios.dcb;
+ struct drm_encoder *encoder;
+ struct drm_crtc *crtc;
+- uint16_t connector[16] = { 0 };
+ int i, ret;
+
+ NV_DEBUG_KMS(dev, "\n");
+@@ -154,52 +153,10 @@ nv04_display_create(struct drm_device *dev)
+
+ if (ret)
+ continue;
+-
+- connector[dcbent->connector] |= (1 << dcbent->type);
+ }
+
+- for (i = 0; i < dcb->entries; i++) {
+- struct dcb_entry *dcbent = &dcb->entry[i];
+- uint16_t encoders;
+- int type;
+-
+- encoders = connector[dcbent->connector];
+- if (!(encoders & (1 << dcbent->type)))
+- continue;
+- connector[dcbent->connector] = 0;
+-
+- switch (dcbent->type) {
+- case OUTPUT_ANALOG:
+- if (!MULTIPLE_ENCODERS(encoders))
+- type = DRM_MODE_CONNECTOR_VGA;
+- else
+- type = DRM_MODE_CONNECTOR_DVII;
+- break;
+- case OUTPUT_TMDS:
+- if (!MULTIPLE_ENCODERS(encoders))
+- type = DRM_MODE_CONNECTOR_DVID;
+- else
+- type = DRM_MODE_CONNECTOR_DVII;
+- break;
+- case OUTPUT_LVDS:
+- type = DRM_MODE_CONNECTOR_LVDS;
+-#if 0
+- /* don't create i2c adapter when lvds ddc not allowed */
+- if (dcbent->lvdsconf.use_straps_for_mode ||
+- dev_priv->vbios->fp_no_ddc)
+- i2c_index = 0xf;
+-#endif
+- break;
+- case OUTPUT_TV:
+- type = DRM_MODE_CONNECTOR_TV;
+- break;
+- default:
+- type = DRM_MODE_CONNECTOR_Unknown;
+- continue;
+- }
+-
+- nouveau_connector_create(dev, dcbent->connector, type);
+- }
++ for (i = 0; i < dcb->connector.entries; i++)
++ nouveau_connector_create(dev, &dcb->connector.entry[i]);
+
+ /* Save previous state */
+ NVLockVgaCrtcs(dev, false);
+diff --git a/drivers/gpu/drm/nouveau/nv04_fbcon.c b/drivers/gpu/drm/nouveau/nv04_fbcon.c
+index fd01caa..813b25c 100644
+--- a/drivers/gpu/drm/nouveau/nv04_fbcon.c
++++ b/drivers/gpu/drm/nouveau/nv04_fbcon.c
+@@ -118,8 +118,8 @@ nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
+ return;
+ }
+
+- width = (image->width + 31) & ~31;
+- dsize = (width * image->height) >> 5;
++ width = ALIGN(image->width, 8);
++ dsize = ALIGN(width * image->height, 32) >> 5;
+
+ if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
+ info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
+@@ -136,8 +136,8 @@ nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
+ ((image->dx + image->width) & 0xffff));
+ OUT_RING(chan, bg);
+ OUT_RING(chan, fg);
+- OUT_RING(chan, (image->height << 16) | image->width);
+ OUT_RING(chan, (image->height << 16) | width);
++ OUT_RING(chan, (image->height << 16) | image->width);
+ OUT_RING(chan, (image->dy << 16) | (image->dx & 0xffff));
+
+ while (dsize) {
+diff --git a/drivers/gpu/drm/nouveau/nv04_fifo.c b/drivers/gpu/drm/nouveau/nv04_fifo.c
+index f31347b..66fe559 100644
+--- a/drivers/gpu/drm/nouveau/nv04_fifo.c
++++ b/drivers/gpu/drm/nouveau/nv04_fifo.c
+@@ -117,6 +117,7 @@ nv04_fifo_create_context(struct nouveau_channel *chan)
+ {
+ struct drm_device *dev = chan->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ unsigned long flags;
+ int ret;
+
+ ret = nouveau_gpuobj_new_fake(dev, NV04_RAMFC(chan->id), ~0,
+@@ -127,6 +128,8 @@ nv04_fifo_create_context(struct nouveau_channel *chan)
+ 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);
+@@ -144,6 +147,8 @@ nv04_fifo_create_context(struct nouveau_channel *chan)
+ /* enable the fifo dma operation */
+ nv_wr32(dev, NV04_PFIFO_MODE,
+ nv_rd32(dev, NV04_PFIFO_MODE) | (1 << chan->id));
++
++ spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
+ return 0;
+ }
+
+diff --git a/drivers/gpu/drm/nouveau/nv04_tv.c b/drivers/gpu/drm/nouveau/nv04_tv.c
+index 9c63099..c4e3404 100644
+--- a/drivers/gpu/drm/nouveau/nv04_tv.c
++++ b/drivers/gpu/drm/nouveau/nv04_tv.c
+@@ -262,7 +262,7 @@ int nv04_tv_create(struct drm_device *dev, struct dcb_entry *entry)
+ nv_encoder->or = ffs(entry->or) - 1;
+
+ /* Run the slave-specific initialization */
+- adap = &dev_priv->vbios->dcb->i2c[i2c_index].chan->adapter;
++ adap = &dev_priv->vbios.dcb.i2c[i2c_index].chan->adapter;
+
+ was_locked = NVLockVgaCrtcs(dev, false);
+
+diff --git a/drivers/gpu/drm/nouveau/nv17_tv.c b/drivers/gpu/drm/nouveau/nv17_tv.c
+index 21ac6e4..74c8803 100644
+--- a/drivers/gpu/drm/nouveau/nv17_tv.c
++++ b/drivers/gpu/drm/nouveau/nv17_tv.c
+@@ -45,8 +45,8 @@ static uint32_t nv42_tv_sample_load(struct drm_encoder *encoder)
+
+ #define RGB_TEST_DATA(r, g, b) (r << 0 | g << 10 | b << 20)
+ testval = RGB_TEST_DATA(0x82, 0xeb, 0x82);
+- if (dev_priv->vbios->tvdactestval)
+- testval = dev_priv->vbios->tvdactestval;
++ if (dev_priv->vbios.tvdactestval)
++ testval = dev_priv->vbios.tvdactestval;
+
+ dacclk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset);
+ head = (dacclk & 0x100) >> 8;
+@@ -367,7 +367,7 @@ static void nv17_tv_prepare(struct drm_encoder *encoder)
+ !enc->crtc &&
+ nv04_dfp_get_bound_head(dev, dcb) == head) {
+ nv04_dfp_bind_head(dev, dcb, head ^ 1,
+- dev_priv->VBIOS.fp.dual_link);
++ dev_priv->vbios.fp.dual_link);
+ }
+ }
+
+diff --git a/drivers/gpu/drm/nouveau/nv40_fifo.c b/drivers/gpu/drm/nouveau/nv40_fifo.c
+index b4f19cc..500ccfd 100644
+--- a/drivers/gpu/drm/nouveau/nv40_fifo.c
++++ b/drivers/gpu/drm/nouveau/nv40_fifo.c
+@@ -37,6 +37,7 @@ nv40_fifo_create_context(struct nouveau_channel *chan)
+ struct drm_device *dev = chan->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ uint32_t fc = NV40_RAMFC(chan->id);
++ unsigned long flags;
+ int ret;
+
+ ret = nouveau_gpuobj_new_fake(dev, NV40_RAMFC(chan->id), ~0,
+@@ -45,6 +46,8 @@ nv40_fifo_create_context(struct nouveau_channel *chan)
+ 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);
+@@ -63,6 +66,8 @@ nv40_fifo_create_context(struct nouveau_channel *chan)
+ /* enable the fifo dma operation */
+ nv_wr32(dev, NV04_PFIFO_MODE,
+ nv_rd32(dev, NV04_PFIFO_MODE) | (1 << chan->id));
++
++ spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
+ return 0;
+ }
+
+@@ -273,7 +278,7 @@ nv40_fifo_init_ramxx(struct drm_device *dev)
+ default:
+ nv_wr32(dev, 0x2230, 0);
+ nv_wr32(dev, NV40_PFIFO_RAMFC,
+- ((nouveau_mem_fb_amount(dev) - 512 * 1024 +
++ ((dev_priv->vram_size - 512 * 1024 +
+ dev_priv->ramfc_offset) >> 16) | (3 << 16));
+ break;
+ }
+diff --git a/drivers/gpu/drm/nouveau/nv40_graph.c b/drivers/gpu/drm/nouveau/nv40_graph.c
+index 53e8afe..0616c96 100644
+--- a/drivers/gpu/drm/nouveau/nv40_graph.c
++++ b/drivers/gpu/drm/nouveau/nv40_graph.c
+@@ -335,6 +335,27 @@ nv40_graph_init(struct drm_device *dev)
+ nv_wr32(dev, 0x400b38, 0x2ffff800);
+ nv_wr32(dev, 0x400b3c, 0x00006000);
+
++ /* Tiling related stuff. */
++ switch (dev_priv->chipset) {
++ case 0x44:
++ case 0x4a:
++ nv_wr32(dev, 0x400bc4, 0x1003d888);
++ nv_wr32(dev, 0x400bbc, 0xb7a7b500);
++ break;
++ case 0x46:
++ nv_wr32(dev, 0x400bc4, 0x0000e024);
++ nv_wr32(dev, 0x400bbc, 0xb7a7b520);
++ break;
++ case 0x4c:
++ case 0x4e:
++ case 0x67:
++ nv_wr32(dev, 0x400bc4, 0x1003d888);
++ nv_wr32(dev, 0x400bbc, 0xb7a7b540);
++ break;
++ default:
++ break;
++ }
++
+ /* Turn all the tiling regions off. */
+ for (i = 0; i < pfb->num_tiles; i++)
+ nv40_graph_set_region_tiling(dev, i, 0, 0, 0);
+diff --git a/drivers/gpu/drm/nouveau/nv50_calc.c b/drivers/gpu/drm/nouveau/nv50_calc.c
+new file mode 100644
+index 0000000..90f3f6b
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv50_calc.c
+@@ -0,0 +1,88 @@
++/*
++ * 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 "drm_fixed.h"
++#include "nouveau_drv.h"
++#include "nouveau_hw.h"
++
++int
++nv50_calc_pll(struct drm_device *dev, struct pll_lims *pll, int clk,
++ int *N1, int *M1, int *N2, int *M2, int *P)
++{
++ struct nouveau_pll_vals pll_vals;
++ int ret;
++
++ ret = nouveau_calc_pll_mnp(dev, pll, clk, &pll_vals);
++ if (ret <= 0)
++ return ret;
++
++ *N1 = pll_vals.N1;
++ *M1 = pll_vals.M1;
++ *N2 = pll_vals.N2;
++ *M2 = pll_vals.M2;
++ *P = pll_vals.log2P;
++ return ret;
++}
++
++int
++nv50_calc_pll2(struct drm_device *dev, struct pll_lims *pll, int clk,
++ int *N, int *fN, int *M, int *P)
++{
++ fixed20_12 fb_div, a, b;
++
++ *P = pll->vco1.maxfreq / clk;
++ if (*P > pll->max_p)
++ *P = pll->max_p;
++ if (*P < pll->min_p)
++ *P = pll->min_p;
++
++ /* *M = ceil(refclk / pll->vco.max_inputfreq); */
++ a.full = dfixed_const(pll->refclk);
++ b.full = dfixed_const(pll->vco1.max_inputfreq);
++ a.full = dfixed_div(a, b);
++ a.full = dfixed_ceil(a);
++ *M = dfixed_trunc(a);
++
++ /* fb_div = (vco * *M) / refclk; */
++ fb_div.full = dfixed_const(clk * *P);
++ fb_div.full = dfixed_mul(fb_div, a);
++ a.full = dfixed_const(pll->refclk);
++ fb_div.full = dfixed_div(fb_div, a);
++
++ /* *N = floor(fb_div); */
++ a.full = dfixed_floor(fb_div);
++ *N = dfixed_trunc(fb_div);
++
++ /* *fN = (fmod(fb_div, 1.0) * 8192) - 4096; */
++ b.full = dfixed_const(8192);
++ a.full = dfixed_mul(a, b);
++ fb_div.full = dfixed_mul(fb_div, b);
++ fb_div.full = fb_div.full - a.full;
++ *fN = dfixed_trunc(fb_div) - 4096;
++ *fN &= 0xffff;
++
++ return clk;
++}
++
+diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c
+index d1a651e..03d0e41 100644
+--- a/drivers/gpu/drm/nouveau/nv50_crtc.c
++++ b/drivers/gpu/drm/nouveau/nv50_crtc.c
+@@ -264,32 +264,40 @@ 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 pll_reg = NV50_PDISPLAY_CRTC_CLK_CTRL1(head);
+- struct nouveau_pll_vals pll;
+- struct pll_lims limits;
++ uint32_t reg = NV50_PDISPLAY_CRTC_CLK_CTRL1(head);
++ struct pll_lims pll;
+ uint32_t reg1, reg2;
+- int ret;
++ int ret, N1, M1, N2, M2, P;
+
+- ret = get_pll_limits(dev, pll_reg, &limits);
++ ret = get_pll_limits(dev, reg, &pll);
+ if (ret)
+ return ret;
+
+- ret = nouveau_calc_pll_mnp(dev, &limits, pclk, &pll);
+- if (ret <= 0)
+- return ret;
++ if (pll.vco2.maxfreq) {
++ ret = nv50_calc_pll(dev, &pll, pclk, &N1, &M1, &N2, &M2, &P);
++ if (ret <= 0)
++ return 0;
++
++ NV_DEBUG(dev, "pclk %d out %d NM1 %d %d NM2 %d %d P %d\n",
++ pclk, ret, N1, M1, N2, M2, P);
+
+- if (limits.vco2.maxfreq) {
+- reg1 = nv_rd32(dev, pll_reg + 4) & 0xff00ff00;
+- reg2 = nv_rd32(dev, pll_reg + 8) & 0x8000ff00;
+- nv_wr32(dev, pll_reg, 0x10000611);
+- nv_wr32(dev, pll_reg + 4, reg1 | (pll.M1 << 16) | pll.N1);
+- nv_wr32(dev, pll_reg + 8,
+- reg2 | (pll.log2P << 28) | (pll.M2 << 16) | pll.N2);
++ reg1 = nv_rd32(dev, reg + 4) & 0xff00ff00;
++ reg2 = nv_rd32(dev, reg + 8) & 0x8000ff00;
++ 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 {
+- reg1 = nv_rd32(dev, pll_reg + 4) & 0xffc00000;
+- nv_wr32(dev, pll_reg, 0x50000610);
+- nv_wr32(dev, pll_reg + 4, reg1 |
+- (pll.log2P << 16) | (pll.M1 << 8) | pll.N1);
++ 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);
++
++ reg1 = nv_rd32(dev, reg + 4) & 0xffc00000;
++ nv_wr32(dev, reg, 0x50000610);
++ nv_wr32(dev, reg + 4, reg1 | (P << 16) | (M1 << 8) | N1);
++ nv_wr32(dev, reg + 8, N2);
+ }
+
+ return 0;
+diff --git a/drivers/gpu/drm/nouveau/nv50_dac.c b/drivers/gpu/drm/nouveau/nv50_dac.c
+index f08f042..1fd9537 100644
+--- a/drivers/gpu/drm/nouveau/nv50_dac.c
++++ b/drivers/gpu/drm/nouveau/nv50_dac.c
+@@ -79,8 +79,8 @@ nv50_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector)
+ }
+
+ /* Use bios provided value if possible. */
+- if (dev_priv->vbios->dactestval) {
+- load_pattern = dev_priv->vbios->dactestval;
++ if (dev_priv->vbios.dactestval) {
++ load_pattern = dev_priv->vbios.dactestval;
+ NV_DEBUG_KMS(dev, "Using bios provided load_pattern of %d\n",
+ load_pattern);
+ } else {
+diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c
+index 90f0bf5..2c2ec5f 100644
+--- a/drivers/gpu/drm/nouveau/nv50_display.c
++++ b/drivers/gpu/drm/nouveau/nv50_display.c
+@@ -143,7 +143,7 @@ nv50_evo_channel_new(struct drm_device *dev, struct nouveau_channel **pchan)
+ }
+
+ ret = nv50_evo_dmaobj_new(chan, 0x3d, NvEvoVRAM, 0, 0x19,
+- 0, nouveau_mem_fb_amount(dev));
++ 0, dev_priv->vram_size);
+ if (ret) {
+ nv50_evo_channel_del(pchan);
+ return ret;
+@@ -231,7 +231,7 @@ nv50_display_init(struct drm_device *dev)
+ /* This used to be in crtc unblank, but seems out of place there. */
+ nv_wr32(dev, NV50_PDISPLAY_UNK_380, 0);
+ /* RAM is clamped to 256 MiB. */
+- ram_amount = nouveau_mem_fb_amount(dev);
++ ram_amount = dev_priv->vram_size;
+ NV_DEBUG_KMS(dev, "ram_amount %d\n", ram_amount);
+ if (ram_amount > 256*1024*1024)
+ ram_amount = 256*1024*1024;
+@@ -370,9 +370,7 @@ nv50_display_init(struct drm_device *dev)
+ struct nouveau_connector *conn = nouveau_connector(connector);
+ struct dcb_gpio_entry *gpio;
+
+- if (connector->connector_type != DRM_MODE_CONNECTOR_DVII &&
+- connector->connector_type != DRM_MODE_CONNECTOR_DVID &&
+- connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort)
++ if (conn->dcb->gpio_tag == 0xff)
+ continue;
+
+ gpio = nouveau_bios_gpio_entry(dev, conn->dcb->gpio_tag);
+@@ -465,8 +463,7 @@ static int nv50_display_disable(struct drm_device *dev)
+ int nv50_display_create(struct drm_device *dev)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct parsed_dcb *dcb = dev_priv->vbios->dcb;
+- uint32_t connector[16] = {};
++ struct dcb_table *dcb = &dev_priv->vbios.dcb;
+ int ret, i;
+
+ NV_DEBUG_KMS(dev, "\n");
+@@ -522,49 +519,20 @@ int nv50_display_create(struct drm_device *dev)
+ NV_WARN(dev, "DCB encoder %d unknown\n", entry->type);
+ continue;
+ }
+-
+- connector[entry->connector] |= (1 << entry->type);
+ }
+
+- /* It appears that DCB 3.0+ VBIOS has a connector table, however,
+- * I'm not 100% certain how to decode it correctly yet so just
+- * look at what encoders are present on each connector index and
+- * attempt to derive the connector type from that.
+- */
+- for (i = 0 ; i < dcb->entries; i++) {
+- struct dcb_entry *entry = &dcb->entry[i];
+- uint16_t encoders;
+- int type;
+-
+- encoders = connector[entry->connector];
+- if (!(encoders & (1 << entry->type)))
++ for (i = 0 ; i < dcb->connector.entries; i++) {
++ if (i != 0 && dcb->connector.entry[i].index2 ==
++ dcb->connector.entry[i - 1].index2)
+ continue;
+- connector[entry->connector] = 0;
+-
+- if (encoders & (1 << OUTPUT_DP)) {
+- type = DRM_MODE_CONNECTOR_DisplayPort;
+- } else if (encoders & (1 << OUTPUT_TMDS)) {
+- if (encoders & (1 << OUTPUT_ANALOG))
+- type = DRM_MODE_CONNECTOR_DVII;
+- else
+- type = DRM_MODE_CONNECTOR_DVID;
+- } else if (encoders & (1 << OUTPUT_ANALOG)) {
+- type = DRM_MODE_CONNECTOR_VGA;
+- } else if (encoders & (1 << OUTPUT_LVDS)) {
+- type = DRM_MODE_CONNECTOR_LVDS;
+- } else {
+- type = DRM_MODE_CONNECTOR_Unknown;
+- }
+-
+- if (type == DRM_MODE_CONNECTOR_Unknown)
+- continue;
+-
+- nouveau_connector_create(dev, entry->connector, type);
++ nouveau_connector_create(dev, &dcb->connector.entry[i]);
+ }
+
+ ret = nv50_display_init(dev);
+- if (ret)
++ if (ret) {
++ nv50_display_destroy(dev);
+ return ret;
++ }
+
+ return 0;
+ }
+@@ -667,8 +635,8 @@ nv50_display_irq_head(struct drm_device *dev, int *phead,
+ return -1;
+ }
+
+- for (i = 0; i < dev_priv->vbios->dcb->entries; i++) {
+- struct dcb_entry *dcbent = &dev_priv->vbios->dcb->entry[i];
++ for (i = 0; i < dev_priv->vbios.dcb.entries; i++) {
++ struct dcb_entry *dcbent = &dev_priv->vbios.dcb.entry[i];
+
+ if (dcbent->type != type)
+ continue;
+@@ -692,7 +660,7 @@ nv50_display_script_select(struct drm_device *dev, struct dcb_entry *dcbent,
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_connector *nv_connector = NULL;
+ struct drm_encoder *encoder;
+- struct nvbios *bios = &dev_priv->VBIOS;
++ struct nvbios *bios = &dev_priv->vbios;
+ uint32_t mc, script = 0, or;
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+@@ -710,7 +678,7 @@ nv50_display_script_select(struct drm_device *dev, struct dcb_entry *dcbent,
+ switch (dcbent->type) {
+ case OUTPUT_LVDS:
+ script = (mc >> 8) & 0xf;
+- if (bios->pub.fp_no_ddc) {
++ if (bios->fp_no_ddc) {
+ if (bios->fp.dual_link)
+ script |= 0x0100;
+ if (bios->fp.if_is_24bit)
+@@ -815,6 +783,37 @@ ack:
+ }
+
+ static void
++nv50_display_unk20_dp_hack(struct drm_device *dev, struct dcb_entry *dcb)
++{
++ int or = ffs(dcb->or) - 1, link = !(dcb->dpconf.sor.link & 1);
++ struct drm_encoder *encoder;
++ uint32_t tmp, unk0 = 0, unk1 = 0;
++
++ if (dcb->type != OUTPUT_DP)
++ return;
++
++ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++
++ if (nv_encoder->dcb == dcb) {
++ unk0 = nv_encoder->dp.unk0;
++ unk1 = nv_encoder->dp.unk1;
++ break;
++ }
++ }
++
++ if (unk0 || unk1) {
++ tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link));
++ tmp &= 0xfffffe03;
++ nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp | unk0);
++
++ tmp = nv_rd32(dev, NV50_SOR_DP_UNK128(or, link));
++ tmp &= 0xfef080c0;
++ nv_wr32(dev, NV50_SOR_DP_UNK128(or, link), tmp | unk1);
++ }
++}
++
++static void
+ nv50_display_unk20_handler(struct drm_device *dev)
+ {
+ struct dcb_entry *dcbent;
+@@ -837,6 +836,8 @@ nv50_display_unk20_handler(struct drm_device *dev)
+
+ nouveau_bios_run_display_table(dev, dcbent, script, pclk);
+
++ nv50_display_unk20_dp_hack(dev, dcbent);
++
+ tmp = nv_rd32(dev, NV50_PDISPLAY_CRTC_CLK_CTRL2(head));
+ tmp &= ~0x000000f;
+ nv_wr32(dev, NV50_PDISPLAY_CRTC_CLK_CTRL2(head), tmp);
+@@ -919,10 +920,12 @@ nv50_display_error_handler(struct drm_device *dev)
+ nv_wr32(dev, NV50_PDISPLAY_TRAPPED_ADDR, 0x90000000);
+ }
+
+-static void
+-nv50_display_irq_hotplug(struct drm_device *dev)
++void
++nv50_display_irq_hotplug_bh(struct work_struct *work)
+ {
+- struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct drm_nouveau_private *dev_priv =
++ container_of(work, struct drm_nouveau_private, hpd_work);
++ struct drm_device *dev = dev_priv->dev;
+ struct drm_connector *connector;
+ const uint32_t gpio_reg[4] = { 0xe104, 0xe108, 0xe280, 0xe284 };
+ uint32_t unplug_mask, plug_mask, change_mask;
+@@ -975,6 +978,8 @@ nv50_display_irq_hotplug(struct drm_device *dev)
+ nv_wr32(dev, 0xe054, nv_rd32(dev, 0xe054));
+ if (dev_priv->chipset >= 0x90)
+ nv_wr32(dev, 0xe074, nv_rd32(dev, 0xe074));
++
++ drm_sysfs_hotplug_event(dev);
+ }
+
+ void
+@@ -983,8 +988,10 @@ nv50_display_irq_handler(struct drm_device *dev)
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ uint32_t delayed = 0;
+
+- while (nv_rd32(dev, NV50_PMC_INTR_0) & NV50_PMC_INTR_0_HOTPLUG)
+- nv50_display_irq_hotplug(dev);
++ if (nv_rd32(dev, NV50_PMC_INTR_0) & NV50_PMC_INTR_0_HOTPLUG) {
++ if (!work_pending(&dev_priv->hpd_work))
++ queue_work(dev_priv->wq, &dev_priv->hpd_work);
++ }
+
+ while (nv_rd32(dev, NV50_PMC_INTR_0) & NV50_PMC_INTR_0_DISPLAY) {
+ uint32_t intr0 = nv_rd32(dev, NV50_PDISPLAY_INTR_0);
+diff --git a/drivers/gpu/drm/nouveau/nv50_display.h b/drivers/gpu/drm/nouveau/nv50_display.h
+index 3ae8d07..581d405 100644
+--- a/drivers/gpu/drm/nouveau/nv50_display.h
++++ b/drivers/gpu/drm/nouveau/nv50_display.h
+@@ -37,6 +37,7 @@
+
+ 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_create(struct drm_device *dev);
+ int nv50_display_destroy(struct drm_device *dev);
+diff --git a/drivers/gpu/drm/nouveau/nv50_fb.c b/drivers/gpu/drm/nouveau/nv50_fb.c
+new file mode 100644
+index 0000000..32611bd
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv50_fb.c
+@@ -0,0 +1,38 @@
++#include "drmP.h"
++#include "drm.h"
++#include "nouveau_drv.h"
++#include "nouveau_drm.h"
++
++int
++nv50_fb_init(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ /* Not a clue what this is exactly. Without pointing it at a
++ * scratch page, VRAM->GART blits with M2MF (as in DDX DFS)
++ * cause IOMMU "read from address 0" errors (rh#561267)
++ */
++ nv_wr32(dev, 0x100c08, dev_priv->gart_info.sg_dummy_bus >> 8);
++
++ /* This is needed to get meaningful information from 100c90
++ * on traps. No idea what these values mean exactly. */
++ switch (dev_priv->chipset) {
++ case 0x50:
++ nv_wr32(dev, 0x100c90, 0x0707ff);
++ break;
++ case 0xa5:
++ case 0xa8:
++ nv_wr32(dev, 0x100c90, 0x0d0fff);
++ break;
++ default:
++ nv_wr32(dev, 0x100c90, 0x1d07ff);
++ break;
++ }
++
++ return 0;
++}
++
++void
++nv50_fb_takedown(struct drm_device *dev)
++{
++}
+diff --git a/drivers/gpu/drm/nouveau/nv50_fbcon.c b/drivers/gpu/drm/nouveau/nv50_fbcon.c
+index 0f57cdf..a8c70e7 100644
+--- a/drivers/gpu/drm/nouveau/nv50_fbcon.c
++++ b/drivers/gpu/drm/nouveau/nv50_fbcon.c
+@@ -109,7 +109,7 @@ nv50_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
+ return;
+ }
+
+- width = (image->width + 31) & ~31;
++ width = ALIGN(image->width, 32);
+ dwords = (width * image->height) >> 5;
+
+ BEGIN_RING(chan, NvSub2D, 0x0814, 2);
+@@ -157,8 +157,11 @@ nv50_fbcon_accel_init(struct fb_info *info)
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_channel *chan = dev_priv->channel;
+ struct nouveau_gpuobj *eng2d = NULL;
++ uint64_t fb;
+ int ret, format;
+
++ fb = info->fix.smem_start - dev_priv->fb_phys + dev_priv->vm_vram_base;
++
+ switch (info->var.bits_per_pixel) {
+ case 8:
+ format = 0xf3;
+@@ -233,7 +236,7 @@ nv50_fbcon_accel_init(struct fb_info *info)
+ BEGIN_RING(chan, NvSub2D, 0x0808, 3);
+ OUT_RING(chan, 0);
+ OUT_RING(chan, 0);
+- OUT_RING(chan, 0);
++ OUT_RING(chan, 1);
+ BEGIN_RING(chan, NvSub2D, 0x081c, 1);
+ OUT_RING(chan, 1);
+ BEGIN_RING(chan, NvSub2D, 0x0840, 4);
+@@ -248,9 +251,8 @@ nv50_fbcon_accel_init(struct fb_info *info)
+ OUT_RING(chan, info->fix.line_length);
+ OUT_RING(chan, info->var.xres_virtual);
+ OUT_RING(chan, info->var.yres_virtual);
+- OUT_RING(chan, 0);
+- OUT_RING(chan, info->fix.smem_start - dev_priv->fb_phys +
+- dev_priv->vm_vram_base);
++ OUT_RING(chan, upper_32_bits(fb));
++ OUT_RING(chan, lower_32_bits(fb));
+ BEGIN_RING(chan, NvSub2D, 0x0230, 2);
+ OUT_RING(chan, format);
+ OUT_RING(chan, 1);
+@@ -258,9 +260,8 @@ nv50_fbcon_accel_init(struct fb_info *info)
+ OUT_RING(chan, info->fix.line_length);
+ OUT_RING(chan, info->var.xres_virtual);
+ OUT_RING(chan, info->var.yres_virtual);
+- OUT_RING(chan, 0);
+- OUT_RING(chan, info->fix.smem_start - dev_priv->fb_phys +
+- dev_priv->vm_vram_base);
++ OUT_RING(chan, upper_32_bits(fb));
++ OUT_RING(chan, lower_32_bits(fb));
+
+ return 0;
+ }
+diff --git a/drivers/gpu/drm/nouveau/nv50_fifo.c b/drivers/gpu/drm/nouveau/nv50_fifo.c
+index df5335a..e20c0e2 100644
+--- a/drivers/gpu/drm/nouveau/nv50_fifo.c
++++ b/drivers/gpu/drm/nouveau/nv50_fifo.c
+@@ -243,6 +243,7 @@ nv50_fifo_create_context(struct nouveau_channel *chan)
+ struct drm_device *dev = chan->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_gpuobj *ramfc = NULL;
++ unsigned long flags;
+ int ret;
+
+ NV_DEBUG(dev, "ch%d\n", chan->id);
+@@ -278,6 +279,8 @@ nv50_fifo_create_context(struct nouveau_channel *chan)
+ return ret;
+ }
+
++ 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);
+@@ -306,10 +309,12 @@ nv50_fifo_create_context(struct nouveau_channel *chan)
+ ret = nv50_fifo_channel_enable(dev, chan->id, false);
+ if (ret) {
+ NV_ERROR(dev, "error enabling ch%d: %d\n", chan->id, ret);
++ spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
+ nouveau_gpuobj_ref_del(dev, &chan->ramfc);
+ return ret;
+ }
+
++ spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
+ return 0;
+ }
+
+diff --git a/drivers/gpu/drm/nouveau/nv50_gpio.c b/drivers/gpu/drm/nouveau/nv50_gpio.c
+new file mode 100644
+index 0000000..c61782b
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv50_gpio.c
+@@ -0,0 +1,76 @@
++/*
++ * 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_hw.h"
++
++static int
++nv50_gpio_location(struct dcb_gpio_entry *gpio, uint32_t *reg, uint32_t *shift)
++{
++ const uint32_t nv50_gpio_reg[4] = { 0xe104, 0xe108, 0xe280, 0xe284 };
++
++ if (gpio->line > 32)
++ return -EINVAL;
++
++ *reg = nv50_gpio_reg[gpio->line >> 3];
++ *shift = (gpio->line & 7) << 2;
++ return 0;
++}
++
++int
++nv50_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag)
++{
++ struct dcb_gpio_entry *gpio;
++ uint32_t r, s, v;
++
++ gpio = nouveau_bios_gpio_entry(dev, tag);
++ if (!gpio)
++ return -ENOENT;
++
++ if (nv50_gpio_location(gpio, &r, &s))
++ return -EINVAL;
++
++ v = nv_rd32(dev, r) >> (s + 2);
++ return ((v & 1) == (gpio->state[1] & 1));
++}
++
++int
++nv50_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state)
++{
++ struct dcb_gpio_entry *gpio;
++ uint32_t r, s, v;
++
++ gpio = nouveau_bios_gpio_entry(dev, tag);
++ if (!gpio)
++ return -ENOENT;
++
++ if (nv50_gpio_location(gpio, &r, &s))
++ return -EINVAL;
++
++ v = nv_rd32(dev, r) & ~(0x3 << s);
++ v |= (gpio->state[state] ^ 2) << s;
++ nv_wr32(dev, r, v);
++ return 0;
++}
+diff --git a/drivers/gpu/drm/nouveau/nv50_graph.c b/drivers/gpu/drm/nouveau/nv50_graph.c
+index 6d50480..b203d06 100644
+--- a/drivers/gpu/drm/nouveau/nv50_graph.c
++++ b/drivers/gpu/drm/nouveau/nv50_graph.c
+@@ -28,7 +28,7 @@
+ #include "drm.h"
+ #include "nouveau_drv.h"
+
+-/*(DEBLOBBED)*/
++#include "nouveau_grctx.h"
+
+ #define IS_G80 ((dev_priv->chipset & 0xf0) == 0x50)
+
+@@ -79,6 +56,10 @@ nv50_graph_init_intr(struct drm_device *dev)
+ static void
+ nv50_graph_init_regs__nv(struct drm_device *dev)
+ {
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint32_t units = nv_rd32(dev, 0x1540);
++ int i;
++
+ NV_DEBUG(dev, "\n");
+
+ nv_wr32(dev, 0x400804, 0xc0000000);
+@@ -88,6 +69,20 @@ nv50_graph_init_regs__nv(struct drm_device *dev)
+ nv_wr32(dev, 0x405018, 0xc0000000);
+ nv_wr32(dev, 0x402000, 0xc0000000);
+
++ for (i = 0; i < 16; i++) {
++ if (units & 1 << i) {
++ if (dev_priv->chipset < 0xa0) {
++ nv_wr32(dev, 0x408900 + (i << 12), 0xc0000000);
++ nv_wr32(dev, 0x408e08 + (i << 12), 0xc0000000);
++ nv_wr32(dev, 0x408314 + (i << 12), 0xc0000000);
++ } else {
++ nv_wr32(dev, 0x408600 + (i << 11), 0xc0000000);
++ nv_wr32(dev, 0x408708 + (i << 11), 0xc0000000);
++ nv_wr32(dev, 0x40831c + (i << 11), 0xc0000000);
++ }
++ }
++ }
++
+ nv_wr32(dev, 0x400108, 0xffffffff);
+
+ nv_wr32(dev, 0x400824, 0x00004000);
+@@ -111,9 +106,34 @@ nv50_graph_init_ctxctl(struct drm_device *dev)
+
+ NV_DEBUG(dev, "\n");
+
+- nouveau_grctx_prog_load(dev);
+- if (!dev_priv->engine.graph.ctxprog)
+- dev_priv->engine.graph.accel_blocked = true;
++ if (nouveau_ctxfw) {
++ nouveau_grctx_prog_load(dev);
++ dev_priv->engine.graph.grctx_size = 0x70000;
++ }
++ if (!dev_priv->engine.graph.ctxprog) {
++ struct nouveau_grctx ctx = {};
++ uint32_t *cp = kmalloc(512 * 4, GFP_KERNEL);
++ int i;
++ if (!cp) {
++ NV_ERROR(dev, "Couldn't alloc ctxprog! Disabling acceleration.\n");
++ dev_priv->engine.graph.accel_blocked = true;
++ return 0;
++ }
++ ctx.dev = dev;
++ ctx.mode = NOUVEAU_GRCTX_PROG;
++ ctx.data = cp;
++ ctx.ctxprog_max = 512;
++ if (!nv50_grctx_init(&ctx)) {
++ dev_priv->engine.graph.grctx_size = ctx.ctxvals_pos * 4;
++
++ nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_INDEX, 0);
++ for (i = 0; i < ctx.ctxprog_len; i++)
++ nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_DATA, cp[i]);
++ } else {
++ dev_priv->engine.graph.accel_blocked = true;
++ }
++ kfree(cp);
++ }
+
+ nv_wr32(dev, 0x400320, 4);
+ nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, 0);
+@@ -193,13 +213,13 @@ nv50_graph_create_context(struct nouveau_channel *chan)
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_gpuobj *ramin = chan->ramin->gpuobj;
+ struct nouveau_gpuobj *ctx;
+- uint32_t grctx_size = 0x70000;
++ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
+ int hdr, ret;
+
+ NV_DEBUG(dev, "ch%d\n", chan->id);
+
+- ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, grctx_size, 0x1000,
+- NVOBJ_FLAG_ZERO_ALLOC |
++ ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, pgraph->grctx_size,
++ 0x1000, NVOBJ_FLAG_ZERO_ALLOC |
+ NVOBJ_FLAG_ZERO_FREE, &chan->ramin_grctx);
+ if (ret)
+ return ret;
+@@ -209,7 +229,7 @@ nv50_graph_create_context(struct nouveau_channel *chan)
+ dev_priv->engine.instmem.prepare_access(dev, true);
+ nv_wo32(dev, ramin, (hdr + 0x00)/4, 0x00190002);
+ nv_wo32(dev, ramin, (hdr + 0x04)/4, chan->ramin_grctx->instance +
+- grctx_size - 1);
++ 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);
+@@ -217,12 +237,16 @@ nv50_graph_create_context(struct nouveau_channel *chan)
+ dev_priv->engine.instmem.finish_access(dev);
+
+ dev_priv->engine.instmem.prepare_access(dev, true);
+- nouveau_grctx_vals_load(dev, ctx);
++ if (!pgraph->ctxprog) {
++ struct nouveau_grctx ctx = {};
++ ctx.dev = chan->dev;
++ ctx.mode = NOUVEAU_GRCTX_VALS;
++ ctx.data = chan->ramin_grctx->gpuobj;
++ nv50_grctx_init(&ctx);
++ } else {
++ nouveau_grctx_vals_load(dev, ctx);
++ }
+ nv_wo32(dev, ctx, 0x00000/4, chan->ramin->instance >> 12);
+- if ((dev_priv->chipset & 0xf0) == 0xa0)
+- nv_wo32(dev, ctx, 0x00004/4, 0x00000000);
+- else
+- nv_wo32(dev, ctx, 0x0011c/4, 0x00000000);
+ dev_priv->engine.instmem.finish_access(dev);
+
+ return 0;
+@@ -386,9 +410,10 @@ struct nouveau_pgraph_object_class nv50_graph_grclass[] = {
+ { 0x5039, false, NULL }, /* m2mf */
+ { 0x502d, false, NULL }, /* 2d */
+ { 0x50c0, false, NULL }, /* compute */
++ { 0x85c0, false, NULL }, /* compute (nva3, nva5, nva8) */
+ { 0x5097, false, NULL }, /* tesla (nv50) */
+- { 0x8297, false, NULL }, /* tesla (nv80/nv90) */
+- { 0x8397, false, NULL }, /* tesla (nva0) */
+- { 0x8597, false, NULL }, /* tesla (nva8) */
++ { 0x8297, false, NULL }, /* tesla (nv8x/nv9x) */
++ { 0x8397, false, NULL }, /* tesla (nva0, nvaa, nvac) */
++ { 0x8597, false, NULL }, /* tesla (nva3, nva5, nva8) */
+ {}
+ };
+diff --git a/drivers/gpu/drm/nouveau/nv50_grctx.c b/drivers/gpu/drm/nouveau/nv50_grctx.c
+new file mode 100644
+index 0000000..42a8fb2
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv50_grctx.c
+@@ -0,0 +1,2383 @@
++/*
++ * Copyright 2009 Marcin Kościelnicki
++ *
++ * 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.
++ */
++
++#define CP_FLAG_CLEAR 0
++#define CP_FLAG_SET 1
++#define CP_FLAG_SWAP_DIRECTION ((0 * 32) + 0)
++#define CP_FLAG_SWAP_DIRECTION_LOAD 0
++#define CP_FLAG_SWAP_DIRECTION_SAVE 1
++#define CP_FLAG_UNK01 ((0 * 32) + 1)
++#define CP_FLAG_UNK01_CLEAR 0
++#define CP_FLAG_UNK01_SET 1
++#define CP_FLAG_UNK03 ((0 * 32) + 3)
++#define CP_FLAG_UNK03_CLEAR 0
++#define CP_FLAG_UNK03_SET 1
++#define CP_FLAG_USER_SAVE ((0 * 32) + 5)
++#define CP_FLAG_USER_SAVE_NOT_PENDING 0
++#define CP_FLAG_USER_SAVE_PENDING 1
++#define CP_FLAG_USER_LOAD ((0 * 32) + 6)
++#define CP_FLAG_USER_LOAD_NOT_PENDING 0
++#define CP_FLAG_USER_LOAD_PENDING 1
++#define CP_FLAG_UNK0B ((0 * 32) + 0xb)
++#define CP_FLAG_UNK0B_CLEAR 0
++#define CP_FLAG_UNK0B_SET 1
++#define CP_FLAG_UNK1D ((0 * 32) + 0x1d)
++#define CP_FLAG_UNK1D_CLEAR 0
++#define CP_FLAG_UNK1D_SET 1
++#define CP_FLAG_UNK20 ((1 * 32) + 0)
++#define CP_FLAG_UNK20_CLEAR 0
++#define CP_FLAG_UNK20_SET 1
++#define CP_FLAG_STATUS ((2 * 32) + 0)
++#define CP_FLAG_STATUS_BUSY 0
++#define CP_FLAG_STATUS_IDLE 1
++#define CP_FLAG_AUTO_SAVE ((2 * 32) + 4)
++#define CP_FLAG_AUTO_SAVE_NOT_PENDING 0
++#define CP_FLAG_AUTO_SAVE_PENDING 1
++#define CP_FLAG_AUTO_LOAD ((2 * 32) + 5)
++#define CP_FLAG_AUTO_LOAD_NOT_PENDING 0
++#define CP_FLAG_AUTO_LOAD_PENDING 1
++#define CP_FLAG_NEWCTX ((2 * 32) + 10)
++#define CP_FLAG_NEWCTX_BUSY 0
++#define CP_FLAG_NEWCTX_DONE 1
++#define CP_FLAG_XFER ((2 * 32) + 11)
++#define CP_FLAG_XFER_IDLE 0
++#define CP_FLAG_XFER_BUSY 1
++#define CP_FLAG_ALWAYS ((2 * 32) + 13)
++#define CP_FLAG_ALWAYS_FALSE 0
++#define CP_FLAG_ALWAYS_TRUE 1
++#define CP_FLAG_INTR ((2 * 32) + 15)
++#define CP_FLAG_INTR_NOT_PENDING 0
++#define CP_FLAG_INTR_PENDING 1
++
++#define CP_CTX 0x00100000
++#define CP_CTX_COUNT 0x000f0000
++#define CP_CTX_COUNT_SHIFT 16
++#define CP_CTX_REG 0x00003fff
++#define CP_LOAD_SR 0x00200000
++#define CP_LOAD_SR_VALUE 0x000fffff
++#define CP_BRA 0x00400000
++#define CP_BRA_IP 0x0001ff00
++#define CP_BRA_IP_SHIFT 8
++#define CP_BRA_IF_CLEAR 0x00000080
++#define CP_BRA_FLAG 0x0000007f
++#define CP_WAIT 0x00500000
++#define CP_WAIT_SET 0x00000080
++#define CP_WAIT_FLAG 0x0000007f
++#define CP_SET 0x00700000
++#define CP_SET_1 0x00000080
++#define CP_SET_FLAG 0x0000007f
++#define CP_NEWCTX 0x00600004
++#define CP_NEXT_TO_SWAP 0x00600005
++#define CP_SET_CONTEXT_POINTER 0x00600006
++#define CP_SET_XFER_POINTER 0x00600007
++#define CP_ENABLE 0x00600009
++#define CP_END 0x0060000c
++#define CP_NEXT_TO_CURRENT 0x0060000d
++#define CP_DISABLE1 0x0090ffff
++#define CP_DISABLE2 0x0091ffff
++#define CP_XFER_1 0x008000ff
++#define CP_XFER_2 0x008800ff
++#define CP_SEEK_1 0x00c000ff
++#define CP_SEEK_2 0x00c800ff
++
++#include "drmP.h"
++#include "nouveau_drv.h"
++#include "nouveau_grctx.h"
++
++/*
++ * 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
++ * microcode to do it. And it's the driver's task to supply this microcode,
++ * further known as ctxprog, as well as the initial context values, known
++ * as ctxvals.
++ *
++ * Without ctxprog, you cannot switch contexts. Not even in software, since
++ * the majority of context [xfer strands] isn't accessible directly. You're
++ * stuck with a single channel, and you also suffer all the problems resulting
++ * from missing ctxvals, since you cannot load them.
++ *
++ * Without ctxvals, you're stuck with PGRAPH's default context. It's enough to
++ * run 2d operations, but trying to utilise 3d or CUDA will just lock you up,
++ * since you don't have... some sort of needed setup.
++ *
++ * Nouveau will just disable acceleration if not given ctxprog + ctxvals, since
++ * it's too much hassle to handle no-ctxprog as a special case.
++ */
++
++/*
++ * How ctxprogs work.
++ *
++ * The ctxprog is written in its own kind of microcode, with very small and
++ * crappy set of available commands. You upload it to a small [512 insns]
++ * area of memory on PGRAPH, and it'll be run when PFIFO wants PGRAPH to
++ * switch channel. or when the driver explicitely requests it. Stuff visible
++ * to ctxprog consists of: PGRAPH MMIO registers, PGRAPH context strands,
++ * the per-channel context save area in VRAM [known as ctxvals or grctx],
++ * 4 flags registers, a scratch register, two grctx pointers, plus many
++ * random poorly-understood details.
++ *
++ * When ctxprog runs, it's supposed to check what operations are asked of it,
++ * save old context if requested, optionally reset PGRAPH and switch to the
++ * new channel, and load the new context. Context consists of three major
++ * parts: subset of MMIO registers and two "xfer areas".
++ */
++
++/* TODO:
++ * - document unimplemented bits compared to nvidia
++ * - NVAx: make a TP subroutine, use it.
++ * - use 0x4008fc instead of 0x1540?
++ */
++
++enum cp_label {
++ cp_check_load = 1,
++ cp_setup_auto_load,
++ cp_setup_load,
++ cp_setup_save,
++ cp_swap_state,
++ cp_prepare_exit,
++ cp_exit,
++};
++
++static void nv50_graph_construct_mmio(struct nouveau_grctx *ctx);
++static void nv50_graph_construct_xfer1(struct nouveau_grctx *ctx);
++static void nv50_graph_construct_xfer2(struct nouveau_grctx *ctx);
++
++/* Main function: construct the ctxprog skeleton, call the other functions. */
++
++int
++nv50_grctx_init(struct nouveau_grctx *ctx)
++{
++ struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
++
++ switch (dev_priv->chipset) {
++ case 0x50:
++ case 0x84:
++ case 0x86:
++ case 0x92:
++ case 0x94:
++ case 0x96:
++ case 0x98:
++ case 0xa0:
++ case 0xa3:
++ case 0xa5:
++ case 0xa8:
++ case 0xaa:
++ case 0xac:
++ break;
++ default:
++ NV_ERROR(ctx->dev, "I don't know how to make a ctxprog for "
++ "your NV%x card.\n", dev_priv->chipset);
++ NV_ERROR(ctx->dev, "Disabling acceleration. Please contact "
++ "the devs.\n");
++ return -ENOSYS;
++ }
++ /* decide whether we're loading/unloading the context */
++ cp_bra (ctx, AUTO_SAVE, PENDING, cp_setup_save);
++ cp_bra (ctx, USER_SAVE, PENDING, cp_setup_save);
++
++ cp_name(ctx, cp_check_load);
++ cp_bra (ctx, AUTO_LOAD, PENDING, cp_setup_auto_load);
++ cp_bra (ctx, USER_LOAD, PENDING, cp_setup_load);
++ cp_bra (ctx, ALWAYS, TRUE, cp_exit);
++
++ /* setup for context load */
++ cp_name(ctx, cp_setup_auto_load);
++ cp_out (ctx, CP_DISABLE1);
++ cp_out (ctx, CP_DISABLE2);
++ cp_out (ctx, CP_ENABLE);
++ cp_out (ctx, CP_NEXT_TO_SWAP);
++ cp_set (ctx, UNK01, SET);
++ cp_name(ctx, cp_setup_load);
++ cp_out (ctx, CP_NEWCTX);
++ cp_wait(ctx, NEWCTX, BUSY);
++ cp_set (ctx, UNK1D, CLEAR);
++ cp_set (ctx, SWAP_DIRECTION, LOAD);
++ cp_bra (ctx, UNK0B, SET, cp_prepare_exit);
++ cp_bra (ctx, ALWAYS, TRUE, cp_swap_state);
++
++ /* setup for context save */
++ cp_name(ctx, cp_setup_save);
++ cp_set (ctx, UNK1D, SET);
++ cp_wait(ctx, STATUS, BUSY);
++ cp_wait(ctx, INTR, PENDING);
++ cp_bra (ctx, STATUS, BUSY, cp_setup_save);
++ cp_set (ctx, UNK01, SET);
++ cp_set (ctx, SWAP_DIRECTION, SAVE);
++
++ /* general PGRAPH state */
++ cp_name(ctx, cp_swap_state);
++ cp_set (ctx, UNK03, SET);
++ cp_pos (ctx, 0x00004/4);
++ cp_ctx (ctx, 0x400828, 1); /* needed. otherwise, flickering happens. */
++ cp_pos (ctx, 0x00100/4);
++ nv50_graph_construct_mmio(ctx);
++ nv50_graph_construct_xfer1(ctx);
++ nv50_graph_construct_xfer2(ctx);
++
++ cp_bra (ctx, SWAP_DIRECTION, SAVE, cp_check_load);
++
++ cp_set (ctx, UNK20, SET);
++ cp_set (ctx, SWAP_DIRECTION, SAVE); /* no idea why this is needed, but fixes at least one lockup. */
++ cp_lsr (ctx, ctx->ctxvals_base);
++ cp_out (ctx, CP_SET_XFER_POINTER);
++ cp_lsr (ctx, 4);
++ cp_out (ctx, CP_SEEK_1);
++ cp_out (ctx, CP_XFER_1);
++ cp_wait(ctx, XFER, BUSY);
++
++ /* pre-exit state updates */
++ cp_name(ctx, cp_prepare_exit);
++ cp_set (ctx, UNK01, CLEAR);
++ cp_set (ctx, UNK03, CLEAR);
++ cp_set (ctx, UNK1D, CLEAR);
++
++ cp_bra (ctx, USER_SAVE, PENDING, cp_exit);
++ cp_out (ctx, CP_NEXT_TO_CURRENT);
++
++ cp_name(ctx, cp_exit);
++ cp_set (ctx, USER_SAVE, NOT_PENDING);
++ cp_set (ctx, USER_LOAD, NOT_PENDING);
++ cp_out (ctx, CP_END);
++ ctx->ctxvals_pos += 0x400; /* padding... no idea why you need it */
++
++ return 0;
++}
++
++/*
++ * Constructs MMIO part of ctxprog and ctxvals. Just a matter of knowing which
++ * registers to save/restore and the default values for them.
++ */
++
++static void
++nv50_graph_construct_mmio(struct nouveau_grctx *ctx)
++{
++ struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
++ int i, j;
++ int offset, base;
++ uint32_t units = nv_rd32 (ctx->dev, 0x1540);
++
++ /* 0800: DISPATCH */
++ cp_ctx(ctx, 0x400808, 7);
++ gr_def(ctx, 0x400814, 0x00000030);
++ cp_ctx(ctx, 0x400834, 0x32);
++ if (dev_priv->chipset == 0x50) {
++ gr_def(ctx, 0x400834, 0xff400040);
++ gr_def(ctx, 0x400838, 0xfff00080);
++ gr_def(ctx, 0x40083c, 0xfff70090);
++ gr_def(ctx, 0x400840, 0xffe806a8);
++ }
++ gr_def(ctx, 0x400844, 0x00000002);
++ if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
++ gr_def(ctx, 0x400894, 0x00001000);
++ gr_def(ctx, 0x4008e8, 0x00000003);
++ gr_def(ctx, 0x4008ec, 0x00001000);
++ if (dev_priv->chipset == 0x50)
++ cp_ctx(ctx, 0x400908, 0xb);
++ else if (dev_priv->chipset < 0xa0)
++ cp_ctx(ctx, 0x400908, 0xc);
++ else
++ cp_ctx(ctx, 0x400908, 0xe);
++
++ if (dev_priv->chipset >= 0xa0)
++ cp_ctx(ctx, 0x400b00, 0x1);
++ if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa) {
++ cp_ctx(ctx, 0x400b10, 0x1);
++ gr_def(ctx, 0x400b10, 0x0001629d);
++ cp_ctx(ctx, 0x400b20, 0x1);
++ gr_def(ctx, 0x400b20, 0x0001629d);
++ }
++
++ /* 0C00: VFETCH */
++ cp_ctx(ctx, 0x400c08, 0x2);
++ gr_def(ctx, 0x400c08, 0x0000fe0c);
++
++ /* 1000 */
++ 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) {
++ cp_ctx(ctx, 0x401008, 0x5);
++ gr_def(ctx, 0x401018, 0x00001000);
++ } else {
++ cp_ctx(ctx, 0x401008, 0x5);
++ gr_def(ctx, 0x401018, 0x00004000);
++ }
++
++ /* 1400 */
++ cp_ctx(ctx, 0x401400, 0x8);
++ cp_ctx(ctx, 0x401424, 0x3);
++ if (dev_priv->chipset == 0x50)
++ gr_def(ctx, 0x40142c, 0x0001fd87);
++ else
++ gr_def(ctx, 0x40142c, 0x00000187);
++ cp_ctx(ctx, 0x401540, 0x5);
++ gr_def(ctx, 0x401550, 0x00001018);
++
++ /* 1800: STREAMOUT */
++ cp_ctx(ctx, 0x401814, 0x1);
++ gr_def(ctx, 0x401814, 0x000000ff);
++ if (dev_priv->chipset == 0x50) {
++ cp_ctx(ctx, 0x40181c, 0xe);
++ gr_def(ctx, 0x401850, 0x00000004);
++ } else if (dev_priv->chipset < 0xa0) {
++ cp_ctx(ctx, 0x40181c, 0xf);
++ gr_def(ctx, 0x401854, 0x00000004);
++ } else {
++ cp_ctx(ctx, 0x40181c, 0x13);
++ gr_def(ctx, 0x401864, 0x00000004);
++ }
++
++ /* 1C00 */
++ cp_ctx(ctx, 0x401c00, 0x1);
++ switch (dev_priv->chipset) {
++ case 0x50:
++ gr_def(ctx, 0x401c00, 0x0001005f);
++ break;
++ case 0x84:
++ case 0x86:
++ case 0x94:
++ gr_def(ctx, 0x401c00, 0x044d00df);
++ break;
++ case 0x92:
++ case 0x96:
++ case 0x98:
++ case 0xa0:
++ case 0xaa:
++ case 0xac:
++ gr_def(ctx, 0x401c00, 0x042500df);
++ break;
++ case 0xa3:
++ case 0xa5:
++ case 0xa8:
++ gr_def(ctx, 0x401c00, 0x142500df);
++ break;
++ }
++
++ /* 2400 */
++ cp_ctx(ctx, 0x402400, 0x1);
++ if (dev_priv->chipset == 0x50)
++ cp_ctx(ctx, 0x402408, 0x1);
++ else
++ cp_ctx(ctx, 0x402408, 0x2);
++ gr_def(ctx, 0x402408, 0x00000600);
++
++ /* 2800 */
++ cp_ctx(ctx, 0x402800, 0x1);
++ if (dev_priv->chipset == 0x50)
++ gr_def(ctx, 0x402800, 0x00000006);
++
++ /* 2C00 */
++ cp_ctx(ctx, 0x402c08, 0x6);
++ if (dev_priv->chipset != 0x50)
++ gr_def(ctx, 0x402c14, 0x01000000);
++ gr_def(ctx, 0x402c18, 0x000000ff);
++ if (dev_priv->chipset == 0x50)
++ cp_ctx(ctx, 0x402ca0, 0x1);
++ else
++ 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)
++ gr_def(ctx, 0x402ca0, 0x00000800);
++ else
++ gr_def(ctx, 0x402ca0, 0x00000400);
++ cp_ctx(ctx, 0x402cac, 0x4);
++
++ /* 3000 */
++ cp_ctx(ctx, 0x403004, 0x1);
++ gr_def(ctx, 0x403004, 0x00000001);
++
++ /* 3404 */
++ if (dev_priv->chipset >= 0xa0) {
++ cp_ctx(ctx, 0x403404, 0x1);
++ gr_def(ctx, 0x403404, 0x00000001);
++ }
++
++ /* 5000 */
++ cp_ctx(ctx, 0x405000, 0x1);
++ switch (dev_priv->chipset) {
++ case 0x50:
++ gr_def(ctx, 0x405000, 0x00300080);
++ break;
++ case 0x84:
++ case 0xa0:
++ case 0xa3:
++ case 0xa5:
++ case 0xa8:
++ case 0xaa:
++ case 0xac:
++ gr_def(ctx, 0x405000, 0x000e0080);
++ break;
++ case 0x86:
++ case 0x92:
++ case 0x94:
++ case 0x96:
++ case 0x98:
++ gr_def(ctx, 0x405000, 0x00000080);
++ break;
++ }
++ cp_ctx(ctx, 0x405014, 0x1);
++ gr_def(ctx, 0x405014, 0x00000004);
++ cp_ctx(ctx, 0x40501c, 0x1);
++ 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);
++
++ /* 6800: M2MF */
++ if (dev_priv->chipset < 0x90) {
++ cp_ctx(ctx, 0x406814, 0x2b);
++ gr_def(ctx, 0x406818, 0x00000f80);
++ gr_def(ctx, 0x406860, 0x007f0080);
++ gr_def(ctx, 0x40689c, 0x007f0080);
++ } else {
++ cp_ctx(ctx, 0x406814, 0x4);
++ if (dev_priv->chipset == 0x98)
++ gr_def(ctx, 0x406818, 0x00000f80);
++ else
++ gr_def(ctx, 0x406818, 0x00001f80);
++ if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
++ gr_def(ctx, 0x40681c, 0x00000030);
++ cp_ctx(ctx, 0x406830, 0x3);
++ }
++
++ /* 7000: per-ROP group state */
++ for (i = 0; i < 8; i++) {
++ if (units & (1<<(i+16))) {
++ cp_ctx(ctx, 0x407000 + (i<<8), 3);
++ if (dev_priv->chipset == 0x50)
++ gr_def(ctx, 0x407000 + (i<<8), 0x1b74f820);
++ else if (dev_priv->chipset != 0xa5)
++ gr_def(ctx, 0x407000 + (i<<8), 0x3b74f821);
++ else
++ gr_def(ctx, 0x407000 + (i<<8), 0x7b74f821);
++ gr_def(ctx, 0x407004 + (i<<8), 0x89058001);
++
++ if (dev_priv->chipset == 0x50) {
++ cp_ctx(ctx, 0x407010 + (i<<8), 1);
++ } else if (dev_priv->chipset < 0xa0) {
++ cp_ctx(ctx, 0x407010 + (i<<8), 2);
++ gr_def(ctx, 0x407010 + (i<<8), 0x00001000);
++ gr_def(ctx, 0x407014 + (i<<8), 0x0000001f);
++ } else {
++ cp_ctx(ctx, 0x407010 + (i<<8), 3);
++ gr_def(ctx, 0x407010 + (i<<8), 0x00001000);
++ if (dev_priv->chipset != 0xa5)
++ gr_def(ctx, 0x407014 + (i<<8), 0x000000ff);
++ else
++ gr_def(ctx, 0x407014 + (i<<8), 0x000001ff);
++ }
++
++ cp_ctx(ctx, 0x407080 + (i<<8), 4);
++ if (dev_priv->chipset != 0xa5)
++ gr_def(ctx, 0x407080 + (i<<8), 0x027c10fa);
++ else
++ gr_def(ctx, 0x407080 + (i<<8), 0x827c10fa);
++ if (dev_priv->chipset == 0x50)
++ gr_def(ctx, 0x407084 + (i<<8), 0x000000c0);
++ else
++ gr_def(ctx, 0x407084 + (i<<8), 0x400000c0);
++ gr_def(ctx, 0x407088 + (i<<8), 0xb7892080);
++
++ if (dev_priv->chipset < 0xa0)
++ cp_ctx(ctx, 0x407094 + (i<<8), 1);
++ else if (dev_priv->chipset <= 0xa0 || dev_priv->chipset >= 0xaa)
++ cp_ctx(ctx, 0x407094 + (i<<8), 3);
++ else {
++ cp_ctx(ctx, 0x407094 + (i<<8), 4);
++ gr_def(ctx, 0x4070a0 + (i<<8), 1);
++ }
++ }
++ }
++
++ cp_ctx(ctx, 0x407c00, 0x3);
++ if (dev_priv->chipset < 0x90)
++ gr_def(ctx, 0x407c00, 0x00010040);
++ else if (dev_priv->chipset < 0xa0)
++ gr_def(ctx, 0x407c00, 0x00390040);
++ else
++ gr_def(ctx, 0x407c00, 0x003d0040);
++ gr_def(ctx, 0x407c08, 0x00000022);
++ if (dev_priv->chipset >= 0xa0) {
++ cp_ctx(ctx, 0x407c10, 0x3);
++ cp_ctx(ctx, 0x407c20, 0x1);
++ cp_ctx(ctx, 0x407c2c, 0x1);
++ }
++
++ if (dev_priv->chipset < 0xa0) {
++ cp_ctx(ctx, 0x407d00, 0x9);
++ } else {
++ cp_ctx(ctx, 0x407d00, 0x15);
++ }
++ if (dev_priv->chipset == 0x98)
++ gr_def(ctx, 0x407d08, 0x00380040);
++ else {
++ if (dev_priv->chipset < 0x90)
++ gr_def(ctx, 0x407d08, 0x00010040);
++ else if (dev_priv->chipset < 0xa0)
++ gr_def(ctx, 0x407d08, 0x00390040);
++ else
++ gr_def(ctx, 0x407d08, 0x003d0040);
++ gr_def(ctx, 0x407d0c, 0x00000022);
++ }
++
++ /* 8000+: per-TP state */
++ for (i = 0; i < 10; i++) {
++ if (units & (1<<i)) {
++ if (dev_priv->chipset < 0xa0)
++ base = 0x408000 + (i<<12);
++ else
++ base = 0x408000 + (i<<11);
++ if (dev_priv->chipset < 0xa0)
++ offset = base + 0xc00;
++ else
++ offset = base + 0x80;
++ cp_ctx(ctx, offset + 0x00, 1);
++ gr_def(ctx, offset + 0x00, 0x0000ff0a);
++ cp_ctx(ctx, offset + 0x08, 1);
++
++ /* per-MP state */
++ for (j = 0; j < (dev_priv->chipset < 0xa0 ? 2 : 4); j++) {
++ if (!(units & (1 << (j+24)))) continue;
++ if (dev_priv->chipset < 0xa0)
++ offset = base + 0x200 + (j<<7);
++ else
++ offset = base + 0x100 + (j<<7);
++ cp_ctx(ctx, offset, 0x20);
++ gr_def(ctx, offset + 0x00, 0x01800000);
++ gr_def(ctx, offset + 0x04, 0x00160000);
++ gr_def(ctx, offset + 0x08, 0x01800000);
++ gr_def(ctx, offset + 0x18, 0x0003ffff);
++ switch (dev_priv->chipset) {
++ case 0x50:
++ gr_def(ctx, offset + 0x1c, 0x00080000);
++ break;
++ case 0x84:
++ gr_def(ctx, offset + 0x1c, 0x00880000);
++ break;
++ case 0x86:
++ gr_def(ctx, offset + 0x1c, 0x008c0000);
++ break;
++ case 0x92:
++ case 0x96:
++ case 0x98:
++ gr_def(ctx, offset + 0x1c, 0x118c0000);
++ break;
++ case 0x94:
++ gr_def(ctx, offset + 0x1c, 0x10880000);
++ break;
++ case 0xa0:
++ case 0xa5:
++ gr_def(ctx, offset + 0x1c, 0x310c0000);
++ break;
++ case 0xa3:
++ case 0xa8:
++ case 0xaa:
++ case 0xac:
++ gr_def(ctx, offset + 0x1c, 0x300c0000);
++ break;
++ }
++ gr_def(ctx, offset + 0x40, 0x00010401);
++ if (dev_priv->chipset == 0x50)
++ gr_def(ctx, offset + 0x48, 0x00000040);
++ else
++ gr_def(ctx, offset + 0x48, 0x00000078);
++ gr_def(ctx, offset + 0x50, 0x000000bf);
++ gr_def(ctx, offset + 0x58, 0x00001210);
++ if (dev_priv->chipset == 0x50)
++ gr_def(ctx, offset + 0x5c, 0x00000080);
++ else
++ gr_def(ctx, offset + 0x5c, 0x08000080);
++ if (dev_priv->chipset >= 0xa0)
++ gr_def(ctx, offset + 0x68, 0x0000003e);
++ }
++
++ if (dev_priv->chipset < 0xa0)
++ cp_ctx(ctx, base + 0x300, 0x4);
++ else
++ cp_ctx(ctx, base + 0x300, 0x5);
++ if (dev_priv->chipset == 0x50)
++ 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)
++ gr_def(ctx, base + 0x304, 0x01127070);
++ else
++ gr_def(ctx, base + 0x304, 0x05127070);
++
++ if (dev_priv->chipset < 0xa0)
++ cp_ctx(ctx, base + 0x318, 1);
++ else
++ cp_ctx(ctx, base + 0x320, 1);
++ if (dev_priv->chipset == 0x50)
++ gr_def(ctx, base + 0x318, 0x0003ffff);
++ else if (dev_priv->chipset < 0xa0)
++ gr_def(ctx, base + 0x318, 0x03ffffff);
++ else
++ gr_def(ctx, base + 0x320, 0x07ffffff);
++
++ if (dev_priv->chipset < 0xa0)
++ cp_ctx(ctx, base + 0x324, 5);
++ else
++ cp_ctx(ctx, base + 0x328, 4);
++
++ if (dev_priv->chipset < 0xa0) {
++ cp_ctx(ctx, base + 0x340, 9);
++ offset = base + 0x340;
++ } else if (dev_priv->chipset <= 0xa0 || dev_priv->chipset >= 0xaa) {
++ cp_ctx(ctx, base + 0x33c, 0xb);
++ offset = base + 0x344;
++ } else {
++ cp_ctx(ctx, base + 0x33c, 0xd);
++ offset = base + 0x344;
++ }
++ gr_def(ctx, offset + 0x0, 0x00120407);
++ gr_def(ctx, offset + 0x4, 0x05091507);
++ if (dev_priv->chipset == 0x84)
++ gr_def(ctx, offset + 0x8, 0x05100202);
++ else
++ gr_def(ctx, offset + 0x8, 0x05010202);
++ gr_def(ctx, offset + 0xc, 0x00030201);
++ if (dev_priv->chipset == 0xa3)
++ cp_ctx(ctx, base + 0x36c, 1);
++
++ cp_ctx(ctx, base + 0x400, 2);
++ gr_def(ctx, base + 0x404, 0x00000040);
++ cp_ctx(ctx, base + 0x40c, 2);
++ gr_def(ctx, base + 0x40c, 0x0d0c0b0a);
++ gr_def(ctx, base + 0x410, 0x00141210);
++
++ if (dev_priv->chipset < 0xa0)
++ offset = base + 0x800;
++ else
++ offset = base + 0x500;
++ cp_ctx(ctx, offset, 6);
++ 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)
++ gr_def(ctx, offset + 0xc, 0x00008000);
++ gr_def(ctx, offset + 0x14, 0x00039e00);
++ cp_ctx(ctx, offset + 0x1c, 2);
++ if (dev_priv->chipset == 0x50)
++ gr_def(ctx, offset + 0x1c, 0x00000040);
++ else
++ gr_def(ctx, offset + 0x1c, 0x00000100);
++ gr_def(ctx, offset + 0x20, 0x00003800);
++
++ if (dev_priv->chipset >= 0xa0) {
++ cp_ctx(ctx, base + 0x54c, 2);
++ if (dev_priv->chipset <= 0xa0 || dev_priv->chipset >= 0xaa)
++ gr_def(ctx, base + 0x54c, 0x003fe006);
++ else
++ gr_def(ctx, base + 0x54c, 0x003fe007);
++ gr_def(ctx, base + 0x550, 0x003fe000);
++ }
++
++ if (dev_priv->chipset < 0xa0)
++ offset = base + 0xa00;
++ else
++ offset = base + 0x680;
++ cp_ctx(ctx, offset, 1);
++ gr_def(ctx, offset, 0x00404040);
++
++ if (dev_priv->chipset < 0xa0)
++ offset = base + 0xe00;
++ else
++ offset = base + 0x700;
++ cp_ctx(ctx, offset, 2);
++ if (dev_priv->chipset < 0xa0)
++ gr_def(ctx, offset, 0x0077f005);
++ else if (dev_priv->chipset == 0xa5)
++ gr_def(ctx, offset, 0x6cf7f007);
++ else if (dev_priv->chipset == 0xa8)
++ gr_def(ctx, offset, 0x6cfff007);
++ else if (dev_priv->chipset == 0xac)
++ gr_def(ctx, offset, 0x0cfff007);
++ else
++ gr_def(ctx, offset, 0x0cf7f007);
++ if (dev_priv->chipset == 0x50)
++ gr_def(ctx, offset + 0x4, 0x00007fff);
++ else if (dev_priv->chipset < 0xa0)
++ gr_def(ctx, offset + 0x4, 0x003f7fff);
++ else
++ gr_def(ctx, offset + 0x4, 0x02bf7fff);
++ cp_ctx(ctx, offset + 0x2c, 1);
++ if (dev_priv->chipset == 0x50) {
++ cp_ctx(ctx, offset + 0x50, 9);
++ gr_def(ctx, offset + 0x54, 0x000003ff);
++ gr_def(ctx, offset + 0x58, 0x00000003);
++ gr_def(ctx, offset + 0x5c, 0x00000003);
++ gr_def(ctx, offset + 0x60, 0x000001ff);
++ gr_def(ctx, offset + 0x64, 0x0000001f);
++ gr_def(ctx, offset + 0x68, 0x0000000f);
++ gr_def(ctx, offset + 0x6c, 0x0000000f);
++ } else if(dev_priv->chipset < 0xa0) {
++ cp_ctx(ctx, offset + 0x50, 1);
++ cp_ctx(ctx, offset + 0x70, 1);
++ } else {
++ cp_ctx(ctx, offset + 0x50, 1);
++ cp_ctx(ctx, offset + 0x60, 5);
++ }
++ }
++ }
++}
++
++/*
++ * xfer areas. These are a pain.
++ *
++ * There are 2 xfer areas: the first one is big and contains all sorts of
++ * stuff, the second is small and contains some per-TP context.
++ *
++ * Each area is split into 8 "strands". The areas, when saved to grctx,
++ * are made of 8-word blocks. Each block contains a single word from
++ * each strand. The strands are independent of each other, their
++ * addresses are unrelated to each other, and data in them is closely
++ * packed together. The strand layout varies a bit between cards: here
++ * and there, a single word is thrown out in the middle and the whole
++ * strand is offset by a bit from corresponding one on another chipset.
++ * For this reason, addresses of stuff in strands are almost useless.
++ * Knowing sequence of stuff and size of gaps between them is much more
++ * useful, and that's how we build the strands in our generator.
++ *
++ * NVA0 takes this mess to a whole new level by cutting the old strands
++ * into a few dozen pieces [known as genes], rearranging them randomly,
++ * and putting them back together to make new strands. Hopefully these
++ * genes correspond more or less directly to the same PGRAPH subunits
++ * as in 400040 register.
++ *
++ * The most common value in default context is 0, and when the genes
++ * are separated by 0's, gene bounduaries are quite speculative...
++ * some of them can be clearly deduced, others can be guessed, and yet
++ * others won't be resolved without figuring out the real meaning of
++ * given ctxval. For the same reason, ending point of each strand
++ * is unknown. Except for strand 0, which is the longest strand and
++ * its end corresponds to end of the whole xfer.
++ *
++ * An unsolved mystery is the seek instruction: it takes an argument
++ * in bits 8-18, and that argument is clearly the place in strands to
++ * seek to... but the offsets don't seem to correspond to offsets as
++ * seen in grctx. Perhaps there's another, real, not randomly-changing
++ * addressing in strands, and the xfer insn just happens to skip over
++ * the unused bits? NV10-NV30 PIPE comes to mind...
++ *
++ * As far as I know, there's no way to access the xfer areas directly
++ * without the help of ctxprog.
++ */
++
++static inline 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);
++ ctx->ctxvals_pos += num << 3;
++}
++
++/* Gene declarations... */
++
++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_ropc(struct nouveau_grctx *ctx);
++static void nv50_graph_construct_xfer_tp(struct nouveau_grctx *ctx);
++
++static void
++nv50_graph_construct_xfer1(struct nouveau_grctx *ctx)
++{
++ struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
++ int i;
++ int offset;
++ int size = 0;
++ uint32_t units = nv_rd32 (ctx->dev, 0x1540);
++
++ offset = (ctx->ctxvals_pos+0x3f)&~0x3f;
++ ctx->ctxvals_base = offset;
++
++ 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);
++ 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);
++ 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);
++ if ((ctx->ctxvals_pos-offset)/8 > size)
++ size = (ctx->ctxvals_pos-offset)/8;
++
++ /* Strand 3: per-ROP group state */
++ ctx->ctxvals_pos = offset + 3;
++ for (i = 0; i < 6; i++)
++ if (units & (1 << (i + 16)))
++ nv50_graph_construct_gene_ropc(ctx);
++ if ((ctx->ctxvals_pos-offset)/8 > size)
++ size = (ctx->ctxvals_pos-offset)/8;
++
++ /* Strands 4-7: per-TP state */
++ for (i = 0; i < 4; i++) {
++ ctx->ctxvals_pos = offset + 4 + i;
++ if (units & (1 << (2 * i)))
++ nv50_graph_construct_xfer_tp(ctx);
++ if (units & (1 << (2 * i + 1)))
++ nv50_graph_construct_xfer_tp(ctx);
++ if ((ctx->ctxvals_pos-offset)/8 > size)
++ size = (ctx->ctxvals_pos-offset)/8;
++ }
++ } 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_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);
++ 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);
++ 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 ((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);
++ 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);
++ 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);
++ for (i = 0; i < 8; i++)
++ if (units & (1<<(i+16)))
++ nv50_graph_construct_gene_ropc(ctx);
++ if ((ctx->ctxvals_pos-offset)/8 > size)
++ size = (ctx->ctxvals_pos-offset)/8;
++
++ /* 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);
++ if (units & (1 << 0))
++ nv50_graph_construct_xfer_tp(ctx);
++ if (units & (1 << 1))
++ nv50_graph_construct_xfer_tp(ctx);
++ if (units & (1 << 2))
++ nv50_graph_construct_xfer_tp(ctx);
++ if (units & (1 << 3))
++ nv50_graph_construct_xfer_tp(ctx);
++ if ((ctx->ctxvals_pos-offset)/8 > size)
++ size = (ctx->ctxvals_pos-offset)/8;
++
++ /* Strand 7 */
++ ctx->ctxvals_pos = offset + 7;
++ if (dev_priv->chipset == 0xa0) {
++ if (units & (1 << 4))
++ nv50_graph_construct_xfer_tp(ctx);
++ if (units & (1 << 5))
++ nv50_graph_construct_xfer_tp(ctx);
++ if (units & (1 << 6))
++ nv50_graph_construct_xfer_tp(ctx);
++ if (units & (1 << 7))
++ nv50_graph_construct_xfer_tp(ctx);
++ if (units & (1 << 8))
++ nv50_graph_construct_xfer_tp(ctx);
++ if (units & (1 << 9))
++ nv50_graph_construct_xfer_tp(ctx);
++ } else {
++ nv50_graph_construct_gene_unk2(ctx);
++ }
++ if ((ctx->ctxvals_pos-offset)/8 > size)
++ size = (ctx->ctxvals_pos-offset)/8;
++ }
++
++ ctx->ctxvals_pos = offset + size * 8;
++ ctx->ctxvals_pos = (ctx->ctxvals_pos+0x3f)&~0x3f;
++ cp_lsr (ctx, offset);
++ cp_out (ctx, CP_SET_XFER_POINTER);
++ cp_lsr (ctx, size);
++ cp_out (ctx, CP_SEEK_1);
++ cp_out (ctx, CP_XFER_1);
++ cp_wait(ctx, XFER, BUSY);
++}
++
++/*
++ * non-trivial demagiced parts of ctx init go here
++ */
++
++static void
++nv50_graph_construct_gene_m2mf(struct nouveau_grctx *ctx)
++{
++ /* m2mf state */
++ 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 */
++ xf_emit (ctx, 1, 0); /* OFFSET_IN */
++ xf_emit (ctx, 1, 0); /* OFFSET_OUT */
++ xf_emit (ctx, 1, 0); /* PITCH_IN */
++ xf_emit (ctx, 1, 0); /* PITCH_OUT */
++ xf_emit (ctx, 1, 0); /* LINE_LENGTH */
++ xf_emit (ctx, 1, 0); /* LINE_COUNT */
++ xf_emit (ctx, 1, 0x21); /* FORMAT: bits 0-4 INPUT_INC, bits 5-9 OUTPUT_INC */
++ xf_emit (ctx, 1, 1); /* LINEAR_IN */
++ xf_emit (ctx, 1, 0x2); /* TILING_MODE_IN: bits 0-2 y tiling, bits 3-5 z tiling */
++ xf_emit (ctx, 1, 0x100); /* TILING_PITCH_IN */
++ xf_emit (ctx, 1, 0x100); /* TILING_HEIGHT_IN */
++ xf_emit (ctx, 1, 1); /* TILING_DEPTH_IN */
++ xf_emit (ctx, 1, 0); /* TILING_POSITION_IN_Z */
++ xf_emit (ctx, 1, 0); /* TILING_POSITION_IN */
++ xf_emit (ctx, 1, 1); /* LINEAR_OUT */
++ xf_emit (ctx, 1, 0x2); /* TILING_MODE_OUT: bits 0-2 y tiling, bits 3-5 z tiling */
++ xf_emit (ctx, 1, 0x100); /* TILING_PITCH_OUT */
++ xf_emit (ctx, 1, 0x100); /* TILING_HEIGHT_OUT */
++ xf_emit (ctx, 1, 1); /* TILING_DEPTH_OUT */
++ xf_emit (ctx, 1, 0); /* TILING_POSITION_OUT_Z */
++ xf_emit (ctx, 1, 0); /* TILING_POSITION_OUT */
++ xf_emit (ctx, 1, 0); /* OFFSET_IN_HIGH */
++ xf_emit (ctx, 1, 0); /* OFFSET_OUT_HIGH */
++}
++
++static void
++nv50_graph_construct_gene_unk1(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);
++ switch (dev_priv->chipset) {
++ case 0x50:
++ case 0x86:
++ case 0x98:
++ case 0xaa:
++ case 0xac:
++ xf_emit(ctx, 0x542, 0);
++ break;
++ case 0x84:
++ case 0x92:
++ case 0x94:
++ case 0x96:
++ xf_emit(ctx, 0x942, 0);
++ break;
++ case 0xa0:
++ case 0xa3:
++ xf_emit(ctx, 0x2042, 0);
++ break;
++ case 0xa5:
++ case 0xa8:
++ xf_emit(ctx, 0x842, 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);
++}
++
++static void
++nv50_graph_construct_gene_unk10(struct nouveau_grctx *ctx)
++{
++ /* 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);
++}
++
++static void
++nv50_graph_construct_gene_unk2(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, 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);
++ 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);
++ if (dev_priv->chipset != 0x50)
++ xf_emit(ctx, 0x28, 0);
++ 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);
++}
++
++static void
++nv50_graph_construct_gene_unk3(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);
++}
++
++static void
++nv50_graph_construct_gene_unk4(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);
++}
++
++static void
++nv50_graph_construct_gene_unk5(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);
++ else
++ xf_emit(ctx, 0x1c4b, 0);
++ xf_emit(ctx, 2, 4);
++ xf_emit(ctx, 1, 0x8100c12);
++ 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);
++ 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);
++}
++
++static void
++nv50_graph_construct_gene_unk6(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);
++ else if (dev_priv->chipset >= 0xa0)
++ xf_emit(ctx, 0xf, 0);
++ 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);
++ if (dev_priv->chipset == 0x50)
++ xf_emit(ctx, 1, 0x3ff);
++ else
++ xf_emit(ctx, 1, 0x7ff);
++ 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);
++ 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);
++ 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);
++ else
++ xf_emit(ctx, 0x5a, 0);
++ xf_emit(ctx, 1, 0xf);
++ 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);
++ else
++ xf_emit(ctx, 0x833, 0);
++ xf_emit(ctx, 1, 0xf);
++ xf_emit(ctx, 0xf, 0);
++}
++
++static void
++nv50_graph_construct_gene_unk7(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);
++}
++
++static void
++nv50_graph_construct_gene_unk8(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);
++}
++
++static void
++nv50_graph_construct_gene_unk9(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);
++ 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, 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 (dev_priv->chipset != 0x50)
++ xf_emit(ctx, 1, 3);
++ 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);
++ if (dev_priv->chipset != 0x50) {
++ xf_emit(ctx, 1, 0);
++ xf_emit(ctx, 1, 3);
++ }
++ if (dev_priv->chipset < 0xa0)
++ xf_emit(ctx, 0x26, 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);
++ if (dev_priv->chipset == 0x50)
++ xf_emit(ctx, 1, 0x3ff);
++ 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);
++}
++
++static void
++nv50_graph_construct_gene_ropc(struct nouveau_grctx *ctx)
++{
++ struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
++ int magic2;
++ if (dev_priv->chipset == 0x50) {
++ magic2 = 0x00003e60;
++ } else if (dev_priv->chipset <= 0xa0 || dev_priv->chipset >= 0xaa) {
++ 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);
++ 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);
++ if (dev_priv->chipset != 0xa0) {
++ if (dev_priv->chipset > 0xa0 && dev_priv->chipset < 0xaa)
++ xf_emit(ctx, 1, 0);
++ else
++ xf_emit(ctx, 1, 0x15);
++ }
++ 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);
++ 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, 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);
++ } else {
++ if (dev_priv->chipset >= 0xa0)
++ xf_emit(ctx, 0x1b, 0);
++ else
++ xf_emit(ctx, 0x15, 0);
++ }
++ 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);
++ 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, 1, 0x11);
++ xf_emit(ctx, 1, 1);
++ xf_emit(ctx, 0x5b, 0);
++}
++
++static void
++nv50_graph_construct_xfer_tp_x1(struct nouveau_grctx *ctx)
++{
++ struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
++ int magic3;
++ if (dev_priv->chipset == 0x50)
++ magic3 = 0x1000;
++ else if (dev_priv->chipset == 0x86 || dev_priv->chipset == 0x98 || dev_priv->chipset >= 0xa8)
++ magic3 = 0x1e00;
++ else
++ 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);
++ else if (dev_priv->chipset >= 0xa0)
++ xf_emit(ctx, 0x14, 0);
++ else
++ xf_emit(ctx, 0x15, 0);
++ xf_emit(ctx, 2, 4);
++ if (dev_priv->chipset >= 0xa0)
++ xf_emit(ctx, 1, 0x03020100);
++ 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);
++ 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);
++ 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);
++ if (dev_priv->chipset == 0x94 || dev_priv->chipset == 0x96)
++ xf_emit(ctx, 0x1024, 0);
++ 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);
++ else
++ xf_emit(ctx, 0x414, 0);
++ xf_emit(ctx, 1, 4);
++ xf_emit(ctx, 1, 3);
++ xf_emit(ctx, 2, 0);
++}
++
++static void
++nv50_graph_construct_xfer_tp_x2(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) {
++ 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, 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);
++ if(dev_priv->chipset == 0x50)
++ xf_emit(ctx, 1, 0);
++ 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);
++ 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);
++}
++
++static void
++nv50_graph_construct_xfer_tp_x3(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);
++ if (dev_priv->chipset == 0x50)
++ xf_emit(ctx, 2, 0);
++ 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);
++}
++
++static void
++nv50_graph_construct_xfer_tp_x4(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);
++ }
++}
++
++static void
++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);
++ } 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);
++ }
++}
++
++static void
++nv50_graph_construct_xfer_tp2(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;
++ 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);
++ 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, 1);
++ xf_emit(ctx, 2, 0);
++ 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);
++ else if (dev_priv->chipset >= 0xa0)
++ xf_emit(ctx, 0xc, 0);
++ else
++ xf_emit(ctx, 0xa, 0);
++ }
++ xf_emit(ctx, 1, 0x08100c12);
++ xf_emit(ctx, 1, 0);
++ if (dev_priv->chipset >= 0xa0) {
++ xf_emit(ctx, 1, 0x1fe21);
++ }
++ 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, 1, 4);
++ if (dev_priv->chipset == 0x50)
++ xf_emit(ctx, 0x3a0, 0);
++ else if (dev_priv->chipset < 0x94)
++ xf_emit(ctx, 0x3a2, 0);
++ else if (dev_priv->chipset == 0x98 || dev_priv->chipset == 0xaa)
++ 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, 0x2d, 0);
++}
++
++static void
++nv50_graph_construct_xfer2(struct nouveau_grctx *ctx)
++{
++ struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
++ int i;
++ uint32_t offset;
++ uint32_t units = nv_rd32 (ctx->dev, 0x1540);
++ int size = 0;
++
++ offset = (ctx->ctxvals_pos+0x3f)&~0x3f;
++
++ if (dev_priv->chipset < 0xa0) {
++ for (i = 0; i < 8; i++) {
++ ctx->ctxvals_pos = offset + i;
++ if (i == 0)
++ xf_emit(ctx, 1, 0x08100c12);
++ if (units & (1 << i))
++ nv50_graph_construct_xfer_tp2(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);
++ if (units & (1 << 0))
++ nv50_graph_construct_xfer_tp2(ctx);
++ if (units & (1 << 1))
++ nv50_graph_construct_xfer_tp2(ctx);
++ if ((ctx->ctxvals_pos-offset)/8 > size)
++ size = (ctx->ctxvals_pos-offset)/8;
++
++ /* Strand 0: TPs 2, 3 */
++ ctx->ctxvals_pos = offset + 1;
++ if (units & (1 << 2))
++ nv50_graph_construct_xfer_tp2(ctx);
++ if (units & (1 << 3))
++ nv50_graph_construct_xfer_tp2(ctx);
++ if ((ctx->ctxvals_pos-offset)/8 > size)
++ size = (ctx->ctxvals_pos-offset)/8;
++
++ /* Strand 0: TPs 4, 5, 6 */
++ ctx->ctxvals_pos = offset + 2;
++ if (units & (1 << 4))
++ nv50_graph_construct_xfer_tp2(ctx);
++ if (units & (1 << 5))
++ nv50_graph_construct_xfer_tp2(ctx);
++ if (units & (1 << 6))
++ nv50_graph_construct_xfer_tp2(ctx);
++ if ((ctx->ctxvals_pos-offset)/8 > size)
++ size = (ctx->ctxvals_pos-offset)/8;
++
++ /* Strand 0: TPs 7, 8, 9 */
++ ctx->ctxvals_pos = offset + 3;
++ if (units & (1 << 7))
++ nv50_graph_construct_xfer_tp2(ctx);
++ if (units & (1 << 8))
++ nv50_graph_construct_xfer_tp2(ctx);
++ if (units & (1 << 9))
++ nv50_graph_construct_xfer_tp2(ctx);
++ if ((ctx->ctxvals_pos-offset)/8 > size)
++ size = (ctx->ctxvals_pos-offset)/8;
++ }
++ ctx->ctxvals_pos = offset + size * 8;
++ ctx->ctxvals_pos = (ctx->ctxvals_pos+0x3f)&~0x3f;
++ cp_lsr (ctx, offset);
++ cp_out (ctx, CP_SET_XFER_POINTER);
++ cp_lsr (ctx, size);
++ cp_out (ctx, CP_SEEK_2);
++ cp_out (ctx, CP_XFER_2);
++ cp_wait(ctx, XFER, BUSY);
++}
+diff --git a/drivers/gpu/drm/nouveau/nv50_instmem.c b/drivers/gpu/drm/nouveau/nv50_instmem.c
+index f0dc4e3..5f21df3 100644
+--- a/drivers/gpu/drm/nouveau/nv50_instmem.c
++++ b/drivers/gpu/drm/nouveau/nv50_instmem.c
+@@ -63,9 +63,10 @@ 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;
+ int ret, i;
+- uint32_t v, save_nv001700;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+@@ -76,17 +77,12 @@ nv50_instmem_init(struct drm_device *dev)
+ for (i = 0x1700; i <= 0x1710; i += 4)
+ priv->save1700[(i-0x1700)/4] = nv_rd32(dev, i);
+
+- if (dev_priv->chipset == 0xaa || dev_priv->chipset == 0xac)
+- dev_priv->vram_sys_base = nv_rd32(dev, 0x100e10) << 12;
+- else
+- dev_priv->vram_sys_base = 0;
+-
+ /* Reserve the last MiB of VRAM, we should probably try to avoid
+ * setting up the below tables over the top of the VBIOS image at
+ * some point.
+ */
+ dev_priv->ramin_rsvd_vram = 1 << 20;
+- c_offset = nouveau_mem_fb_amount(dev) - dev_priv->ramin_rsvd_vram;
++ c_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;
+@@ -106,7 +102,7 @@ nv50_instmem_init(struct drm_device *dev)
+ dev_priv->vm_gart_size = NV50_VM_BLOCK;
+
+ dev_priv->vm_vram_base = dev_priv->vm_gart_base + dev_priv->vm_gart_size;
+- dev_priv->vm_vram_size = nouveau_mem_fb_amount(dev);
++ 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);
+@@ -189,8 +185,8 @@ nv50_instmem_init(struct drm_device *dev)
+
+ i = 0;
+ while (v < dev_priv->vram_sys_base + c_offset + c_size) {
+- BAR0_WI32(priv->pramin_pt->gpuobj, i + 0, v);
+- BAR0_WI32(priv->pramin_pt->gpuobj, i + 4, 0x00000000);
++ 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;
+ }
+@@ -390,7 +386,7 @@ nv50_instmem_populate(struct drm_device *dev, struct nouveau_gpuobj *gpuobj,
+ if (gpuobj->im_backing)
+ return -EINVAL;
+
+- *sz = (*sz + (NV50_INSTMEM_PAGE_SIZE-1)) & ~(NV50_INSTMEM_PAGE_SIZE-1);
++ *sz = ALIGN(*sz, NV50_INSTMEM_PAGE_SIZE);
+ if (*sz == 0)
+ return -EINVAL;
+
+diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c
+index c2fff54..b11eaf9 100644
+--- a/drivers/gpu/drm/nouveau/nv50_sor.c
++++ b/drivers/gpu/drm/nouveau/nv50_sor.c
+@@ -211,7 +211,7 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
+ mode_ctl = 0x0200;
+ break;
+ case OUTPUT_DP:
+- mode_ctl |= 0x00050000;
++ mode_ctl |= (nv_encoder->dp.mc_unknown << 16);
+ if (nv_encoder->dcb->sorconf.link & 1)
+ mode_ctl |= 0x00000800;
+ else
+@@ -274,6 +274,7 @@ static const struct drm_encoder_funcs nv50_sor_encoder_funcs = {
+ int
+ nv50_sor_create(struct drm_device *dev, struct dcb_entry *entry)
+ {
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_encoder *nv_encoder = NULL;
+ struct drm_encoder *encoder;
+ bool dum;
+@@ -319,5 +320,32 @@ nv50_sor_create(struct drm_device *dev, struct dcb_entry *entry)
+ encoder->possible_crtcs = entry->heads;
+ encoder->possible_clones = 0;
+
++ if (nv_encoder->dcb->type == OUTPUT_DP) {
++ int or = nv_encoder->or, link = !(entry->dpconf.sor.link & 1);
++ uint32_t tmp;
++
++ if (dev_priv->chipset < 0x90 ||
++ dev_priv->chipset == 0x92 || dev_priv->chipset == 0xa0)
++ tmp = nv_rd32(dev, NV50_PDISPLAY_SOR_MODE_CTRL_C(or));
++ else
++ tmp = nv_rd32(dev, NV90_PDISPLAY_SOR_MODE_CTRL_C(or));
++
++ switch ((tmp & 0x00000f00) >> 8) {
++ case 8:
++ case 9:
++ nv_encoder->dp.mc_unknown = (tmp & 0x000f0000) >> 16;
++ tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link));
++ nv_encoder->dp.unk0 = tmp & 0x000001fc;
++ tmp = nv_rd32(dev, NV50_SOR_DP_UNK128(or, link));
++ nv_encoder->dp.unk1 = tmp & 0x010f7f3f;
++ break;
++ default:
++ break;
++ }
++
++ if (!nv_encoder->dp.mc_unknown)
++ nv_encoder->dp.mc_unknown = 5;
++ }
++
+ return 0;
+ }
+--
+1.7.0.1
+
OpenPOWER on IntegriCloud