9175 lines
290 KiB
Diff
9175 lines
290 KiB
Diff
From 1fe467ac55c8503078f679b1f503e4895d6c5895 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 1/3] 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>
|
|
|
|
drm/nouveau: fix POST detection for certain chipsets
|
|
|
|
We totally fail at detecting un-POSTed chipsets prior to G80. This commit
|
|
changes the pre-G80 POST detection to read the programmed horizontal total
|
|
from CRTC 0, and assume the card isn't POSTed if it's 0.
|
|
|
|
NVIDIA use some other heuristics more similar to what we do on G80, but I
|
|
wasted quite a long time trying to figure out the exact specifics of what
|
|
they do so we can try this for a bit instead.
|
|
|
|
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
|
|
|
|
drm/nv40: allow cold-booting of nv4x chipsets
|
|
|
|
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
|
|
|
|
drm/nouveau: fix init table handlers to return proper error codes
|
|
|
|
We really want to be able to distinguish between INIT_DONE and an actual
|
|
error sometimes. This commit fixes up several lazy "return 0;" to be
|
|
actual error codes, and explicitly reserves "0" as "success, but stop
|
|
parsing this table".
|
|
|
|
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
|
|
|
|
drm/nouveau: display error message for any failed init table opcode
|
|
|
|
Some handlers don't report specific errors, but we still *really* want to
|
|
know if we failed to parse a complete init table.
|
|
|
|
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
|
|
|
|
drm/nouveau: ensure we've parsed i2c table entry for INIT_*I2C* handlers
|
|
|
|
We may not have parsed the entry yet if the i2c_index is for an i2c bus
|
|
that's not referenced by a DCB encoder.
|
|
|
|
This could be done oh so much more nicely, except we have to care about
|
|
prehistoric DCB tables too, and they make life painful.
|
|
|
|
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
|
|
|
|
drm/nouveau: support init table i2c device identifier 0x81
|
|
|
|
It appears to be meant to reference the second "default index".
|
|
|
|
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
|
|
|
|
drm/nouveau: fix i2c-related init table handlers
|
|
|
|
Mutliple issues. INIT_ZM_I2C_BYTE/INIT_I2C_BYTE didn't even try and
|
|
use the register value, and all the handlers were using the wrong
|
|
slave address.
|
|
|
|
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
|
|
|
|
drm/nv50: cast IGP memory location to u64 before shifting
|
|
|
|
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
|
|
|
|
drm/nouveau: match U/DP script against SOR link
|
|
|
|
It appears version 0x21 'U' and 'd' tables require us to take the SOR link
|
|
into account when selecting the appropriate table for a particular output.
|
|
|
|
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
|
|
|
|
drm/nv50: fix memory detection for cards with >=4GiB VRAM
|
|
|
|
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
|
|
|
|
drm/nouveau: completely fail init if we fail to map the PRAMIN BAR
|
|
|
|
On cards where there's a specific BAR for PRAMIN, we used to try and fall
|
|
back to the "legacy" aperture within the mmio BAR.
|
|
|
|
This is doomed to cause problems, so lets just fail completely as there's
|
|
obviously something else very wrong anyway.
|
|
|
|
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
|
|
|
|
drm/nouveau: Don't clear AGPCMD completely on INIT_RESET.
|
|
|
|
We just need to clear the SBA and ENABLE bits to reset the AGP
|
|
controller: If the AGP bridge was configured to use "fast writes",
|
|
clearing the FW bit would break the subsequent MMIO writes and
|
|
eventually end with a lockup.
|
|
|
|
Note that all the BIOSes I've seen do the same as we did (it works for
|
|
them because they don't use MMIO), OTOH the blob leaves FW untouched.
|
|
|
|
Signed-off-by: Francisco Jerez <currojerez@riseup.net>
|
|
|
|
drm/nouveau: Ignore broken legacy I2C entries.
|
|
|
|
The nv05 card in the bug report [1] doesn't have usable I2C port
|
|
register offsets (they're all filled with zeros). Ignore them and use
|
|
the defaults.
|
|
|
|
[1] http://bugs.launchpad.net/bugs/569505
|
|
|
|
Signed-off-by: Francisco Jerez <currojerez@riseup.net>
|
|
|
|
drm/nv50: use alternate source of SOR_MODE_CTRL for DP hack
|
|
|
|
Fixes module unload+reload on Dell M4500, where the "normal" registers
|
|
get reset to 0.
|
|
|
|
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
|
|
|
|
drm/nv50: fix duallink_possible calculation for DCB 4.0 cards
|
|
|
|
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
|
|
|
|
drm/nv50: obey dcb->duallink_possible
|
|
|
|
It was once assumed that all G8x had dual-link TMDS everywhere, this isn't
|
|
actually the case - especially considering passive DP->DVI converters and
|
|
some HDMI connectors only support single-link.
|
|
|
|
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
|
|
|
|
drm/nouveau: fix dual-link displays when plugged into single-link outputs
|
|
|
|
When selecting the native mode for a display we weren't taking into account
|
|
whether or not it was actually supported on that particular output.
|
|
|
|
This patch modifies our native mode selection to run all modes through
|
|
mode_valid() first.
|
|
|
|
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
|
|
|
|
drm/nouveau: reduce usage of fence spinlock to when absolutely necessary
|
|
|
|
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
|
|
|
|
drm/nouveau: move LVDS detection back to connector detect() time
|
|
|
|
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
|
|
|
|
drm/nouveau: Put the dithering check back in nouveau_connector_create.
|
|
|
|
a7b9f9e5adef dropped it by accident.
|
|
|
|
Signed-off-by: Francisco Jerez <currojerez@riseup.net>
|
|
Tested-by: Thibaut Girka <thib@sitedethib.com>
|
|
|
|
drm/nouveau: set encoder for lvds
|
|
|
|
fixes oops in nouveau_connector_get_modes with nv_encoder is NULL
|
|
|
|
Signed-off-by: Albert Damen <albrt@gmx.net>
|
|
Signed-off-by: Francisco Jerez <currojerez@riseup.net>
|
|
|
|
drm/nouveau: tidy connector/encoder creation a little
|
|
|
|
Create connectors before encoders to avoid having to do another loop across
|
|
encoder list whenever we create a new connector. This allows us to pass
|
|
the connector to the encoder creation functions, and avoid using a
|
|
create_resources() callback since we can now call it directly.
|
|
|
|
This can also potentially modify the connector ordering on nv50. On cards
|
|
where the DCB connector and encoder tables are in the same order, things
|
|
will be unchanged. However, there's some cards where the ordering between
|
|
the tables differ, and in one case, leads us to naming the connectors
|
|
"wrongly".
|
|
|
|
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
|
|
|
|
drm/nv50: fix regression caused by ed15e77b6ee7c4fa6f50c18b3325e7f96ed3aade
|
|
|
|
It became possible for us to have connectors present without any encoders
|
|
attached (TV out, we don't support TVDAC yet), which caused the DDX to
|
|
segfault.
|
|
|
|
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
|
|
|
|
drm/nv04: fix regression caused by ed15e77b6ee7c4fa6f50c18b3325e7f96ed3aade
|
|
|
|
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
|
|
---
|
|
drivers/gpu/drm/nouveau/Makefile | 7 +-
|
|
drivers/gpu/drm/nouveau/nouveau_bios.c | 1057 ++++++++-----
|
|
drivers/gpu/drm/nouveau/nouveau_bios.h | 133 +-
|
|
drivers/gpu/drm/nouveau/nouveau_bo.c | 68 +-
|
|
drivers/gpu/drm/nouveau/nouveau_calc.c | 4 +-
|
|
drivers/gpu/drm/nouveau/nouveau_channel.c | 17 +-
|
|
drivers/gpu/drm/nouveau/nouveau_connector.c | 383 +++---
|
|
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 | 70 +-
|
|
drivers/gpu/drm/nouveau/nouveau_encoder.h | 7 +-
|
|
drivers/gpu/drm/nouveau/nouveau_fence.c | 31 +-
|
|
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 | 129 +-
|
|
drivers/gpu/drm/nouveau/nouveau_reg.h | 1 +
|
|
drivers/gpu/drm/nouveau/nouveau_sgdma.c | 18 +
|
|
drivers/gpu/drm/nouveau/nouveau_state.c | 39 +-
|
|
drivers/gpu/drm/nouveau/nv04_crtc.c | 6 +-
|
|
drivers/gpu/drm/nouveau/nv04_dac.c | 15 +-
|
|
drivers/gpu/drm/nouveau/nv04_dfp.c | 12 +-
|
|
drivers/gpu/drm/nouveau/nv04_display.c | 64 +-
|
|
drivers/gpu/drm/nouveau/nv04_fbcon.c | 6 +-
|
|
drivers/gpu/drm/nouveau/nv04_fifo.c | 5 +
|
|
drivers/gpu/drm/nouveau/nv04_graph.c | 3 +-
|
|
drivers/gpu/drm/nouveau/nv04_tv.c | 10 +-
|
|
drivers/gpu/drm/nouveau/nv17_tv.c | 12 +-
|
|
drivers/gpu/drm/nouveau/nv40_fifo.c | 7 +-
|
|
drivers/gpu/drm/nouveau/nv40_graph.c | 21 +
|
|
drivers/gpu/drm/nouveau/nv50_calc.c | 87 +
|
|
drivers/gpu/drm/nouveau/nv50_crtc.c | 46 +-
|
|
drivers/gpu/drm/nouveau/nv50_dac.c | 13 +-
|
|
drivers/gpu/drm/nouveau/nv50_display.c | 122 +-
|
|
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 | 42 +-
|
|
47 files changed, 4702 insertions(+), 1121 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..1803cc4 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
|
|
@@ -256,6 +257,11 @@ static bool NVShadowVBIOS(struct drm_device *dev, uint8_t *data)
|
|
struct init_tbl_entry {
|
|
char *name;
|
|
uint8_t id;
|
|
+ /* Return:
|
|
+ * > 0: success, length of opcode
|
|
+ * 0: success, but abort further parsing of table (INIT_DONE etc)
|
|
+ * < 0: failure, table parsing will be aborted
|
|
+ */
|
|
int (*handler)(struct nvbios *, uint16_t, struct init_exec *);
|
|
};
|
|
|
|
@@ -311,11 +317,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 +426,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 +653,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 +695,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 +706,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 */
|
|
@@ -709,29 +715,121 @@ static int dcb_entry_idx_from_crtchead(struct drm_device *dev)
|
|
return dcb_entry;
|
|
}
|
|
|
|
+static int
|
|
+read_dcb_i2c_entry(struct drm_device *dev, int dcb_version, uint8_t *i2ctable, int index, struct dcb_i2c_entry *i2c)
|
|
+{
|
|
+ uint8_t dcb_i2c_ver = dcb_version, headerlen = 0, entry_len = 4;
|
|
+ int i2c_entries = DCB_MAX_NUM_I2C_ENTRIES;
|
|
+ int recordoffset = 0, rdofs = 1, wrofs = 0;
|
|
+ uint8_t port_type = 0;
|
|
+
|
|
+ if (!i2ctable)
|
|
+ return -EINVAL;
|
|
+
|
|
+ if (dcb_version >= 0x30) {
|
|
+ if (i2ctable[0] != dcb_version) /* necessary? */
|
|
+ NV_WARN(dev,
|
|
+ "DCB I2C table version mismatch (%02X vs %02X)\n",
|
|
+ i2ctable[0], dcb_version);
|
|
+ dcb_i2c_ver = i2ctable[0];
|
|
+ headerlen = i2ctable[1];
|
|
+ if (i2ctable[2] <= DCB_MAX_NUM_I2C_ENTRIES)
|
|
+ i2c_entries = i2ctable[2];
|
|
+ else
|
|
+ NV_WARN(dev,
|
|
+ "DCB I2C table has more entries than indexable "
|
|
+ "(%d entries, max %d)\n", i2ctable[2],
|
|
+ DCB_MAX_NUM_I2C_ENTRIES);
|
|
+ entry_len = i2ctable[3];
|
|
+ /* [4] is i2c_default_indices, read in parse_dcb_table() */
|
|
+ }
|
|
+ /*
|
|
+ * It's your own fault if you call this function on a DCB 1.1 BIOS --
|
|
+ * the test below is for DCB 1.2
|
|
+ */
|
|
+ if (dcb_version < 0x14) {
|
|
+ recordoffset = 2;
|
|
+ rdofs = 0;
|
|
+ wrofs = 1;
|
|
+ }
|
|
+
|
|
+ if (index == 0xf)
|
|
+ return 0;
|
|
+ if (index >= i2c_entries) {
|
|
+ NV_ERROR(dev, "DCB I2C index too big (%d >= %d)\n",
|
|
+ index, i2ctable[2]);
|
|
+ return -ENOENT;
|
|
+ }
|
|
+ if (i2ctable[headerlen + entry_len * index + 3] == 0xff) {
|
|
+ NV_ERROR(dev, "DCB I2C entry invalid\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ if (dcb_i2c_ver >= 0x30) {
|
|
+ port_type = i2ctable[headerlen + recordoffset + 3 + entry_len * index];
|
|
+
|
|
+ /*
|
|
+ * Fixup for chips using same address offset for read and
|
|
+ * write.
|
|
+ */
|
|
+ if (port_type == 4) /* seen on C51 */
|
|
+ rdofs = wrofs = 1;
|
|
+ if (port_type >= 5) /* G80+ */
|
|
+ rdofs = wrofs = 0;
|
|
+ }
|
|
+
|
|
+ if (dcb_i2c_ver >= 0x40) {
|
|
+ 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];
|
|
+ i2c->write = i2ctable[headerlen + recordoffset + wrofs + entry_len * index];
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
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;
|
|
+ else
|
|
+ if (i2c_index == 0x81)
|
|
+ i2c_index = (dcb->i2c_default_indices & 0xf0) >> 4;
|
|
+
|
|
+ if (i2c_index > DCB_MAX_NUM_I2C_ENTRIES) {
|
|
+ NV_ERROR(dev, "invalid i2c_index 0x%x\n", i2c_index);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ /* Make sure i2c table entry has been parsed, it may not
|
|
+ * have been if this is a bus not referenced by a DCB encoder
|
|
+ */
|
|
+ read_dcb_i2c_entry(dev, dcb->version, dcb->i2c_table,
|
|
+ i2c_index, &dcb->i2c[i2c_index]);
|
|
|
|
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 +842,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 +855,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;
|
|
@@ -817,7 +915,7 @@ init_io_restrict_prog(struct nvbios *bios, uint16_t offset,
|
|
NV_ERROR(bios->dev,
|
|
"0x%04X: Config 0x%02X exceeds maximal bound 0x%02X\n",
|
|
offset, config, count);
|
|
- return 0;
|
|
+ return -EINVAL;
|
|
}
|
|
|
|
configval = ROM32(bios->data[offset + 11 + config * 4]);
|
|
@@ -919,7 +1017,7 @@ init_io_restrict_pll(struct nvbios *bios, uint16_t offset,
|
|
NV_ERROR(bios->dev,
|
|
"0x%04X: Config 0x%02X exceeds maximal bound 0x%02X\n",
|
|
offset, config, count);
|
|
- return 0;
|
|
+ return -EINVAL;
|
|
}
|
|
|
|
freq = ROM16(bios->data[offset + 12 + config * 2]);
|
|
@@ -1066,6 +1164,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)
|
|
{
|
|
@@ -1169,7 +1387,7 @@ init_io_restrict_pll2(struct nvbios *bios, uint16_t offset,
|
|
NV_ERROR(bios->dev,
|
|
"0x%04X: Config 0x%02X exceeds maximal bound 0x%02X\n",
|
|
offset, config, count);
|
|
- return 0;
|
|
+ return -EINVAL;
|
|
}
|
|
|
|
freq = ROM32(bios->data[offset + 11 + config * 4]);
|
|
@@ -1230,12 +1448,11 @@ init_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
*/
|
|
|
|
uint8_t i2c_index = bios->data[offset + 1];
|
|
- uint8_t i2c_address = bios->data[offset + 2];
|
|
+ uint8_t i2c_address = bios->data[offset + 2] >> 1;
|
|
uint8_t count = bios->data[offset + 3];
|
|
- int len = 4 + count * 3;
|
|
struct nouveau_i2c_chan *chan;
|
|
- struct i2c_msg msg;
|
|
- int i;
|
|
+ int len = 4 + count * 3;
|
|
+ int ret, i;
|
|
|
|
if (!iexec->execute)
|
|
return len;
|
|
@@ -1246,35 +1463,34 @@ init_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
|
|
chan = init_i2c_device_find(bios->dev, i2c_index);
|
|
if (!chan)
|
|
- return 0;
|
|
+ return -ENODEV;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
- uint8_t i2c_reg = bios->data[offset + 4 + i * 3];
|
|
+ uint8_t reg = bios->data[offset + 4 + i * 3];
|
|
uint8_t mask = bios->data[offset + 5 + i * 3];
|
|
uint8_t data = bios->data[offset + 6 + i * 3];
|
|
- uint8_t value;
|
|
+ union i2c_smbus_data val;
|
|
|
|
- msg.addr = i2c_address;
|
|
- msg.flags = I2C_M_RD;
|
|
- msg.len = 1;
|
|
- msg.buf = &value;
|
|
- if (i2c_transfer(&chan->adapter, &msg, 1) != 1)
|
|
- return 0;
|
|
+ ret = i2c_smbus_xfer(&chan->adapter, i2c_address, 0,
|
|
+ I2C_SMBUS_READ, reg,
|
|
+ I2C_SMBUS_BYTE_DATA, &val);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
|
|
BIOSLOG(bios, "0x%04X: I2CReg: 0x%02X, Value: 0x%02X, "
|
|
"Mask: 0x%02X, Data: 0x%02X\n",
|
|
- offset, i2c_reg, value, mask, data);
|
|
+ offset, reg, val.byte, mask, data);
|
|
|
|
- value = (value & mask) | data;
|
|
+ if (!bios->execute)
|
|
+ continue;
|
|
|
|
- if (bios->execute) {
|
|
- msg.addr = i2c_address;
|
|
- msg.flags = 0;
|
|
- msg.len = 1;
|
|
- msg.buf = &value;
|
|
- if (i2c_transfer(&chan->adapter, &msg, 1) != 1)
|
|
- return 0;
|
|
- }
|
|
+ val.byte &= mask;
|
|
+ val.byte |= data;
|
|
+ ret = i2c_smbus_xfer(&chan->adapter, i2c_address, 0,
|
|
+ I2C_SMBUS_WRITE, reg,
|
|
+ I2C_SMBUS_BYTE_DATA, &val);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
}
|
|
|
|
return len;
|
|
@@ -1300,12 +1516,11 @@ init_zm_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
*/
|
|
|
|
uint8_t i2c_index = bios->data[offset + 1];
|
|
- uint8_t i2c_address = bios->data[offset + 2];
|
|
+ uint8_t i2c_address = bios->data[offset + 2] >> 1;
|
|
uint8_t count = bios->data[offset + 3];
|
|
- int len = 4 + count * 2;
|
|
struct nouveau_i2c_chan *chan;
|
|
- struct i2c_msg msg;
|
|
- int i;
|
|
+ int len = 4 + count * 2;
|
|
+ int ret, i;
|
|
|
|
if (!iexec->execute)
|
|
return len;
|
|
@@ -1316,23 +1531,25 @@ init_zm_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
|
|
chan = init_i2c_device_find(bios->dev, i2c_index);
|
|
if (!chan)
|
|
- return 0;
|
|
+ return -ENODEV;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
- uint8_t i2c_reg = bios->data[offset + 4 + i * 2];
|
|
- uint8_t data = bios->data[offset + 5 + i * 2];
|
|
+ uint8_t reg = bios->data[offset + 4 + i * 2];
|
|
+ union i2c_smbus_data val;
|
|
+
|
|
+ val.byte = bios->data[offset + 5 + i * 2];
|
|
|
|
BIOSLOG(bios, "0x%04X: I2CReg: 0x%02X, Data: 0x%02X\n",
|
|
- offset, i2c_reg, data);
|
|
-
|
|
- if (bios->execute) {
|
|
- msg.addr = i2c_address;
|
|
- msg.flags = 0;
|
|
- msg.len = 1;
|
|
- msg.buf = &data;
|
|
- if (i2c_transfer(&chan->adapter, &msg, 1) != 1)
|
|
- return 0;
|
|
- }
|
|
+ offset, reg, val.byte);
|
|
+
|
|
+ if (!bios->execute)
|
|
+ continue;
|
|
+
|
|
+ ret = i2c_smbus_xfer(&chan->adapter, i2c_address, 0,
|
|
+ I2C_SMBUS_WRITE, reg,
|
|
+ I2C_SMBUS_BYTE_DATA, &val);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
}
|
|
|
|
return len;
|
|
@@ -1356,7 +1573,7 @@ init_zm_i2c(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
*/
|
|
|
|
uint8_t i2c_index = bios->data[offset + 1];
|
|
- uint8_t i2c_address = bios->data[offset + 2];
|
|
+ uint8_t i2c_address = bios->data[offset + 2] >> 1;
|
|
uint8_t count = bios->data[offset + 3];
|
|
int len = 4 + count;
|
|
struct nouveau_i2c_chan *chan;
|
|
@@ -1373,7 +1590,7 @@ init_zm_i2c(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
|
|
chan = init_i2c_device_find(bios->dev, i2c_index);
|
|
if (!chan)
|
|
- return 0;
|
|
+ return -ENODEV;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
data[i] = bios->data[offset + 4 + i];
|
|
@@ -1387,7 +1604,7 @@ init_zm_i2c(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
msg.len = count;
|
|
msg.buf = data;
|
|
if (i2c_transfer(&chan->adapter, &msg, 1) != 1)
|
|
- return 0;
|
|
+ return -EIO;
|
|
}
|
|
|
|
return len;
|
|
@@ -1426,7 +1643,7 @@ init_tmds(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
|
|
reg = get_tmds_index_reg(bios->dev, mlv);
|
|
if (!reg)
|
|
- return 0;
|
|
+ return -EINVAL;
|
|
|
|
bios_wr32(bios, reg,
|
|
tmdsaddr | NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE);
|
|
@@ -1470,7 +1687,7 @@ init_zm_tmds_group(struct nvbios *bios, uint16_t offset,
|
|
|
|
reg = get_tmds_index_reg(bios->dev, mlv);
|
|
if (!reg)
|
|
- return 0;
|
|
+ return -EINVAL;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
uint8_t tmdsaddr = bios->data[offset + 3 + i * 2];
|
|
@@ -1909,7 +2126,8 @@ init_reset(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
/* no iexec->execute check by design */
|
|
|
|
pci_nv_19 = bios_rd32(bios, NV_PBUS_PCI_NV_19);
|
|
- bios_wr32(bios, NV_PBUS_PCI_NV_19, 0);
|
|
+ bios_wr32(bios, NV_PBUS_PCI_NV_19, pci_nv_19 & ~0xf00);
|
|
+
|
|
bios_wr32(bios, reg, value1);
|
|
|
|
udelay(10);
|
|
@@ -1945,7 +2163,7 @@ init_configure_mem(struct nvbios *bios, uint16_t offset,
|
|
uint32_t reg, data;
|
|
|
|
if (bios->major_version > 2)
|
|
- return 0;
|
|
+ return -ENODEV;
|
|
|
|
bios_idxprt_wr(bios, NV_VIO_SRX, NV_VIO_SR_CLOCK_INDEX, bios_idxprt_rd(
|
|
bios, NV_VIO_SRX, NV_VIO_SR_CLOCK_INDEX) | 0x20);
|
|
@@ -2000,7 +2218,7 @@ init_configure_clk(struct nvbios *bios, uint16_t offset,
|
|
int clock;
|
|
|
|
if (bios->major_version > 2)
|
|
- return 0;
|
|
+ return -ENODEV;
|
|
|
|
clock = ROM16(bios->data[meminitoffs + 4]) * 10;
|
|
setPLL(bios, NV_PRAMDAC_NVPLL_COEFF, clock);
|
|
@@ -2033,7 +2251,7 @@ init_configure_preinit(struct nvbios *bios, uint16_t offset,
|
|
uint8_t cr3c = ((straps << 2) & 0xf0) | (straps & (1 << 6));
|
|
|
|
if (bios->major_version > 2)
|
|
- return 0;
|
|
+ return -ENODEV;
|
|
|
|
bios_idxprt_wr(bios, NV_CIO_CRX__COLOR,
|
|
NV_CIO_CRE_SCRATCH4__INDEX, cr3c);
|
|
@@ -2572,48 +2790,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;
|
|
@@ -2669,7 +2876,7 @@ init_ram_restrict_zm_reg_group(struct nvbios *bios, uint16_t offset,
|
|
NV_ERROR(bios->dev,
|
|
"0x%04X: Zero block length - has the M table "
|
|
"been parsed?\n", offset);
|
|
- return 0;
|
|
+ return -EINVAL;
|
|
}
|
|
|
|
strap_ramcfg = (bios_rd32(bios, NV_PEXTDEV_BOOT_0) >> 2) & 0xf;
|
|
@@ -2853,14 +3060,14 @@ init_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
|
|
if (!bios->display.output) {
|
|
NV_ERROR(dev, "INIT_AUXCH: no active output\n");
|
|
- return 0;
|
|
+ return -EINVAL;
|
|
}
|
|
|
|
auxch = init_i2c_device_find(dev, bios->display.output->i2c_index);
|
|
if (!auxch) {
|
|
NV_ERROR(dev, "INIT_AUXCH: couldn't get auxch %d\n",
|
|
bios->display.output->i2c_index);
|
|
- return 0;
|
|
+ return -ENODEV;
|
|
}
|
|
|
|
if (!iexec->execute)
|
|
@@ -2873,7 +3080,7 @@ init_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
ret = nouveau_dp_auxch(auxch, 9, addr, &data, 1);
|
|
if (ret) {
|
|
NV_ERROR(dev, "INIT_AUXCH: rd auxch fail %d\n", ret);
|
|
- return 0;
|
|
+ return ret;
|
|
}
|
|
|
|
data &= bios->data[offset + 0];
|
|
@@ -2882,7 +3089,7 @@ init_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
ret = nouveau_dp_auxch(auxch, 8, addr, &data, 1);
|
|
if (ret) {
|
|
NV_ERROR(dev, "INIT_AUXCH: wr auxch fail %d\n", ret);
|
|
- return 0;
|
|
+ return ret;
|
|
}
|
|
}
|
|
|
|
@@ -2912,14 +3119,14 @@ init_zm_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
|
|
if (!bios->display.output) {
|
|
NV_ERROR(dev, "INIT_ZM_AUXCH: no active output\n");
|
|
- return 0;
|
|
+ return -EINVAL;
|
|
}
|
|
|
|
auxch = init_i2c_device_find(dev, bios->display.output->i2c_index);
|
|
if (!auxch) {
|
|
NV_ERROR(dev, "INIT_ZM_AUXCH: couldn't get auxch %d\n",
|
|
bios->display.output->i2c_index);
|
|
- return 0;
|
|
+ return -ENODEV;
|
|
}
|
|
|
|
if (!iexec->execute)
|
|
@@ -2930,7 +3137,7 @@ init_zm_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
|
ret = nouveau_dp_auxch(auxch, 8, addr, &bios->data[offset], 1);
|
|
if (ret) {
|
|
NV_ERROR(dev, "INIT_ZM_AUXCH: wr auxch fail %d\n", ret);
|
|
- return 0;
|
|
+ return ret;
|
|
}
|
|
}
|
|
|
|
@@ -2947,6 +3154,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 },
|
|
@@ -3014,7 +3224,7 @@ parse_init_table(struct nvbios *bios, unsigned int offset,
|
|
* is changed back to EXECUTE.
|
|
*/
|
|
|
|
- int count = 0, i, res;
|
|
+ int count = 0, i, ret;
|
|
uint8_t id;
|
|
|
|
/*
|
|
@@ -3029,26 +3239,33 @@ parse_init_table(struct nvbios *bios, unsigned int offset,
|
|
for (i = 0; itbl_entry[i].name && (itbl_entry[i].id != id); i++)
|
|
;
|
|
|
|
- if (itbl_entry[i].name) {
|
|
- BIOSLOG(bios, "0x%04X: [ (0x%02X) - %s ]\n",
|
|
- offset, itbl_entry[i].id, itbl_entry[i].name);
|
|
-
|
|
- /* execute eventual command handler */
|
|
- res = (*itbl_entry[i].handler)(bios, offset, iexec);
|
|
- if (!res)
|
|
- break;
|
|
- /*
|
|
- * Add the offset of the current command including all data
|
|
- * of that command. The offset will then be pointing on the
|
|
- * next op code.
|
|
- */
|
|
- offset += res;
|
|
- } else {
|
|
+ if (!itbl_entry[i].name) {
|
|
NV_ERROR(bios->dev,
|
|
"0x%04X: Init table command not found: "
|
|
"0x%02X\n", offset, id);
|
|
return -ENOENT;
|
|
}
|
|
+
|
|
+ BIOSLOG(bios, "0x%04X: [ (0x%02X) - %s ]\n", offset,
|
|
+ itbl_entry[i].id, itbl_entry[i].name);
|
|
+
|
|
+ /* execute eventual command handler */
|
|
+ ret = (*itbl_entry[i].handler)(bios, offset, iexec);
|
|
+ if (ret < 0) {
|
|
+ NV_ERROR(bios->dev, "0x%04X: Failed parsing init "
|
|
+ "table opcode: %s %d\n", offset,
|
|
+ itbl_entry[i].name, ret);
|
|
+ }
|
|
+
|
|
+ if (ret <= 0)
|
|
+ break;
|
|
+
|
|
+ /*
|
|
+ * Add the offset of the current command including all data
|
|
+ * of that command. The offset will then be pointing on the
|
|
+ * next op code.
|
|
+ */
|
|
+ offset += ret;
|
|
}
|
|
|
|
if (offset >= bios->length)
|
|
@@ -3123,7 +3340,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 +3357,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 +3411,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 +3432,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 +3480,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 +3614,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 +3647,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 +3686,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 +3710,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 +3781,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 +3856,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);
|
|
@@ -3679,20 +3901,37 @@ int nouveau_bios_parse_lvds_table(struct drm_device *dev, int pxclk, bool *dl, b
|
|
|
|
static uint8_t *
|
|
bios_output_config_match(struct drm_device *dev, struct dcb_entry *dcbent,
|
|
- uint16_t record, int record_len, int record_nr)
|
|
+ uint16_t record, int record_len, int record_nr,
|
|
+ bool match_link)
|
|
{
|
|
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;
|
|
|
|
+ switch (dcbent->type) {
|
|
+ case OUTPUT_TMDS:
|
|
+ case OUTPUT_LVDS:
|
|
+ case OUTPUT_DP:
|
|
+ break;
|
|
+ default:
|
|
+ match_link = false;
|
|
+ break;
|
|
+ }
|
|
+
|
|
for (i = 0; i < record_nr; i++, record += record_len) {
|
|
table = ROM16(bios->data[record]);
|
|
if (!table)
|
|
continue;
|
|
entry = ROM32(bios->data[table]);
|
|
|
|
+ if (match_link) {
|
|
+ v = (entry & 0x00c00000) >> 22;
|
|
+ if (!(v & dcbent->sorconf.link))
|
|
+ continue;
|
|
+ }
|
|
+
|
|
v = (entry & 0x000f0000) >> 16;
|
|
if (!(v & dcbent->or))
|
|
continue;
|
|
@@ -3716,7 +3955,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 +3964,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;
|
|
@@ -3734,7 +3973,7 @@ nouveau_bios_dp_table(struct drm_device *dev, struct dcb_entry *dcbent,
|
|
*length = table[4];
|
|
return bios_output_config_match(dev, dcbent,
|
|
bios->display.dp_table_ptr + table[1],
|
|
- table[2], table[3]);
|
|
+ table[2], table[3], table[0] >= 0x21);
|
|
}
|
|
|
|
int
|
|
@@ -3765,7 +4004,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;
|
|
@@ -3823,7 +4062,7 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent,
|
|
dcbent->type, dcbent->location, dcbent->or);
|
|
otable = bios_output_config_match(dev, dcbent, table[1] +
|
|
bios->display.script_table_ptr,
|
|
- table[2], table[3]);
|
|
+ table[2], table[3], table[0] >= 0x21);
|
|
if (!otable) {
|
|
NV_ERROR(dev, "Couldn't find matching output script table\n");
|
|
return 1;
|
|
@@ -3918,8 +4157,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 +4217,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 +4532,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 +4572,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 +4642,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 +4766,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 +5036,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 +5147,14 @@ static int parse_bmp_structure(struct drm_device *dev, struct nvbios *bios, unsi
|
|
bios->legacy.i2c_indices.crt = bios->data[legacy_i2c_offset];
|
|
bios->legacy.i2c_indices.tv = bios->data[legacy_i2c_offset + 1];
|
|
bios->legacy.i2c_indices.panel = bios->data[legacy_i2c_offset + 2];
|
|
- bios->bdcb.dcb.i2c[0].write = bios->data[legacy_i2c_offset + 4];
|
|
- bios->bdcb.dcb.i2c[0].read = bios->data[legacy_i2c_offset + 5];
|
|
- bios->bdcb.dcb.i2c[1].write = bios->data[legacy_i2c_offset + 6];
|
|
- bios->bdcb.dcb.i2c[1].read = bios->data[legacy_i2c_offset + 7];
|
|
+ if (bios->data[legacy_i2c_offset + 4])
|
|
+ bios->dcb.i2c[0].write = bios->data[legacy_i2c_offset + 4];
|
|
+ if (bios->data[legacy_i2c_offset + 5])
|
|
+ bios->dcb.i2c[0].read = bios->data[legacy_i2c_offset + 5];
|
|
+ if (bios->data[legacy_i2c_offset + 6])
|
|
+ bios->dcb.i2c[1].write = bios->data[legacy_i2c_offset + 6];
|
|
+ if (bios->data[legacy_i2c_offset + 7])
|
|
+ bios->dcb.i2c[1].read = bios->data[legacy_i2c_offset + 7];
|
|
|
|
if (bmplength > 74) {
|
|
bios->fmaxvco = ROM32(bmp[67]);
|
|
@@ -4961,82 +5205,10 @@ static uint16_t findstr(uint8_t *data, int n, const uint8_t *str, int len)
|
|
return 0;
|
|
}
|
|
|
|
-static int
|
|
-read_dcb_i2c_entry(struct drm_device *dev, int dcb_version, uint8_t *i2ctable, int index, struct dcb_i2c_entry *i2c)
|
|
-{
|
|
- uint8_t dcb_i2c_ver = dcb_version, headerlen = 0, entry_len = 4;
|
|
- int i2c_entries = DCB_MAX_NUM_I2C_ENTRIES;
|
|
- int recordoffset = 0, rdofs = 1, wrofs = 0;
|
|
- uint8_t port_type = 0;
|
|
-
|
|
- if (!i2ctable)
|
|
- return -EINVAL;
|
|
-
|
|
- if (dcb_version >= 0x30) {
|
|
- if (i2ctable[0] != dcb_version) /* necessary? */
|
|
- NV_WARN(dev,
|
|
- "DCB I2C table version mismatch (%02X vs %02X)\n",
|
|
- i2ctable[0], dcb_version);
|
|
- dcb_i2c_ver = i2ctable[0];
|
|
- headerlen = i2ctable[1];
|
|
- if (i2ctable[2] <= DCB_MAX_NUM_I2C_ENTRIES)
|
|
- i2c_entries = i2ctable[2];
|
|
- else
|
|
- NV_WARN(dev,
|
|
- "DCB I2C table has more entries than indexable "
|
|
- "(%d entries, max index 15)\n", i2ctable[2]);
|
|
- entry_len = i2ctable[3];
|
|
- /* [4] is i2c_default_indices, read in parse_dcb_table() */
|
|
- }
|
|
- /*
|
|
- * It's your own fault if you call this function on a DCB 1.1 BIOS --
|
|
- * the test below is for DCB 1.2
|
|
- */
|
|
- if (dcb_version < 0x14) {
|
|
- recordoffset = 2;
|
|
- rdofs = 0;
|
|
- wrofs = 1;
|
|
- }
|
|
-
|
|
- if (index == 0xf)
|
|
- return 0;
|
|
- if (index > i2c_entries) {
|
|
- NV_ERROR(dev, "DCB I2C index too big (%d > %d)\n",
|
|
- index, i2ctable[2]);
|
|
- return -ENOENT;
|
|
- }
|
|
- if (i2ctable[headerlen + entry_len * index + 3] == 0xff) {
|
|
- NV_ERROR(dev, "DCB I2C entry invalid\n");
|
|
- return -EINVAL;
|
|
- }
|
|
-
|
|
- if (dcb_i2c_ver >= 0x30) {
|
|
- port_type = i2ctable[headerlen + recordoffset + 3 + entry_len * index];
|
|
-
|
|
- /*
|
|
- * Fixup for chips using same address offset for read and
|
|
- * write.
|
|
- */
|
|
- if (port_type == 4) /* seen on C51 */
|
|
- rdofs = wrofs = 1;
|
|
- if (port_type >= 5) /* G80+ */
|
|
- rdofs = wrofs = 0;
|
|
- }
|
|
-
|
|
- if (dcb_i2c_ver >= 0x40 && port_type != 5 && port_type != 6)
|
|
- NV_WARN(dev, "DCB I2C table has port type %d\n", port_type);
|
|
-
|
|
- i2c->port_type = port_type;
|
|
- i2c->read = i2ctable[headerlen + recordoffset + rdofs + entry_len * index];
|
|
- i2c->write = i2ctable[headerlen + recordoffset + wrofs + entry_len * index];
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
static struct dcb_gpio_entry *
|
|
new_gpio_entry(struct nvbios *bios)
|
|
{
|
|
- struct parsed_dcb_gpio *gpio = &bios->bdcb.gpio;
|
|
+ struct dcb_gpio_table *gpio = &bios->dcb.gpio;
|
|
|
|
return &gpio->entry[gpio->entries++];
|
|
}
|
|
@@ -5045,14 +5217,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 +5247,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 +5280,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 +5288,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 +5296,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 +5330,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 +5423,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 +5452,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 +5498,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 +5509,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 +5536,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,23 +5547,17 @@ 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;
|
|
entry->or = (conn >> 24) & 0xf;
|
|
- /*
|
|
- * Normal entries consist of a single bit, but dual link has the
|
|
- * next most significant bit set too
|
|
- */
|
|
- entry->duallink_possible =
|
|
- ((1 << (ffs(entry->or) - 1)) * 3 == entry->or);
|
|
|
|
switch (entry->type) {
|
|
case OUTPUT_ANALOG:
|
|
@@ -5314,7 +5565,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 +5574,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 +5598,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 +5608,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 +5635,20 @@ 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;
|
|
+ }
|
|
+
|
|
+ if (dcb->version < 0x40) {
|
|
+ /* Normal entries consist of a single bit, but dual link has
|
|
+ * the next most significant bit set too
|
|
+ */
|
|
+ entry->duallink_possible =
|
|
+ ((1 << (ffs(entry->or) - 1)) * 3 == entry->or);
|
|
+ } else {
|
|
+ entry->duallink_possible = (entry->sorconf.link == 3);
|
|
}
|
|
|
|
/* unsure what DCB version introduces this, 3.0? */
|
|
@@ -5396,7 +5659,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 +5725,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 +5797,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 +5805,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 +5826,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 +5852,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 +5900,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 +5929,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 +5937,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 +5960,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 +5972,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 +6089,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 +6122,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 +6135,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 +6151,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 +6178,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 +6209,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,26 +6225,48 @@ 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);
|
|
}
|
|
|
|
+static bool
|
|
+nouveau_bios_posted(struct drm_device *dev)
|
|
+{
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ bool was_locked;
|
|
+ unsigned htotal;
|
|
+
|
|
+ if (dev_priv->chipset >= NV_50) {
|
|
+ if (NVReadVgaCrtc(dev, 0, 0x00) == 0 &&
|
|
+ NVReadVgaCrtc(dev, 0, 0x1a) == 0)
|
|
+ return false;
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ was_locked = NVLockVgaCrtcs(dev, false);
|
|
+ htotal = NVReadVgaCrtc(dev, 0, 0x06);
|
|
+ htotal |= (NVReadVgaCrtc(dev, 0, 0x07) & 0x01) << 8;
|
|
+ htotal |= (NVReadVgaCrtc(dev, 0, 0x07) & 0x20) << 4;
|
|
+ htotal |= (NVReadVgaCrtc(dev, 0, 0x25) & 0x01) << 10;
|
|
+ htotal |= (NVReadVgaCrtc(dev, 0, 0x41) & 0x01) << 11;
|
|
+ NVLockVgaCrtcs(dev, was_locked);
|
|
+ return (htotal != 0);
|
|
+}
|
|
+
|
|
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;
|
|
|
|
@@ -6007,11 +6292,9 @@ nouveau_bios_init(struct drm_device *dev)
|
|
bios->execute = false;
|
|
|
|
/* ... unless card isn't POSTed already */
|
|
- if (dev_priv->card_type >= NV_10 &&
|
|
- NVReadVgaCrtc(dev, 0, 0x00) == 0 &&
|
|
- NVReadVgaCrtc(dev, 0, 0x1a) == 0) {
|
|
+ if (!nouveau_bios_posted(dev)) {
|
|
NV_INFO(dev, "Adaptor not initialised\n");
|
|
- if (dev_priv->card_type < NV_50) {
|
|
+ if (dev_priv->card_type < NV_40) {
|
|
NV_ERROR(dev, "Unable to POST this chipset\n");
|
|
return -ENODEV;
|
|
}
|
|
@@ -6023,10 +6306,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..bd33a54 100644
|
|
--- a/drivers/gpu/drm/nouveau/nouveau_bios.h
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.h
|
|
@@ -34,9 +34,73 @@
|
|
|
|
#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;
|
|
+ void *drm;
|
|
+};
|
|
+
|
|
+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 +135,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 +207,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 +216,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 +246,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 +272,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..f9b2acf 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);
|
|
@@ -258,9 +257,7 @@ nouveau_channel_free(struct nouveau_channel *chan)
|
|
nouveau_debugfs_channel_fini(chan);
|
|
|
|
/* Give outstanding push buffers a chance to complete */
|
|
- spin_lock_irqsave(&chan->fence.lock, flags);
|
|
nouveau_fence_update(chan);
|
|
- spin_unlock_irqrestore(&chan->fence.lock, flags);
|
|
if (chan->fence.sequence != chan->fence.sequence_ack) {
|
|
struct nouveau_fence *fence = NULL;
|
|
|
|
@@ -280,9 +277,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 +304,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 +318,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 a378bc3..fb51958 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,19 +236,6 @@ nouveau_connector_detect(struct drm_connector *connector)
|
|
struct nouveau_i2c_chan *i2c;
|
|
int type, flags;
|
|
|
|
- if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
|
|
- nv_encoder = find_encoder_by_type(connector, OUTPUT_LVDS);
|
|
- if (nv_encoder && nv_connector->native_mode) {
|
|
- unsigned status = connector_status_connected;
|
|
-
|
|
-#ifdef CONFIG_ACPI
|
|
- if (!nouveau_ignorelid && !acpi_lid_open())
|
|
- status = connector_status_unknown;
|
|
-#endif
|
|
- nouveau_connector_set_encoder(connector, nv_encoder);
|
|
- return status;
|
|
- }
|
|
-
|
|
/* Cleanup the previous EDID block. */
|
|
if (nv_connector->edid) {
|
|
drm_mode_connector_update_edid_property(connector, NULL);
|
|
@@ -281,7 +268,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
|
|
@@ -302,7 +289,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);
|
|
@@ -320,14 +307,75 @@ detect_analog:
|
|
return connector_status_disconnected;
|
|
}
|
|
|
|
+static enum drm_connector_status
|
|
+nouveau_connector_detect_lvds(struct drm_connector *connector)
|
|
+{
|
|
+ struct drm_device *dev = connector->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
+ struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
|
+ struct nouveau_encoder *nv_encoder = NULL;
|
|
+ enum drm_connector_status status = connector_status_disconnected;
|
|
+
|
|
+ /* Cleanup the previous EDID block. */
|
|
+ if (nv_connector->edid) {
|
|
+ drm_mode_connector_update_edid_property(connector, NULL);
|
|
+ kfree(nv_connector->edid);
|
|
+ nv_connector->edid = NULL;
|
|
+ }
|
|
+
|
|
+ nv_encoder = find_encoder_by_type(connector, OUTPUT_LVDS);
|
|
+ if (!nv_encoder)
|
|
+ return connector_status_disconnected;
|
|
+
|
|
+ if (!dev_priv->vbios.fp_no_ddc) {
|
|
+ status = nouveau_connector_detect(connector);
|
|
+ if (status == connector_status_connected)
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ /* If no EDID found above, and the VBIOS indicates a hardcoded
|
|
+ * modeline is avalilable for the panel, set it as the panel's
|
|
+ * native mode and exit.
|
|
+ */
|
|
+ if (nouveau_bios_fp_mode(dev, NULL) && (dev_priv->vbios.fp_no_ddc ||
|
|
+ nv_encoder->dcb->lvdsconf.use_straps_for_mode)) {
|
|
+ status = connector_status_connected;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ /* Still nothing, some VBIOS images have a hardcoded EDID block
|
|
+ * stored for the panel stored in them.
|
|
+ */
|
|
+ if (!dev_priv->vbios.fp_no_ddc) {
|
|
+ struct edid *edid =
|
|
+ (struct edid *)nouveau_bios_embedded_edid(dev);
|
|
+ if (edid) {
|
|
+ nv_connector->edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
|
|
+ *(nv_connector->edid) = *edid;
|
|
+ status = connector_status_connected;
|
|
+ }
|
|
+ }
|
|
+
|
|
+out:
|
|
+#ifdef CONFIG_ACPI
|
|
+ if (status == connector_status_connected &&
|
|
+ !nouveau_ignorelid && !acpi_lid_open())
|
|
+ status = connector_status_unknown;
|
|
+#endif
|
|
+
|
|
+ drm_mode_connector_update_edid_property(connector, nv_connector->edid);
|
|
+ nouveau_connector_set_encoder(connector, nv_encoder);
|
|
+ return status;
|
|
+}
|
|
+
|
|
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
|
|
@@ -337,7 +385,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;
|
|
@@ -371,7 +419,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;
|
|
|
|
@@ -431,24 +479,27 @@ nouveau_connector_set_property(struct drm_connector *connector,
|
|
}
|
|
|
|
static struct drm_display_mode *
|
|
-nouveau_connector_native_mode(struct nouveau_connector *connector)
|
|
+nouveau_connector_native_mode(struct drm_connector *connector)
|
|
{
|
|
- struct drm_device *dev = connector->base.dev;
|
|
+ struct drm_connector_helper_funcs *helper = connector->helper_private;
|
|
+ struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
|
+ struct drm_device *dev = connector->dev;
|
|
struct drm_display_mode *mode, *largest = NULL;
|
|
int high_w = 0, high_h = 0, high_v = 0;
|
|
|
|
- /* Use preferred mode if there is one.. */
|
|
- list_for_each_entry(mode, &connector->base.probed_modes, head) {
|
|
+ list_for_each_entry(mode, &nv_connector->base.probed_modes, head) {
|
|
+ if (helper->mode_valid(connector, mode) != MODE_OK)
|
|
+ continue;
|
|
+
|
|
+ /* Use preferred mode if there is one.. */
|
|
if (mode->type & DRM_MODE_TYPE_PREFERRED) {
|
|
NV_DEBUG_KMS(dev, "native mode from preferred\n");
|
|
return drm_mode_duplicate(dev, mode);
|
|
}
|
|
- }
|
|
|
|
- /* Otherwise, take the resolution with the largest width, then height,
|
|
- * then vertical refresh
|
|
- */
|
|
- list_for_each_entry(mode, &connector->base.probed_modes, head) {
|
|
+ /* Otherwise, take the resolution with the largest width, then
|
|
+ * height, then vertical refresh
|
|
+ */
|
|
if (mode->hdisplay < high_w)
|
|
continue;
|
|
|
|
@@ -530,21 +581,28 @@ static int
|
|
nouveau_connector_get_modes(struct drm_connector *connector)
|
|
{
|
|
struct drm_device *dev = connector->dev;
|
|
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
|
struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
|
|
+ struct drm_display_mode mode;
|
|
int ret = 0;
|
|
|
|
- /* If we're not LVDS, destroy the previous native mode, the attached
|
|
- * monitor could have changed.
|
|
+ /* destroy the native mode, the attached monitor could have changed.
|
|
*/
|
|
- if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS &&
|
|
- nv_connector->native_mode) {
|
|
+ if (nv_connector->native_mode) {
|
|
drm_mode_destroy(dev, nv_connector->native_mode);
|
|
nv_connector->native_mode = NULL;
|
|
}
|
|
|
|
if (nv_connector->edid)
|
|
ret = drm_add_edid_modes(connector, nv_connector->edid);
|
|
+ else
|
|
+ if (nv_encoder->dcb->type == OUTPUT_LVDS &&
|
|
+ (nv_encoder->dcb->lvdsconf.use_straps_for_mode ||
|
|
+ dev_priv->vbios.fp_no_ddc) &&
|
|
+ nouveau_bios_fp_mode(dev, &mode)) {
|
|
+ nv_connector->native_mode = drm_mode_duplicate(dev, &mode);
|
|
+ }
|
|
|
|
/* Find the native mode if this is a digital panel, if we didn't
|
|
* find any modes through DDC previously add the native mode to
|
|
@@ -552,7 +610,7 @@ nouveau_connector_get_modes(struct drm_connector *connector)
|
|
*/
|
|
if (!nv_connector->native_mode)
|
|
nv_connector->native_mode =
|
|
- nouveau_connector_native_mode(nv_connector);
|
|
+ nouveau_connector_native_mode(connector);
|
|
if (ret == 0 && nv_connector->native_mode) {
|
|
struct drm_display_mode *mode;
|
|
|
|
@@ -565,7 +623,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;
|
|
@@ -583,9 +641,9 @@ nouveau_connector_mode_valid(struct drm_connector *connector,
|
|
|
|
switch (nv_encoder->dcb->type) {
|
|
case OUTPUT_LVDS:
|
|
- BUG_ON(!nv_connector->native_mode);
|
|
- if (mode->hdisplay > nv_connector->native_mode->hdisplay ||
|
|
- mode->vdisplay > nv_connector->native_mode->vdisplay)
|
|
+ if (nv_connector->native_mode &&
|
|
+ (mode->hdisplay > nv_connector->native_mode->hdisplay ||
|
|
+ mode->vdisplay > nv_connector->native_mode->vdisplay))
|
|
return MODE_PANEL;
|
|
|
|
min_clock = 0;
|
|
@@ -593,8 +651,7 @@ nouveau_connector_mode_valid(struct drm_connector *connector,
|
|
break;
|
|
case OUTPUT_TMDS:
|
|
if ((dev_priv->card_type >= NV_50 && !nouveau_duallink) ||
|
|
- (dev_priv->card_type < NV_50 &&
|
|
- !nv_encoder->dcb->duallink_possible))
|
|
+ !nv_encoder->dcb->duallink_possible)
|
|
max_clock = 165000;
|
|
else
|
|
max_clock = 330000;
|
|
@@ -615,6 +672,9 @@ nouveau_connector_mode_valid(struct drm_connector *connector,
|
|
|
|
clock *= 3;
|
|
break;
|
|
+ default:
|
|
+ BUG_ON(1);
|
|
+ return MODE_BAD;
|
|
}
|
|
|
|
if (clock < min_clock)
|
|
@@ -656,193 +716,138 @@ nouveau_connector_funcs = {
|
|
.force = nouveau_connector_force
|
|
};
|
|
|
|
-static int
|
|
-nouveau_connector_create_lvds(struct drm_device *dev,
|
|
- struct drm_connector *connector)
|
|
-{
|
|
- struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
|
- struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
- struct nouveau_i2c_chan *i2c = NULL;
|
|
- struct nouveau_encoder *nv_encoder;
|
|
- struct drm_display_mode native, *mode, *temp;
|
|
- bool dummy, if_is_24bit = false;
|
|
- int ret, flags;
|
|
-
|
|
- nv_encoder = find_encoder_by_type(connector, OUTPUT_LVDS);
|
|
- if (!nv_encoder)
|
|
- return -ENODEV;
|
|
-
|
|
- ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &if_is_24bit);
|
|
- if (ret) {
|
|
- NV_ERROR(dev, "Error parsing LVDS table, disabling LVDS\n");
|
|
- return ret;
|
|
- }
|
|
- nv_connector->use_dithering = !if_is_24bit;
|
|
-
|
|
- /* Firstly try getting EDID over DDC, if allowed and I2C channel
|
|
- * is available.
|
|
- */
|
|
- if (!dev_priv->VBIOS.pub.fp_no_ddc && nv_encoder->dcb->i2c_index < 0xf)
|
|
- i2c = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index);
|
|
-
|
|
- if (i2c) {
|
|
- nouveau_connector_ddc_prepare(connector, &flags);
|
|
- nv_connector->edid = drm_get_edid(connector, &i2c->adapter);
|
|
- nouveau_connector_ddc_finish(connector, flags);
|
|
- }
|
|
-
|
|
- /* If no EDID found above, and the VBIOS indicates a hardcoded
|
|
- * modeline is avalilable for the panel, set it as the panel's
|
|
- * native mode and exit.
|
|
- */
|
|
- if (!nv_connector->edid && nouveau_bios_fp_mode(dev, &native) &&
|
|
- (nv_encoder->dcb->lvdsconf.use_straps_for_mode ||
|
|
- dev_priv->VBIOS.pub.fp_no_ddc)) {
|
|
- nv_connector->native_mode = drm_mode_duplicate(dev, &native);
|
|
- goto out;
|
|
- }
|
|
-
|
|
- /* Still nothing, some VBIOS images have a hardcoded EDID block
|
|
- * stored for the panel stored in them.
|
|
- */
|
|
- if (!nv_connector->edid && !nv_connector->native_mode &&
|
|
- !dev_priv->VBIOS.pub.fp_no_ddc) {
|
|
- struct edid *edid =
|
|
- (struct edid *)nouveau_bios_embedded_edid(dev);
|
|
- if (edid) {
|
|
- nv_connector->edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
|
|
- *(nv_connector->edid) = *edid;
|
|
- }
|
|
- }
|
|
-
|
|
- if (!nv_connector->edid)
|
|
- goto out;
|
|
-
|
|
- /* We didn't find/use a panel mode from the VBIOS, so parse the EDID
|
|
- * block and look for the preferred mode there.
|
|
- */
|
|
- ret = drm_add_edid_modes(connector, nv_connector->edid);
|
|
- if (ret == 0)
|
|
- goto out;
|
|
- nv_connector->detected_encoder = nv_encoder;
|
|
- nv_connector->native_mode = nouveau_connector_native_mode(nv_connector);
|
|
- list_for_each_entry_safe(mode, temp, &connector->probed_modes, head)
|
|
- drm_mode_remove(connector, mode);
|
|
-
|
|
-out:
|
|
- if (!nv_connector->native_mode) {
|
|
- NV_ERROR(dev, "LVDS present in DCB table, but couldn't "
|
|
- "determine its native mode. Disabling.\n");
|
|
- return -ENODEV;
|
|
- }
|
|
-
|
|
- drm_mode_connector_update_edid_property(connector, nv_connector->edid);
|
|
- return 0;
|
|
-}
|
|
+static const struct drm_connector_funcs
|
|
+nouveau_connector_funcs_lvds = {
|
|
+ .dpms = drm_helper_connector_dpms,
|
|
+ .save = NULL,
|
|
+ .restore = NULL,
|
|
+ .detect = nouveau_connector_detect_lvds,
|
|
+ .destroy = nouveau_connector_destroy,
|
|
+ .fill_modes = drm_helper_probe_single_connector_modes,
|
|
+ .set_property = nouveau_connector_set_property,
|
|
+ .force = nouveau_connector_force
|
|
+};
|
|
|
|
-int
|
|
-nouveau_connector_create(struct drm_device *dev, int index, int type)
|
|
+struct drm_connector *
|
|
+nouveau_connector_create(struct drm_device *dev, int index)
|
|
{
|
|
+ const struct drm_connector_funcs *funcs = &nouveau_connector_funcs;
|
|
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
struct nouveau_connector *nv_connector = NULL;
|
|
+ struct dcb_connector_table_entry *dcb = NULL;
|
|
struct drm_connector *connector;
|
|
- struct drm_encoder *encoder;
|
|
- int ret;
|
|
+ int type, ret = 0;
|
|
|
|
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;
|
|
+ if (index >= dev_priv->vbios.dcb.connector.entries)
|
|
+ return ERR_PTR(-EINVAL);
|
|
|
|
- switch (type) {
|
|
- case DRM_MODE_CONNECTOR_VGA:
|
|
- NV_INFO(dev, "Detected a VGA connector\n");
|
|
+ dcb = &dev_priv->vbios.dcb.connector.entry[index];
|
|
+ if (dcb->drm)
|
|
+ return dcb->drm;
|
|
+
|
|
+ switch (dcb->type) {
|
|
+ case DCB_CONNECTOR_VGA:
|
|
+ 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:
|
|
+ type = DRM_MODE_CONNECTOR_TV;
|
|
break;
|
|
- case DRM_MODE_CONNECTOR_DVII:
|
|
- NV_INFO(dev, "Detected a DVI-I connector\n");
|
|
+ case DCB_CONNECTOR_DVI_I:
|
|
+ type = DRM_MODE_CONNECTOR_DVII;
|
|
break;
|
|
- case DRM_MODE_CONNECTOR_LVDS:
|
|
- NV_INFO(dev, "Detected a LVDS connector\n");
|
|
+ case DCB_CONNECTOR_DVI_D:
|
|
+ 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:
|
|
+ type = DRM_MODE_CONNECTOR_HDMIA;
|
|
break;
|
|
- case DRM_MODE_CONNECTOR_DisplayPort:
|
|
- NV_INFO(dev, "Detected a DisplayPort connector\n");
|
|
+ case DCB_CONNECTOR_LVDS:
|
|
+ type = DRM_MODE_CONNECTOR_LVDS;
|
|
+ funcs = &nouveau_connector_funcs_lvds;
|
|
break;
|
|
- default:
|
|
- NV_ERROR(dev, "Unknown connector, this is not good.\n");
|
|
+ case DCB_CONNECTOR_DP:
|
|
+ type = DRM_MODE_CONNECTOR_DisplayPort;
|
|
+ break;
|
|
+ case DCB_CONNECTOR_eDP:
|
|
+ type = DRM_MODE_CONNECTOR_eDP;
|
|
break;
|
|
+ default:
|
|
+ NV_ERROR(dev, "unknown connector type: 0x%02x!!\n", dcb->type);
|
|
+ return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
+ nv_connector = kzalloc(sizeof(*nv_connector), GFP_KERNEL);
|
|
+ if (!nv_connector)
|
|
+ return ERR_PTR(-ENOMEM);
|
|
+ nv_connector->dcb = dcb;
|
|
+ connector = &nv_connector->base;
|
|
+
|
|
/* defaults, will get overridden in detect() */
|
|
connector->interlace_allowed = false;
|
|
connector->doublescan_allowed = false;
|
|
|
|
- drm_connector_init(dev, connector, &nouveau_connector_funcs, type);
|
|
+ drm_connector_init(dev, connector, funcs, type);
|
|
drm_connector_helper_add(connector, &nouveau_connector_helper_funcs);
|
|
|
|
+ /* Check if we need dithering enabled */
|
|
+ if (dcb->type == DCB_CONNECTOR_LVDS) {
|
|
+ bool dummy, is_24bit = false;
|
|
+
|
|
+ ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &is_24bit);
|
|
+ if (ret) {
|
|
+ NV_ERROR(dev, "Error parsing LVDS table, disabling "
|
|
+ "LVDS\n");
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ nv_connector->use_dithering = !is_24bit;
|
|
+ }
|
|
+
|
|
/* 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)
|
|
- 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);
|
|
+ dcb->drm = connector;
|
|
+ return dcb->drm;
|
|
|
|
- if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
|
|
- ret = nouveau_connector_create_lvds(dev, connector);
|
|
- if (ret) {
|
|
- connector->funcs->destroy(connector);
|
|
- return ret;
|
|
- }
|
|
- }
|
|
+fail:
|
|
+ drm_connector_cleanup(connector);
|
|
+ kfree(connector);
|
|
+ return ERR_PTR(ret);
|
|
|
|
- return 0;
|
|
}
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h
|
|
index 728b809..1ce3d91 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);
|
|
+struct drm_connector *
|
|
+nouveau_connector_create(struct drm_device *, int index);
|
|
|
|
#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..c31159a 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;
|
|
|
|
@@ -195,7 +196,7 @@ struct nouveau_channel {
|
|
struct list_head pending;
|
|
uint32_t sequence;
|
|
uint32_t sequence_ack;
|
|
- uint32_t last_sequence_irq;
|
|
+ atomic_t last_sequence_irq;
|
|
} fence;
|
|
|
|
/* DMA push buffer */
|
|
@@ -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 *);
|
|
@@ -1081,13 +1083,13 @@ extern long nouveau_compat_ioctl(struct file *file, unsigned int cmd,
|
|
unsigned long arg);
|
|
|
|
/* nv04_dac.c */
|
|
-extern int nv04_dac_create(struct drm_device *dev, struct dcb_entry *entry);
|
|
+extern int nv04_dac_create(struct drm_connector *, struct dcb_entry *);
|
|
extern uint32_t nv17_dac_sample_load(struct drm_encoder *encoder);
|
|
extern int nv04_dac_output_offset(struct drm_encoder *encoder);
|
|
extern void nv04_dac_update_dacclk(struct drm_encoder *encoder, bool enable);
|
|
|
|
/* nv04_dfp.c */
|
|
-extern int nv04_dfp_create(struct drm_device *dev, struct dcb_entry *entry);
|
|
+extern int nv04_dfp_create(struct drm_connector *, struct dcb_entry *);
|
|
extern int nv04_dfp_get_bound_head(struct drm_device *dev, struct dcb_entry *dcbent);
|
|
extern void nv04_dfp_bind_head(struct drm_device *dev, struct dcb_entry *dcbent,
|
|
int head, bool dl);
|
|
@@ -1096,10 +1098,10 @@ extern void nv04_dfp_update_fp_control(struct drm_encoder *encoder, int mode);
|
|
|
|
/* nv04_tv.c */
|
|
extern int nv04_tv_identify(struct drm_device *dev, int i2c_index);
|
|
-extern int nv04_tv_create(struct drm_device *dev, struct dcb_entry *entry);
|
|
+extern int nv04_tv_create(struct drm_connector *, struct dcb_entry *);
|
|
|
|
/* nv17_tv.c */
|
|
-extern int nv17_tv_create(struct drm_device *dev, struct dcb_entry *entry);
|
|
+extern int nv17_tv_create(struct drm_connector *, struct dcb_entry *);
|
|
|
|
/* nv04_display.c */
|
|
extern int nv04_display_create(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);
|
|
@@ -1139,7 +1142,6 @@ extern int nouveau_fence_wait(void *obj, void *arg, bool lazy, bool intr);
|
|
extern int nouveau_fence_flush(void *obj, void *arg);
|
|
extern void nouveau_fence_unref(void **obj);
|
|
extern void *nouveau_fence_ref(void *obj);
|
|
-extern void nouveau_fence_handler(struct drm_device *dev, int channel);
|
|
|
|
/* nouveau_gem.c */
|
|
extern int nouveau_gem_new(struct drm_device *, struct nouveau_channel *,
|
|
@@ -1163,6 +1165,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..e4442e2 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;
|
|
@@ -68,8 +71,8 @@ static inline struct drm_encoder *to_drm_encoder(struct nouveau_encoder *enc)
|
|
|
|
struct nouveau_connector *
|
|
nouveau_encoder_connector_get(struct nouveau_encoder *encoder);
|
|
-int nv50_sor_create(struct drm_device *dev, struct dcb_entry *entry);
|
|
-int nv50_dac_create(struct drm_device *dev, struct dcb_entry *entry);
|
|
+int nv50_sor_create(struct drm_connector *, struct dcb_entry *);
|
|
+int nv50_dac_create(struct drm_connector *, struct dcb_entry *);
|
|
|
|
struct bit_displayport_encoder_table {
|
|
uint32_t match;
|
|
diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c
|
|
index faddf53..813d853 100644
|
|
--- a/drivers/gpu/drm/nouveau/nouveau_fence.c
|
|
+++ b/drivers/gpu/drm/nouveau/nouveau_fence.c
|
|
@@ -67,12 +67,13 @@ nouveau_fence_update(struct nouveau_channel *chan)
|
|
if (USE_REFCNT)
|
|
sequence = nvchan_rd32(chan, 0x48);
|
|
else
|
|
- sequence = chan->fence.last_sequence_irq;
|
|
+ sequence = atomic_read(&chan->fence.last_sequence_irq);
|
|
|
|
if (chan->fence.sequence_ack == sequence)
|
|
return;
|
|
chan->fence.sequence_ack = sequence;
|
|
|
|
+ spin_lock(&chan->fence.lock);
|
|
list_for_each_safe(entry, tmp, &chan->fence.pending) {
|
|
fence = list_entry(entry, struct nouveau_fence, entry);
|
|
|
|
@@ -84,6 +85,7 @@ nouveau_fence_update(struct nouveau_channel *chan)
|
|
if (sequence == chan->fence.sequence_ack)
|
|
break;
|
|
}
|
|
+ spin_unlock(&chan->fence.lock);
|
|
}
|
|
|
|
int
|
|
@@ -119,7 +121,6 @@ nouveau_fence_emit(struct nouveau_fence *fence)
|
|
{
|
|
struct drm_nouveau_private *dev_priv = fence->channel->dev->dev_private;
|
|
struct nouveau_channel *chan = fence->channel;
|
|
- unsigned long flags;
|
|
int ret;
|
|
|
|
ret = RING_SPACE(chan, 2);
|
|
@@ -127,9 +128,7 @@ nouveau_fence_emit(struct nouveau_fence *fence)
|
|
return ret;
|
|
|
|
if (unlikely(chan->fence.sequence == chan->fence.sequence_ack - 1)) {
|
|
- spin_lock_irqsave(&chan->fence.lock, flags);
|
|
nouveau_fence_update(chan);
|
|
- spin_unlock_irqrestore(&chan->fence.lock, flags);
|
|
|
|
BUG_ON(chan->fence.sequence ==
|
|
chan->fence.sequence_ack - 1);
|
|
@@ -138,9 +137,9 @@ nouveau_fence_emit(struct nouveau_fence *fence)
|
|
fence->sequence = ++chan->fence.sequence;
|
|
|
|
kref_get(&fence->refcount);
|
|
- spin_lock_irqsave(&chan->fence.lock, flags);
|
|
+ spin_lock(&chan->fence.lock);
|
|
list_add_tail(&fence->entry, &chan->fence.pending);
|
|
- spin_unlock_irqrestore(&chan->fence.lock, flags);
|
|
+ spin_unlock(&chan->fence.lock);
|
|
|
|
BEGIN_RING(chan, NvSubSw, USE_REFCNT ? 0x0050 : 0x0150, 1);
|
|
OUT_RING(chan, fence->sequence);
|
|
@@ -173,14 +172,11 @@ nouveau_fence_signalled(void *sync_obj, void *sync_arg)
|
|
{
|
|
struct nouveau_fence *fence = nouveau_fence(sync_obj);
|
|
struct nouveau_channel *chan = fence->channel;
|
|
- unsigned long flags;
|
|
|
|
if (fence->signalled)
|
|
return true;
|
|
|
|
- spin_lock_irqsave(&chan->fence.lock, flags);
|
|
nouveau_fence_update(chan);
|
|
- spin_unlock_irqrestore(&chan->fence.lock, flags);
|
|
return fence->signalled;
|
|
}
|
|
|
|
@@ -221,27 +217,12 @@ nouveau_fence_flush(void *sync_obj, void *sync_arg)
|
|
return 0;
|
|
}
|
|
|
|
-void
|
|
-nouveau_fence_handler(struct drm_device *dev, int channel)
|
|
-{
|
|
- struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
- struct nouveau_channel *chan = NULL;
|
|
-
|
|
- if (channel >= 0 && channel < dev_priv->engine.fifo.channels)
|
|
- chan = dev_priv->fifos[channel];
|
|
-
|
|
- if (chan) {
|
|
- spin_lock_irq(&chan->fence.lock);
|
|
- nouveau_fence_update(chan);
|
|
- spin_unlock_irq(&chan->fence.lock);
|
|
- }
|
|
-}
|
|
-
|
|
int
|
|
nouveau_fence_init(struct nouveau_channel *chan)
|
|
{
|
|
INIT_LIST_HEAD(&chan->fence.pending);
|
|
spin_lock_init(&chan->fence.lock);
|
|
+ atomic_set(&chan->fence.last_sequence_irq, 0);
|
|
return 0;
|
|
}
|
|
|
|
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..816948b 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,39 @@ 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;
|
|
+
|
|
+ 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
|
|
+ if (dev_priv->card_type < NV_50) {
|
|
+ dev_priv->vram_size = nv_rd32(dev, NV04_FIFO_DATA);
|
|
+ dev_priv->vram_size &= NV10_FIFO_DATA_RAM_AMOUNT_MB_MASK;
|
|
+ } else {
|
|
+ dev_priv->vram_size = nv_rd32(dev, NV04_FIFO_DATA);
|
|
+ dev_priv->vram_size |= (dev_priv->vram_size & 0xff) << 32;
|
|
+ dev_priv->vram_size &= 0xffffffff00;
|
|
+ if (dev_priv->chipset == 0xaa || dev_priv->chipset == 0xac) {
|
|
+ dev_priv->vram_sys_base = nv_rd32(dev, 0x100e10);
|
|
+ dev_priv->vram_sys_base <<= 12;
|
|
}
|
|
- break;
|
|
}
|
|
|
|
- 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 +669,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..7c1d252 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);
|
|
@@ -691,29 +701,24 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
|
|
NV_INFO(dev, "Detected an NV%2x generation card (0x%08x)\n",
|
|
dev_priv->card_type, reg0);
|
|
|
|
- /* map larger RAMIN aperture on NV40 cards */
|
|
- dev_priv->ramin = NULL;
|
|
+ /* Map PRAMIN BAR, or on older cards, the aperture withing BAR0 */
|
|
if (dev_priv->card_type >= NV_40) {
|
|
int ramin_bar = 2;
|
|
if (pci_resource_len(dev->pdev, ramin_bar) == 0)
|
|
ramin_bar = 3;
|
|
|
|
dev_priv->ramin_size = pci_resource_len(dev->pdev, ramin_bar);
|
|
- dev_priv->ramin = ioremap(
|
|
- pci_resource_start(dev->pdev, ramin_bar),
|
|
+ dev_priv->ramin =
|
|
+ ioremap(pci_resource_start(dev->pdev, ramin_bar),
|
|
dev_priv->ramin_size);
|
|
if (!dev_priv->ramin) {
|
|
- NV_ERROR(dev, "Failed to init RAMIN mapping, "
|
|
- "limited instance memory available\n");
|
|
+ NV_ERROR(dev, "Failed to PRAMIN BAR");
|
|
+ return -ENOMEM;
|
|
}
|
|
- }
|
|
-
|
|
- /* On older cards (or if the above failed), create a map covering
|
|
- * the BAR0 PRAMIN aperture */
|
|
- if (!dev_priv->ramin) {
|
|
+ } else {
|
|
dev_priv->ramin_size = 1 * 1024 * 1024;
|
|
dev_priv->ramin = ioremap(mmio_start_offs + NV_RAMIN,
|
|
- dev_priv->ramin_size);
|
|
+ dev_priv->ramin_size);
|
|
if (!dev_priv->ramin) {
|
|
NV_ERROR(dev, "Failed to map BAR0 PRAMIN.\n");
|
|
return -ENOMEM;
|
|
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..8066c56 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);
|
|
@@ -501,11 +501,13 @@ static const struct drm_encoder_funcs nv04_dac_funcs = {
|
|
.destroy = nv04_dac_destroy,
|
|
};
|
|
|
|
-int nv04_dac_create(struct drm_device *dev, struct dcb_entry *entry)
|
|
+int
|
|
+nv04_dac_create(struct drm_connector *connector, struct dcb_entry *entry)
|
|
{
|
|
const struct drm_encoder_helper_funcs *helper;
|
|
- struct drm_encoder *encoder;
|
|
struct nouveau_encoder *nv_encoder = NULL;
|
|
+ struct drm_device *dev = connector->dev;
|
|
+ struct drm_encoder *encoder;
|
|
|
|
nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
|
|
if (!nv_encoder)
|
|
@@ -527,5 +529,6 @@ int nv04_dac_create(struct drm_device *dev, struct dcb_entry *entry)
|
|
encoder->possible_crtcs = entry->heads;
|
|
encoder->possible_clones = 0;
|
|
|
|
+ drm_mode_connector_attach_encoder(connector, encoder);
|
|
return 0;
|
|
}
|
|
diff --git a/drivers/gpu/drm/nouveau/nv04_dfp.c b/drivers/gpu/drm/nouveau/nv04_dfp.c
|
|
index 483f875..3559d89 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;
|
|
@@ -584,11 +584,12 @@ static const struct drm_encoder_funcs nv04_dfp_funcs = {
|
|
.destroy = nv04_dfp_destroy,
|
|
};
|
|
|
|
-int nv04_dfp_create(struct drm_device *dev, struct dcb_entry *entry)
|
|
+int
|
|
+nv04_dfp_create(struct drm_connector *connector, struct dcb_entry *entry)
|
|
{
|
|
const struct drm_encoder_helper_funcs *helper;
|
|
- struct drm_encoder *encoder;
|
|
struct nouveau_encoder *nv_encoder = NULL;
|
|
+ struct drm_encoder *encoder;
|
|
int type;
|
|
|
|
switch (entry->type) {
|
|
@@ -613,11 +614,12 @@ int nv04_dfp_create(struct drm_device *dev, struct dcb_entry *entry)
|
|
nv_encoder->dcb = entry;
|
|
nv_encoder->or = ffs(entry->or) - 1;
|
|
|
|
- drm_encoder_init(dev, encoder, &nv04_dfp_funcs, type);
|
|
+ drm_encoder_init(connector->dev, encoder, &nv04_dfp_funcs, type);
|
|
drm_encoder_helper_add(encoder, helper);
|
|
|
|
encoder->possible_crtcs = entry->heads;
|
|
encoder->possible_clones = 0;
|
|
|
|
+ drm_mode_connector_attach_encoder(connector, encoder);
|
|
return 0;
|
|
}
|
|
diff --git a/drivers/gpu/drm/nouveau/nv04_display.c b/drivers/gpu/drm/nouveau/nv04_display.c
|
|
index ef77215..b35b7ed 100644
|
|
--- a/drivers/gpu/drm/nouveau/nv04_display.c
|
|
+++ b/drivers/gpu/drm/nouveau/nv04_display.c
|
|
@@ -93,10 +93,10 @@ 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_connector *connector, *ct;
|
|
struct drm_encoder *encoder;
|
|
struct drm_crtc *crtc;
|
|
- uint16_t connector[16] = { 0 };
|
|
int i, ret;
|
|
|
|
NV_DEBUG_KMS(dev, "\n");
|
|
@@ -133,19 +133,23 @@ nv04_display_create(struct drm_device *dev)
|
|
for (i = 0; i < dcb->entries; i++) {
|
|
struct dcb_entry *dcbent = &dcb->entry[i];
|
|
|
|
+ connector = nouveau_connector_create(dev, dcbent->connector);
|
|
+ if (IS_ERR(connector))
|
|
+ continue;
|
|
+
|
|
switch (dcbent->type) {
|
|
case OUTPUT_ANALOG:
|
|
- ret = nv04_dac_create(dev, dcbent);
|
|
+ ret = nv04_dac_create(connector, dcbent);
|
|
break;
|
|
case OUTPUT_LVDS:
|
|
case OUTPUT_TMDS:
|
|
- ret = nv04_dfp_create(dev, dcbent);
|
|
+ ret = nv04_dfp_create(connector, dcbent);
|
|
break;
|
|
case OUTPUT_TV:
|
|
if (dcbent->location == DCB_LOC_ON_CHIP)
|
|
- ret = nv17_tv_create(dev, dcbent);
|
|
+ ret = nv17_tv_create(connector, dcbent);
|
|
else
|
|
- ret = nv04_tv_create(dev, dcbent);
|
|
+ ret = nv04_tv_create(connector, dcbent);
|
|
break;
|
|
default:
|
|
NV_WARN(dev, "DCB type %d not known\n", dcbent->type);
|
|
@@ -154,51 +158,15 @@ 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;
|
|
+ list_for_each_entry_safe(connector, ct,
|
|
+ &dev->mode_config.connector_list, head) {
|
|
+ if (!connector->encoder_ids[0]) {
|
|
+ NV_WARN(dev, "%s has no encoders, removing\n",
|
|
+ drm_get_connector_name(connector));
|
|
+ connector->funcs->destroy(connector);
|
|
}
|
|
-
|
|
- nouveau_connector_create(dev, dcbent->connector, type);
|
|
}
|
|
|
|
/* Save previous state */
|
|
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_graph.c b/drivers/gpu/drm/nouveau/nv04_graph.c
|
|
index e260986..f0cbbc0 100644
|
|
--- a/drivers/gpu/drm/nouveau/nv04_graph.c
|
|
+++ b/drivers/gpu/drm/nouveau/nv04_graph.c
|
|
@@ -527,8 +527,7 @@ static int
|
|
nv04_graph_mthd_set_ref(struct nouveau_channel *chan, int grclass,
|
|
int mthd, uint32_t data)
|
|
{
|
|
- chan->fence.last_sequence_irq = data;
|
|
- nouveau_fence_handler(chan->dev, chan->id);
|
|
+ atomic_set(&chan->fence.last_sequence_irq, data);
|
|
return 0;
|
|
}
|
|
|
|
diff --git a/drivers/gpu/drm/nouveau/nv04_tv.c b/drivers/gpu/drm/nouveau/nv04_tv.c
|
|
index 9c63099..84b5954 100644
|
|
--- a/drivers/gpu/drm/nouveau/nv04_tv.c
|
|
+++ b/drivers/gpu/drm/nouveau/nv04_tv.c
|
|
@@ -223,10 +223,12 @@ static void nv04_tv_destroy(struct drm_encoder *encoder)
|
|
kfree(nv_encoder);
|
|
}
|
|
|
|
-int nv04_tv_create(struct drm_device *dev, struct dcb_entry *entry)
|
|
+int
|
|
+nv04_tv_create(struct drm_connector *connector, struct dcb_entry *entry)
|
|
{
|
|
struct nouveau_encoder *nv_encoder;
|
|
struct drm_encoder *encoder;
|
|
+ struct drm_device *dev = connector->dev;
|
|
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
|
struct i2c_adapter *adap;
|
|
struct drm_encoder_funcs *funcs = NULL;
|
|
@@ -262,11 +264,11 @@ 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);
|
|
|
|
- ret = drm_i2c_encoder_init(encoder->dev, to_encoder_slave(encoder), adap,
|
|
+ ret = drm_i2c_encoder_init(dev, to_encoder_slave(encoder), adap,
|
|
&nv04_tv_encoder_info[type].board_info);
|
|
|
|
NVLockVgaCrtcs(dev, was_locked);
|
|
@@ -294,7 +296,9 @@ int nv04_tv_create(struct drm_device *dev, struct dcb_entry *entry)
|
|
|
|
/* Set the slave encoder configuration */
|
|
sfuncs->set_config(encoder, nv04_tv_encoder_info[type].params);
|
|
+ sfuncs->create_resources(encoder, connector);
|
|
|
|
+ drm_mode_connector_attach_encoder(connector, encoder);
|
|
return 0;
|
|
|
|
fail:
|
|
diff --git a/drivers/gpu/drm/nouveau/nv17_tv.c b/drivers/gpu/drm/nouveau/nv17_tv.c
|
|
index 21ac6e4..44437ff 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);
|
|
}
|
|
}
|
|
|
|
@@ -744,8 +744,10 @@ static struct drm_encoder_funcs nv17_tv_funcs = {
|
|
.destroy = nv17_tv_destroy,
|
|
};
|
|
|
|
-int nv17_tv_create(struct drm_device *dev, struct dcb_entry *entry)
|
|
+int
|
|
+nv17_tv_create(struct drm_connector *connector, struct dcb_entry *entry)
|
|
{
|
|
+ struct drm_device *dev = connector->dev;
|
|
struct drm_encoder *encoder;
|
|
struct nv17_tv_encoder *tv_enc = NULL;
|
|
|
|
@@ -774,5 +776,7 @@ int nv17_tv_create(struct drm_device *dev, struct dcb_entry *entry)
|
|
encoder->possible_crtcs = entry->heads;
|
|
encoder->possible_clones = 0;
|
|
|
|
+ nv17_tv_create_resources(encoder, connector);
|
|
+ drm_mode_connector_attach_encoder(connector, encoder);
|
|
return 0;
|
|
}
|
|
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..2cdc2bf
|
|
--- /dev/null
|
|
+++ b/drivers/gpu/drm/nouveau/nv50_calc.c
|
|
@@ -0,0 +1,87 @@
|
|
+/*
|
|
+ * 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..e114f81 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 {
|
|
@@ -275,14 +275,11 @@ static const struct drm_encoder_funcs nv50_dac_encoder_funcs = {
|
|
};
|
|
|
|
int
|
|
-nv50_dac_create(struct drm_device *dev, struct dcb_entry *entry)
|
|
+nv50_dac_create(struct drm_connector *connector, struct dcb_entry *entry)
|
|
{
|
|
struct nouveau_encoder *nv_encoder;
|
|
struct drm_encoder *encoder;
|
|
|
|
- NV_DEBUG_KMS(dev, "\n");
|
|
- NV_INFO(dev, "Detected a DAC output\n");
|
|
-
|
|
nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
|
|
if (!nv_encoder)
|
|
return -ENOMEM;
|
|
@@ -293,12 +290,14 @@ nv50_dac_create(struct drm_device *dev, struct dcb_entry *entry)
|
|
|
|
nv_encoder->disconnect = nv50_dac_disconnect;
|
|
|
|
- drm_encoder_init(dev, encoder, &nv50_dac_encoder_funcs,
|
|
+ drm_encoder_init(connector->dev, encoder, &nv50_dac_encoder_funcs,
|
|
DRM_MODE_ENCODER_DAC);
|
|
drm_encoder_helper_add(encoder, &nv50_dac_helper_funcs);
|
|
|
|
encoder->possible_crtcs = entry->heads;
|
|
encoder->possible_clones = 0;
|
|
+
|
|
+ drm_mode_connector_attach_encoder(connector, encoder);
|
|
return 0;
|
|
}
|
|
|
|
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c
|
|
index 90f0bf5..a0d7467 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,8 @@ 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;
|
|
+ struct drm_connector *connector, *ct;
|
|
int ret, i;
|
|
|
|
NV_DEBUG_KMS(dev, "\n");
|
|
@@ -509,62 +507,39 @@ int nv50_display_create(struct drm_device *dev)
|
|
continue;
|
|
}
|
|
|
|
+ connector = nouveau_connector_create(dev, entry->connector);
|
|
+ if (IS_ERR(connector))
|
|
+ continue;
|
|
+
|
|
switch (entry->type) {
|
|
case OUTPUT_TMDS:
|
|
case OUTPUT_LVDS:
|
|
case OUTPUT_DP:
|
|
- nv50_sor_create(dev, entry);
|
|
+ nv50_sor_create(connector, entry);
|
|
break;
|
|
case OUTPUT_ANALOG:
|
|
- nv50_dac_create(dev, entry);
|
|
+ nv50_dac_create(connector, entry);
|
|
break;
|
|
default:
|
|
NV_WARN(dev, "DCB encoder %d unknown\n", entry->type);
|
|
continue;
|
|
}
|
|
-
|
|
- connector[entry->connector] |= (1 << entry->type);
|
|
}
|
|
|
|
- /* It appears that DCB 3.0+ VBIOS has a connector table, however,
|
|
- * I'm not 100% certain how to decode it correctly yet so just
|
|
- * look at what encoders are present on each connector index and
|
|
- * attempt to derive the connector type from that.
|
|
- */
|
|
- for (i = 0 ; i < dcb->entries; i++) {
|
|
- struct dcb_entry *entry = &dcb->entry[i];
|
|
- uint16_t encoders;
|
|
- int type;
|
|
-
|
|
- encoders = connector[entry->connector];
|
|
- if (!(encoders & (1 << entry->type)))
|
|
- continue;
|
|
- connector[entry->connector] = 0;
|
|
-
|
|
- if (encoders & (1 << OUTPUT_DP)) {
|
|
- type = DRM_MODE_CONNECTOR_DisplayPort;
|
|
- } else if (encoders & (1 << OUTPUT_TMDS)) {
|
|
- if (encoders & (1 << OUTPUT_ANALOG))
|
|
- type = DRM_MODE_CONNECTOR_DVII;
|
|
- else
|
|
- type = DRM_MODE_CONNECTOR_DVID;
|
|
- } else if (encoders & (1 << OUTPUT_ANALOG)) {
|
|
- type = DRM_MODE_CONNECTOR_VGA;
|
|
- } else if (encoders & (1 << OUTPUT_LVDS)) {
|
|
- type = DRM_MODE_CONNECTOR_LVDS;
|
|
- } else {
|
|
- type = DRM_MODE_CONNECTOR_Unknown;
|
|
+ list_for_each_entry_safe(connector, ct,
|
|
+ &dev->mode_config.connector_list, head) {
|
|
+ if (!connector->encoder_ids[0]) {
|
|
+ NV_WARN(dev, "%s has no encoders, removing\n",
|
|
+ drm_get_connector_name(connector));
|
|
+ connector->funcs->destroy(connector);
|
|
}
|
|
-
|
|
- if (type == DRM_MODE_CONNECTOR_Unknown)
|
|
- continue;
|
|
-
|
|
- nouveau_connector_create(dev, entry->connector, type);
|
|
}
|
|
|
|
ret = nv50_display_init(dev);
|
|
- if (ret)
|
|
+ if (ret) {
|
|
+ nv50_display_destroy(dev);
|
|
return ret;
|
|
+ }
|
|
|
|
return 0;
|
|
}
|
|
@@ -667,8 +642,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 +667,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 +685,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 +790,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 +843,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 +927,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 +985,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 +995,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,30 +28,7 @@
|
|
#include "drm.h"
|
|
#include "nouveau_drv.h"
|
|
|
|
-MODULE_FIRMWARE("nouveau/nv50.ctxprog");
|
|
-MODULE_FIRMWARE("nouveau/nv50.ctxvals");
|
|
-MODULE_FIRMWARE("nouveau/nv84.ctxprog");
|
|
-MODULE_FIRMWARE("nouveau/nv84.ctxvals");
|
|
-MODULE_FIRMWARE("nouveau/nv86.ctxprog");
|
|
-MODULE_FIRMWARE("nouveau/nv86.ctxvals");
|
|
-MODULE_FIRMWARE("nouveau/nv92.ctxprog");
|
|
-MODULE_FIRMWARE("nouveau/nv92.ctxvals");
|
|
-MODULE_FIRMWARE("nouveau/nv94.ctxprog");
|
|
-MODULE_FIRMWARE("nouveau/nv94.ctxvals");
|
|
-MODULE_FIRMWARE("nouveau/nv96.ctxprog");
|
|
-MODULE_FIRMWARE("nouveau/nv96.ctxvals");
|
|
-MODULE_FIRMWARE("nouveau/nv98.ctxprog");
|
|
-MODULE_FIRMWARE("nouveau/nv98.ctxvals");
|
|
-MODULE_FIRMWARE("nouveau/nva0.ctxprog");
|
|
-MODULE_FIRMWARE("nouveau/nva0.ctxvals");
|
|
-MODULE_FIRMWARE("nouveau/nva5.ctxprog");
|
|
-MODULE_FIRMWARE("nouveau/nva5.ctxvals");
|
|
-MODULE_FIRMWARE("nouveau/nva8.ctxprog");
|
|
-MODULE_FIRMWARE("nouveau/nva8.ctxvals");
|
|
-MODULE_FIRMWARE("nouveau/nvaa.ctxprog");
|
|
-MODULE_FIRMWARE("nouveau/nvaa.ctxvals");
|
|
-MODULE_FIRMWARE("nouveau/nvac.ctxprog");
|
|
-MODULE_FIRMWARE("nouveau/nvac.ctxvals");
|
|
+#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..4832bba 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
|
|
@@ -272,32 +272,22 @@ static const struct drm_encoder_funcs nv50_sor_encoder_funcs = {
|
|
};
|
|
|
|
int
|
|
-nv50_sor_create(struct drm_device *dev, struct dcb_entry *entry)
|
|
+nv50_sor_create(struct drm_connector *connector, struct dcb_entry *entry)
|
|
{
|
|
struct nouveau_encoder *nv_encoder = NULL;
|
|
+ struct drm_device *dev = connector->dev;
|
|
struct drm_encoder *encoder;
|
|
- bool dum;
|
|
int type;
|
|
|
|
NV_DEBUG_KMS(dev, "\n");
|
|
|
|
switch (entry->type) {
|
|
case OUTPUT_TMDS:
|
|
- NV_INFO(dev, "Detected a TMDS output\n");
|
|
+ case OUTPUT_DP:
|
|
type = DRM_MODE_ENCODER_TMDS;
|
|
break;
|
|
case OUTPUT_LVDS:
|
|
- NV_INFO(dev, "Detected a LVDS output\n");
|
|
type = DRM_MODE_ENCODER_LVDS;
|
|
-
|
|
- if (nouveau_bios_parse_lvds_table(dev, 0, &dum, &dum)) {
|
|
- NV_ERROR(dev, "Failed parsing LVDS table\n");
|
|
- return -EINVAL;
|
|
- }
|
|
- break;
|
|
- case OUTPUT_DP:
|
|
- NV_INFO(dev, "Detected a DP output\n");
|
|
- type = DRM_MODE_ENCODER_TMDS;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
@@ -319,5 +309,29 @@ 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;
|
|
+
|
|
+ tmp = nv_rd32(dev, 0x61c700 + (or * 0x800));
|
|
+
|
|
+ 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;
|
|
+ }
|
|
+
|
|
+ drm_mode_connector_attach_encoder(connector, encoder);
|
|
return 0;
|
|
}
|
|
--
|
|
1.7.1
|
|
|