diff options
author | Alexandre Oliva <lxoliva@fsfla.org> | 2010-08-14 04:03:56 +0000 |
---|---|---|
committer | Alexandre Oliva <lxoliva@fsfla.org> | 2010-08-14 04:03:56 +0000 |
commit | cb3073a52ff53c702442b12ce3ba46c8ac860d1b (patch) | |
tree | 429a21d53929335b2dd411c50d8acda4fd739cfe /freed-ora/tags/f13/2.6.33.5-131.fc13/drm-nouveau-updates.patch | |
parent | eaf70c475fea4240c562209affe5c163c3f6bd0c (diff) | |
download | linux-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.patch | 7669 |
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, <h); + 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 + |