From: Alex Deucher alexander.deucher@amd.com
These are the radeon patches for 3.11. Some of these patches are huge so, it might be easier to review things here: http://cgit.freedesktop.org/~agd5f/linux/log/?h=drm-next-3.11-wip
I'll send a formal pull in request in the next day or two.
Highlights of this series: - DPM support (Dynamic Power Management) for r6xx-SI - Support for CIK (Sea Islands): modesetting, 3D, compute, UVD - ASPM support for R6xx-SI
Since this is the initial public DPM code, it's still disabled by default until we get more community testing. Pass dpm=1 to the radeon module to enable it.
Alex Deucher (164): drm/radeon: fix AVI infoframe generation drm/radeon: add backlight quirk for hybrid mac drm/radeon: add a reset work handler drm/radeon: add CIK chip families drm/radeon: add DCE8 macro for CIK drm/radeon: adapt to PCI BAR changes on CIK drm/radeon: add gpu init support for CIK (v9) drm/radeon: Add support for CIK GPU reset (v2) drm/radeon: add support for MC/VM setup on CIK (v6) drm/radeon/cik: stop page faults from hanging the system (v2) drm/radeon: add initial ucode loading for CIK (v5) drm/radeon: add support mc ucode loading on CIK (v2) drm/radeon: Add CP init for CIK (v7) drm/radeon: add IB and fence dispatch functions for CIK gfx (v7) drm/radeon: add ring and IB tests for CIK (v3) drm/radeon: implement async vm_flush for the CP (v7) drm/radeon: Add support for RLC init on CIK (v4) drm/radeon: add support for interrupts on CIK (v5) drm/radeon/cik: log and handle VM page fault interrupts drm/radeon/cik: add support for sDMA dma engines (v8) drm/radeon: implement async vm_flush for the sDMA (v6) drm/radeon/cik: add support for doing async VM pt updates (v5) drm/radeon/cik: fill in startup/shutdown callbacks (v4) drm/radeon: upstream ObjectID.h updates (v2) drm/radeon: upstream atombios.h updates (v2) drm/radeon: atombios power table updates (v2) drm/radeon: handle the integrated thermal controller on CI drm/radeon: update power state parsing for CI drm/radeon/dce8: add support for display watermark setup drm/radeon/cik: add hw cursor support (v2) drm/radeon/dce8: properly handle interlaced timing drm/radeon/dce8: crtc_set_base updates drm/radeon/atom: add DCE8 encoder support drm/radeon/atom: add support for new DVO tables drm/radeon: update DISPCLK programming for DCE8 drm/radeon: add support pll selection for DCE8 (v4) drm/radeon: Handle PPLL0 powerdown on DCE8 drm/radeon: use frac fb div on DCE8 drm/radeon: add SS override support for KB/KV drm/radeon: Update radeon_info_ioctl for CIK (v2) drm/radeon: add get_gpu_clock_counter() callback for cik drm/radeon: update CIK soft reset drm/radeon: add indirect register accessors for SMC registers drm/radeon: add get_xclk() callback for CIK drm/radeon/cik: add pcie_port indirect register accessors drm/radeon: update radeon_atom_get_clock_dividers() for SI drm/radeon: update radeon_atom_get_clock_dividers for CIK drm/radeon/cik: add srbm_select function drm/radeon: use callbacks for ring pointer handling drm/radeon: implement simple doorbell page allocator drm/radeon/cik: Add support for compute queues (v2) drm/radeon/cik: switch to type3 nop packet for compute rings drm/radeon: fix up ring functions for compute rings drm/radeon/cik: add support for compute interrupts drm/radeon/cik: add support for golden register init drm/radeon: add radeon_asic struct for CIK (v11) drm/radeon: add cik tile mode array query drm/radeon: add current Bonaire PCI ids drm/radeon: add current KB pci ids drm/radeon/kms: add accessors for RCU indirect space drm/radeon/evergreen: add indirect register accessors for CG registers drm/radeon: make get_temperature functions a callback drm/radeon: add support for thermal sensor on tn drm/radeon/kms: move ucode defines to a separate header drm/radeon: properly set up the RLC on ON/LN/TN (v3) drm/radeon/kms: add atom helper functions for dpm (v3) drm/radeon/kms: add new asic struct for rv6xx (v3) drm/radeon/kms: add common dpm infrastructure drm/radeon/kms: fix up rs780/rs880 display watermark calc for dpm drm/radeon/kms: fix up 6xx/7xx display watermark calc for dpm drm/radeon/kms: fix up dce4/5 display watermark calc for dpm drm/radeon/kms: fix up dce6 display watermark calc for dpm drm/radeon/kms: add common r600 dpm functions drm/radeon/kms: add dpm support for rs780/rs880 drm/radeon/kms: add dpm support for rv6xx drm/radeon/kms: add dpm support for rv7xx (v2) drm/radeon/kms: add dpm support for evergreen (v2) drm/radeon/kms: add dpm support for btc (v2) drm/radeon/kms: add dpm support for sumo asics drm/radeon/kms: add dpm support for trinity asics drm/radeon/dpm: let atom control display phy powergating drm/radeon: add dpm UVD handling for r7xx asics drm/radeon: add dpm UVD handling for evergreen/btc asics drm/radeon: add dpm UVD handling for sumo asics drm/radeon: add dpm UVD handling for TN asics (v2) drm/radeon/kms: enable UVD as needed (v9) drm/radeon/dpm: add helpers for extended power tables (v2) drm/radeon/dpm: track whether we are on AC or battery drm/radeon/dpm: fixup dynamic state adjust for sumo drm/radeon/dpm: fixup dynamic state adjust for TN drm/radeon/dpm: fixup dynamic state adjust for btc (v2) drm/radeon/kms: add dpm support for cayman drm/radeon/cayman: update tdp limits in set_power_state drm/radeon/dpm/rs780: restructure code drm/radeon/dpm/rv6xx: restructure code drm/radeon/dpm/rv7xx: restructure code drm/radeon/dpm/evergreen: restructure code drm/radeon/dpm/btc: restructure code drm/radeon/dpm/cayman: restructure code drm/radeon/dpm/sumo: restructure code drm/radeon/dpm/tn: restructure code drm/radeon/dpm: add new pre/post_set_power_state callbacks drm/radeon/dpm: add pre/post_set_power_state callbacks (6xx-eg) drm/radeon/dpm: add pre/post_set_power_state callback (sumo) drm/radeon/dpm: add pre/post_set_power_state callback (TN) drm/radeon/dpm: add pre/post_set_power_state callback (BTC) drm/radeon/dpm: add pre/post_set_power_state callback (cayman) drm/radeon/dpm: remove broken dyn state remnants drm/radeon: add missing UVD clock set in cayman dpm code drm/radeon/dpm: remove local sumo_get_xclk() drm/radeon: implement apci perf request drm/radeon/atom: add helper to calcuate mpll params drm/radeon: update radeon_atom_is_voltage_gpio() for SI drm/radeon: update radeon_atom_get_voltage_table() for SI drm/radeon: implement pcie gen2/3 support for SI drm/radeon: add accessors of pif_phy indirect register space drm/radeon: add support for ASPM on evergreen asics drm/radeon: add support for ASPM on SI asics (v2) drm/radeon: enable additional power gating features on trinity drm/radeon: update rlc programming sequence on SI drm/radeon: add atom get leakage vddc function drm/radeon: add indirect accessors for UVD CTX registers drm/radeon: initialize save/restore buffer for pg on verde drm/radeon: add clearstate init for verde power gating drm/radeon: implement clock and power gating for SI drm/radeon/dpm: add an enum for pcie gen selection drm/radeon/dpm: pull in phase shedding limits from atom drm/radeon/dpm: endian fixes for extended power tables drm/radeon/dpm: pull in ppm info from atom drm/radeon/dpm: save some display parameters for DPM drm/radeon: minor sid.h cleanup drm: add some additional fixed point helpers (v2) drm/radeon/dpm/cayman: use new fixed point functions drm/radeon: fix some memory leaks in extended table parsing drm/radeon/dpm: validate voltages against dispclk requirements drm/radeon: update radeon_atombios_get_default_voltages for mvdd drm/radeon/dpm: add pcie gen helper function drm/radeon: fix typo in atom voltage table handling (6xx-ni) drm/radeon: fix typo in atom voltage table handling (si+) drm/radeon/atom: fix voltage table parsing drm/radeon/dpm/ni: properly catch errors in dpm setup drm/radeon/dpm/btc: properly catch errors in dpm setup drm/radeon/dpm/evergreen: properly catch errors in dpm setup drm/radeon/dpm/sumo: properly catch errors in dpm setup drm/radeon/dpm/trinity: properly catch errors in dpm setup drm/radeon/dpm/r7xx: properly catch errors in dpm setup drm/radeon/dpm/r6xx: properly catch errors in dpm setup drm/radeon/dpm/rs780: properly catch errors in dpm setup drm/radeon: add SI to r600_is_internal_thermal_sensor() drm/radeon: switch SI to use radeon_ucode.h drm/radeon/kms: add dpm support for SI (v3) drm/radeon/dpm: add dpm_enable failure output (7xx-ni) drm/radeon/dpm: add dpm_enable failure output (si) drm/radeon/dpm: add dpm_set_power_state failure output (7xx-ni) drm/radeon/dpm: add dpm_set_power_state failure output (si) drm/radeon/dpm: fix typo in setting uvd clock drm/radeon/si: fix typo in function name drm/radeon: fix typo in cik_select_se_sh() drm/radeon: fix typo in ni_print_power_state drm/radeon/dpm: add support for setting UVD clock on rs780 drm/radeon/dpm: add support for setting UVD clock on rv6xx drm/radeon/dpm: fix UVD clock setting on cayman drm/radeon/dpm: fix UVD clock setting on SI drm/radeon: fix endian issues in atombios dpm code
Christian König (1): drm/radeon: add UVD support for CIK (v3)
drivers/gpu/drm/radeon/Makefile | 5 +- drivers/gpu/drm/radeon/ObjectID.h | 40 + drivers/gpu/drm/radeon/atombios.h | 547 ++- drivers/gpu/drm/radeon/atombios_crtc.c | 88 +- drivers/gpu/drm/radeon/atombios_encoders.c | 51 +- drivers/gpu/drm/radeon/btc_dpm.c | 2731 ++++++++++ drivers/gpu/drm/radeon/btc_dpm.h | 57 + drivers/gpu/drm/radeon/btcd.h | 181 + drivers/gpu/drm/radeon/cik.c | 6995 +++++++++++++++++++++++++ drivers/gpu/drm/radeon/cik_blit_shaders.c | 246 + drivers/gpu/drm/radeon/cik_blit_shaders.h | 32 + drivers/gpu/drm/radeon/cik_reg.h | 147 + drivers/gpu/drm/radeon/cikd.h | 1297 +++++ drivers/gpu/drm/radeon/clearstate_cayman.h | 1081 ++++ drivers/gpu/drm/radeon/clearstate_defs.h | 44 + drivers/gpu/drm/radeon/clearstate_evergreen.h | 1080 ++++ drivers/gpu/drm/radeon/clearstate_si.h | 941 ++++ drivers/gpu/drm/radeon/cypress_dpm.c | 2170 ++++++++ drivers/gpu/drm/radeon/cypress_dpm.h | 160 + drivers/gpu/drm/radeon/evergreen.c | 644 +++- drivers/gpu/drm/radeon/evergreen_hdmi.c | 11 +- drivers/gpu/drm/radeon/evergreen_reg.h | 12 + drivers/gpu/drm/radeon/evergreen_smc.h | 67 + drivers/gpu/drm/radeon/evergreend.h | 389 ++- drivers/gpu/drm/radeon/ni.c | 198 +- drivers/gpu/drm/radeon/ni_dpm.c | 4288 +++++++++++++++ drivers/gpu/drm/radeon/ni_dpm.h | 248 + drivers/gpu/drm/radeon/nid.h | 561 ++ drivers/gpu/drm/radeon/nislands_smc.h | 329 ++ drivers/gpu/drm/radeon/ppsmc.h | 110 + drivers/gpu/drm/radeon/r600.c | 147 +- drivers/gpu/drm/radeon/r600_dpm.c | 1024 ++++ drivers/gpu/drm/radeon/r600_dpm.h | 226 + drivers/gpu/drm/radeon/r600_hdmi.c | 11 +- drivers/gpu/drm/radeon/r600_reg.h | 6 + drivers/gpu/drm/radeon/r600d.h | 232 +- drivers/gpu/drm/radeon/radeon.h | 517 ++- drivers/gpu/drm/radeon/radeon_acpi.c | 145 + drivers/gpu/drm/radeon/radeon_asic.c | 524 ++- drivers/gpu/drm/radeon/radeon_asic.h | 166 + drivers/gpu/drm/radeon/radeon_atombios.c | 880 +++- drivers/gpu/drm/radeon/radeon_cs.c | 1 + drivers/gpu/drm/radeon/radeon_cursor.c | 10 +- drivers/gpu/drm/radeon/radeon_device.c | 106 +- drivers/gpu/drm/radeon/radeon_display.c | 21 +- drivers/gpu/drm/radeon/radeon_drv.c | 7 +- drivers/gpu/drm/radeon/radeon_family.h | 3 + drivers/gpu/drm/radeon/radeon_irq_kms.c | 20 + drivers/gpu/drm/radeon/radeon_kms.c | 41 +- drivers/gpu/drm/radeon/radeon_mode.h | 93 + drivers/gpu/drm/radeon/radeon_pm.c | 610 ++- drivers/gpu/drm/radeon/radeon_reg.h | 1 + drivers/gpu/drm/radeon/radeon_ring.c | 55 +- drivers/gpu/drm/radeon/radeon_ucode.h | 129 + drivers/gpu/drm/radeon/radeon_uvd.c | 32 +- drivers/gpu/drm/radeon/rs690.c | 291 +- drivers/gpu/drm/radeon/rs780_dpm.c | 963 ++++ drivers/gpu/drm/radeon/rs780_dpm.h | 109 + drivers/gpu/drm/radeon/rs780d.h | 168 + drivers/gpu/drm/radeon/rv515.c | 224 +- drivers/gpu/drm/radeon/rv6xx_dpm.c | 2059 ++++++++ drivers/gpu/drm/radeon/rv6xx_dpm.h | 95 + drivers/gpu/drm/radeon/rv6xxd.h | 246 + drivers/gpu/drm/radeon/rv730_dpm.c | 508 ++ drivers/gpu/drm/radeon/rv730d.h | 165 + drivers/gpu/drm/radeon/rv740_dpm.c | 416 ++ drivers/gpu/drm/radeon/rv740d.h | 117 + drivers/gpu/drm/radeon/rv770_dpm.c | 2446 +++++++++ drivers/gpu/drm/radeon/rv770_dpm.h | 287 + drivers/gpu/drm/radeon/rv770_smc.c | 621 +++ drivers/gpu/drm/radeon/rv770_smc.h | 209 + drivers/gpu/drm/radeon/rv770d.h | 279 +- drivers/gpu/drm/radeon/si.c | 1337 +++++- drivers/gpu/drm/radeon/si_dpm.c | 6369 ++++++++++++++++++++++ drivers/gpu/drm/radeon/si_dpm.h | 227 + drivers/gpu/drm/radeon/si_smc.c | 284 + drivers/gpu/drm/radeon/sid.h | 599 ++- drivers/gpu/drm/radeon/sislands_smc.h | 397 ++ drivers/gpu/drm/radeon/sumo_dpm.c | 1801 +++++++ drivers/gpu/drm/radeon/sumo_dpm.h | 220 + drivers/gpu/drm/radeon/sumo_smc.c | 222 + drivers/gpu/drm/radeon/sumod.h | 372 ++ drivers/gpu/drm/radeon/trinity_dpm.c | 1887 +++++++ drivers/gpu/drm/radeon/trinity_dpm.h | 131 + drivers/gpu/drm/radeon/trinity_smc.c | 115 + drivers/gpu/drm/radeon/trinityd.h | 228 + include/drm/drm_fixed.h | 96 + include/drm/drm_pciids.h | 24 + 88 files changed, 52708 insertions(+), 561 deletions(-) create mode 100644 drivers/gpu/drm/radeon/btc_dpm.c create mode 100644 drivers/gpu/drm/radeon/btc_dpm.h create mode 100644 drivers/gpu/drm/radeon/btcd.h create mode 100644 drivers/gpu/drm/radeon/cik.c create mode 100644 drivers/gpu/drm/radeon/cik_blit_shaders.c create mode 100644 drivers/gpu/drm/radeon/cik_blit_shaders.h create mode 100644 drivers/gpu/drm/radeon/cik_reg.h create mode 100644 drivers/gpu/drm/radeon/cikd.h create mode 100644 drivers/gpu/drm/radeon/clearstate_cayman.h create mode 100644 drivers/gpu/drm/radeon/clearstate_defs.h create mode 100644 drivers/gpu/drm/radeon/clearstate_evergreen.h create mode 100644 drivers/gpu/drm/radeon/clearstate_si.h create mode 100644 drivers/gpu/drm/radeon/cypress_dpm.c create mode 100644 drivers/gpu/drm/radeon/cypress_dpm.h create mode 100644 drivers/gpu/drm/radeon/evergreen_smc.h create mode 100644 drivers/gpu/drm/radeon/ni_dpm.c create mode 100644 drivers/gpu/drm/radeon/ni_dpm.h create mode 100644 drivers/gpu/drm/radeon/nislands_smc.h create mode 100644 drivers/gpu/drm/radeon/ppsmc.h create mode 100644 drivers/gpu/drm/radeon/r600_dpm.c create mode 100644 drivers/gpu/drm/radeon/r600_dpm.h create mode 100644 drivers/gpu/drm/radeon/radeon_ucode.h create mode 100644 drivers/gpu/drm/radeon/rs780_dpm.c create mode 100644 drivers/gpu/drm/radeon/rs780_dpm.h create mode 100644 drivers/gpu/drm/radeon/rs780d.h create mode 100644 drivers/gpu/drm/radeon/rv6xx_dpm.c create mode 100644 drivers/gpu/drm/radeon/rv6xx_dpm.h create mode 100644 drivers/gpu/drm/radeon/rv6xxd.h create mode 100644 drivers/gpu/drm/radeon/rv730_dpm.c create mode 100644 drivers/gpu/drm/radeon/rv730d.h create mode 100644 drivers/gpu/drm/radeon/rv740_dpm.c create mode 100644 drivers/gpu/drm/radeon/rv740d.h create mode 100644 drivers/gpu/drm/radeon/rv770_dpm.c create mode 100644 drivers/gpu/drm/radeon/rv770_dpm.h create mode 100644 drivers/gpu/drm/radeon/rv770_smc.c create mode 100644 drivers/gpu/drm/radeon/rv770_smc.h create mode 100644 drivers/gpu/drm/radeon/si_dpm.c create mode 100644 drivers/gpu/drm/radeon/si_dpm.h create mode 100644 drivers/gpu/drm/radeon/si_smc.c create mode 100644 drivers/gpu/drm/radeon/sislands_smc.h create mode 100644 drivers/gpu/drm/radeon/sumo_dpm.c create mode 100644 drivers/gpu/drm/radeon/sumo_dpm.h create mode 100644 drivers/gpu/drm/radeon/sumo_smc.c create mode 100644 drivers/gpu/drm/radeon/sumod.h create mode 100644 drivers/gpu/drm/radeon/trinity_dpm.c create mode 100644 drivers/gpu/drm/radeon/trinity_dpm.h create mode 100644 drivers/gpu/drm/radeon/trinity_smc.c create mode 100644 drivers/gpu/drm/radeon/trinityd.h
On Wed, Jun 26, 2013 at 09:21:20AM -0400, alexdeucher@gmail.com wrote:
From: Alex Deucher alexander.deucher@amd.com
These are the radeon patches for 3.11. Some of these patches are huge so, it might be easier to review things here: http://cgit.freedesktop.org/~agd5f/linux/log/?h=drm-next-3.11-wip
I'll send a formal pull in request in the next day or two.
Highlights of this series:
- DPM support (Dynamic Power Management) for r6xx-SI
- Support for CIK (Sea Islands): modesetting, 3D, compute, UVD
- ASPM support for R6xx-SI
Since this is the initial public DPM code, it's still disabled by default until we get more community testing. Pass dpm=1 to the radeon module to enable it.
So for the serie (except one i replied to) got my :
Reviewed-by: Jerome Glisse jglisse@redhat.com
I mostly checked the logic behind function ie what function is supose to do and does it do it properly. I didn't do any tedious check like is it using proper define for this generation.
I wish more of the acronym related to power management were documented. There is so many magic name that it's often hard to understand the logic of the code, what it's doing and what is it that it's computing.
Regarding CIK i fear that the hsa addition doesn't fit very well with our ring stuff, we probably don't want a ring struct for each of the 32 or 64 queues. But as right now we are only using few queues, that's probably alright.
Cheers, Jerome
Alex Deucher (164): drm/radeon: fix AVI infoframe generation drm/radeon: add backlight quirk for hybrid mac drm/radeon: add a reset work handler drm/radeon: add CIK chip families drm/radeon: add DCE8 macro for CIK drm/radeon: adapt to PCI BAR changes on CIK drm/radeon: add gpu init support for CIK (v9) drm/radeon: Add support for CIK GPU reset (v2) drm/radeon: add support for MC/VM setup on CIK (v6) drm/radeon/cik: stop page faults from hanging the system (v2) drm/radeon: add initial ucode loading for CIK (v5) drm/radeon: add support mc ucode loading on CIK (v2) drm/radeon: Add CP init for CIK (v7) drm/radeon: add IB and fence dispatch functions for CIK gfx (v7) drm/radeon: add ring and IB tests for CIK (v3) drm/radeon: implement async vm_flush for the CP (v7) drm/radeon: Add support for RLC init on CIK (v4) drm/radeon: add support for interrupts on CIK (v5) drm/radeon/cik: log and handle VM page fault interrupts drm/radeon/cik: add support for sDMA dma engines (v8) drm/radeon: implement async vm_flush for the sDMA (v6) drm/radeon/cik: add support for doing async VM pt updates (v5) drm/radeon/cik: fill in startup/shutdown callbacks (v4) drm/radeon: upstream ObjectID.h updates (v2) drm/radeon: upstream atombios.h updates (v2) drm/radeon: atombios power table updates (v2) drm/radeon: handle the integrated thermal controller on CI drm/radeon: update power state parsing for CI drm/radeon/dce8: add support for display watermark setup drm/radeon/cik: add hw cursor support (v2) drm/radeon/dce8: properly handle interlaced timing drm/radeon/dce8: crtc_set_base updates drm/radeon/atom: add DCE8 encoder support drm/radeon/atom: add support for new DVO tables drm/radeon: update DISPCLK programming for DCE8 drm/radeon: add support pll selection for DCE8 (v4) drm/radeon: Handle PPLL0 powerdown on DCE8 drm/radeon: use frac fb div on DCE8 drm/radeon: add SS override support for KB/KV drm/radeon: Update radeon_info_ioctl for CIK (v2) drm/radeon: add get_gpu_clock_counter() callback for cik drm/radeon: update CIK soft reset drm/radeon: add indirect register accessors for SMC registers drm/radeon: add get_xclk() callback for CIK drm/radeon/cik: add pcie_port indirect register accessors drm/radeon: update radeon_atom_get_clock_dividers() for SI drm/radeon: update radeon_atom_get_clock_dividers for CIK drm/radeon/cik: add srbm_select function drm/radeon: use callbacks for ring pointer handling drm/radeon: implement simple doorbell page allocator drm/radeon/cik: Add support for compute queues (v2) drm/radeon/cik: switch to type3 nop packet for compute rings drm/radeon: fix up ring functions for compute rings drm/radeon/cik: add support for compute interrupts drm/radeon/cik: add support for golden register init drm/radeon: add radeon_asic struct for CIK (v11) drm/radeon: add cik tile mode array query drm/radeon: add current Bonaire PCI ids drm/radeon: add current KB pci ids drm/radeon/kms: add accessors for RCU indirect space drm/radeon/evergreen: add indirect register accessors for CG registers drm/radeon: make get_temperature functions a callback drm/radeon: add support for thermal sensor on tn drm/radeon/kms: move ucode defines to a separate header drm/radeon: properly set up the RLC on ON/LN/TN (v3) drm/radeon/kms: add atom helper functions for dpm (v3) drm/radeon/kms: add new asic struct for rv6xx (v3) drm/radeon/kms: add common dpm infrastructure drm/radeon/kms: fix up rs780/rs880 display watermark calc for dpm drm/radeon/kms: fix up 6xx/7xx display watermark calc for dpm drm/radeon/kms: fix up dce4/5 display watermark calc for dpm drm/radeon/kms: fix up dce6 display watermark calc for dpm drm/radeon/kms: add common r600 dpm functions drm/radeon/kms: add dpm support for rs780/rs880 drm/radeon/kms: add dpm support for rv6xx drm/radeon/kms: add dpm support for rv7xx (v2) drm/radeon/kms: add dpm support for evergreen (v2) drm/radeon/kms: add dpm support for btc (v2) drm/radeon/kms: add dpm support for sumo asics drm/radeon/kms: add dpm support for trinity asics drm/radeon/dpm: let atom control display phy powergating drm/radeon: add dpm UVD handling for r7xx asics drm/radeon: add dpm UVD handling for evergreen/btc asics drm/radeon: add dpm UVD handling for sumo asics drm/radeon: add dpm UVD handling for TN asics (v2) drm/radeon/kms: enable UVD as needed (v9) drm/radeon/dpm: add helpers for extended power tables (v2) drm/radeon/dpm: track whether we are on AC or battery drm/radeon/dpm: fixup dynamic state adjust for sumo drm/radeon/dpm: fixup dynamic state adjust for TN drm/radeon/dpm: fixup dynamic state adjust for btc (v2) drm/radeon/kms: add dpm support for cayman drm/radeon/cayman: update tdp limits in set_power_state drm/radeon/dpm/rs780: restructure code drm/radeon/dpm/rv6xx: restructure code drm/radeon/dpm/rv7xx: restructure code drm/radeon/dpm/evergreen: restructure code drm/radeon/dpm/btc: restructure code drm/radeon/dpm/cayman: restructure code drm/radeon/dpm/sumo: restructure code drm/radeon/dpm/tn: restructure code drm/radeon/dpm: add new pre/post_set_power_state callbacks drm/radeon/dpm: add pre/post_set_power_state callbacks (6xx-eg) drm/radeon/dpm: add pre/post_set_power_state callback (sumo) drm/radeon/dpm: add pre/post_set_power_state callback (TN) drm/radeon/dpm: add pre/post_set_power_state callback (BTC) drm/radeon/dpm: add pre/post_set_power_state callback (cayman) drm/radeon/dpm: remove broken dyn state remnants drm/radeon: add missing UVD clock set in cayman dpm code drm/radeon/dpm: remove local sumo_get_xclk() drm/radeon: implement apci perf request drm/radeon/atom: add helper to calcuate mpll params drm/radeon: update radeon_atom_is_voltage_gpio() for SI drm/radeon: update radeon_atom_get_voltage_table() for SI drm/radeon: implement pcie gen2/3 support for SI drm/radeon: add accessors of pif_phy indirect register space drm/radeon: add support for ASPM on evergreen asics drm/radeon: add support for ASPM on SI asics (v2) drm/radeon: enable additional power gating features on trinity drm/radeon: update rlc programming sequence on SI drm/radeon: add atom get leakage vddc function drm/radeon: add indirect accessors for UVD CTX registers drm/radeon: initialize save/restore buffer for pg on verde drm/radeon: add clearstate init for verde power gating drm/radeon: implement clock and power gating for SI drm/radeon/dpm: add an enum for pcie gen selection drm/radeon/dpm: pull in phase shedding limits from atom drm/radeon/dpm: endian fixes for extended power tables drm/radeon/dpm: pull in ppm info from atom drm/radeon/dpm: save some display parameters for DPM drm/radeon: minor sid.h cleanup drm: add some additional fixed point helpers (v2) drm/radeon/dpm/cayman: use new fixed point functions drm/radeon: fix some memory leaks in extended table parsing drm/radeon/dpm: validate voltages against dispclk requirements drm/radeon: update radeon_atombios_get_default_voltages for mvdd drm/radeon/dpm: add pcie gen helper function drm/radeon: fix typo in atom voltage table handling (6xx-ni) drm/radeon: fix typo in atom voltage table handling (si+) drm/radeon/atom: fix voltage table parsing drm/radeon/dpm/ni: properly catch errors in dpm setup drm/radeon/dpm/btc: properly catch errors in dpm setup drm/radeon/dpm/evergreen: properly catch errors in dpm setup drm/radeon/dpm/sumo: properly catch errors in dpm setup drm/radeon/dpm/trinity: properly catch errors in dpm setup drm/radeon/dpm/r7xx: properly catch errors in dpm setup drm/radeon/dpm/r6xx: properly catch errors in dpm setup drm/radeon/dpm/rs780: properly catch errors in dpm setup drm/radeon: add SI to r600_is_internal_thermal_sensor() drm/radeon: switch SI to use radeon_ucode.h drm/radeon/kms: add dpm support for SI (v3) drm/radeon/dpm: add dpm_enable failure output (7xx-ni) drm/radeon/dpm: add dpm_enable failure output (si) drm/radeon/dpm: add dpm_set_power_state failure output (7xx-ni) drm/radeon/dpm: add dpm_set_power_state failure output (si) drm/radeon/dpm: fix typo in setting uvd clock drm/radeon/si: fix typo in function name drm/radeon: fix typo in cik_select_se_sh() drm/radeon: fix typo in ni_print_power_state drm/radeon/dpm: add support for setting UVD clock on rs780 drm/radeon/dpm: add support for setting UVD clock on rv6xx drm/radeon/dpm: fix UVD clock setting on cayman drm/radeon/dpm: fix UVD clock setting on SI drm/radeon: fix endian issues in atombios dpm code
Christian König (1): drm/radeon: add UVD support for CIK (v3)
drivers/gpu/drm/radeon/Makefile | 5 +- drivers/gpu/drm/radeon/ObjectID.h | 40 + drivers/gpu/drm/radeon/atombios.h | 547 ++- drivers/gpu/drm/radeon/atombios_crtc.c | 88 +- drivers/gpu/drm/radeon/atombios_encoders.c | 51 +- drivers/gpu/drm/radeon/btc_dpm.c | 2731 ++++++++++ drivers/gpu/drm/radeon/btc_dpm.h | 57 + drivers/gpu/drm/radeon/btcd.h | 181 + drivers/gpu/drm/radeon/cik.c | 6995 +++++++++++++++++++++++++ drivers/gpu/drm/radeon/cik_blit_shaders.c | 246 + drivers/gpu/drm/radeon/cik_blit_shaders.h | 32 + drivers/gpu/drm/radeon/cik_reg.h | 147 + drivers/gpu/drm/radeon/cikd.h | 1297 +++++ drivers/gpu/drm/radeon/clearstate_cayman.h | 1081 ++++ drivers/gpu/drm/radeon/clearstate_defs.h | 44 + drivers/gpu/drm/radeon/clearstate_evergreen.h | 1080 ++++ drivers/gpu/drm/radeon/clearstate_si.h | 941 ++++ drivers/gpu/drm/radeon/cypress_dpm.c | 2170 ++++++++ drivers/gpu/drm/radeon/cypress_dpm.h | 160 + drivers/gpu/drm/radeon/evergreen.c | 644 +++- drivers/gpu/drm/radeon/evergreen_hdmi.c | 11 +- drivers/gpu/drm/radeon/evergreen_reg.h | 12 + drivers/gpu/drm/radeon/evergreen_smc.h | 67 + drivers/gpu/drm/radeon/evergreend.h | 389 ++- drivers/gpu/drm/radeon/ni.c | 198 +- drivers/gpu/drm/radeon/ni_dpm.c | 4288 +++++++++++++++ drivers/gpu/drm/radeon/ni_dpm.h | 248 + drivers/gpu/drm/radeon/nid.h | 561 ++ drivers/gpu/drm/radeon/nislands_smc.h | 329 ++ drivers/gpu/drm/radeon/ppsmc.h | 110 + drivers/gpu/drm/radeon/r600.c | 147 +- drivers/gpu/drm/radeon/r600_dpm.c | 1024 ++++ drivers/gpu/drm/radeon/r600_dpm.h | 226 + drivers/gpu/drm/radeon/r600_hdmi.c | 11 +- drivers/gpu/drm/radeon/r600_reg.h | 6 + drivers/gpu/drm/radeon/r600d.h | 232 +- drivers/gpu/drm/radeon/radeon.h | 517 ++- drivers/gpu/drm/radeon/radeon_acpi.c | 145 + drivers/gpu/drm/radeon/radeon_asic.c | 524 ++- drivers/gpu/drm/radeon/radeon_asic.h | 166 + drivers/gpu/drm/radeon/radeon_atombios.c | 880 +++- drivers/gpu/drm/radeon/radeon_cs.c | 1 + drivers/gpu/drm/radeon/radeon_cursor.c | 10 +- drivers/gpu/drm/radeon/radeon_device.c | 106 +- drivers/gpu/drm/radeon/radeon_display.c | 21 +- drivers/gpu/drm/radeon/radeon_drv.c | 7 +- drivers/gpu/drm/radeon/radeon_family.h | 3 + drivers/gpu/drm/radeon/radeon_irq_kms.c | 20 + drivers/gpu/drm/radeon/radeon_kms.c | 41 +- drivers/gpu/drm/radeon/radeon_mode.h | 93 + drivers/gpu/drm/radeon/radeon_pm.c | 610 ++- drivers/gpu/drm/radeon/radeon_reg.h | 1 + drivers/gpu/drm/radeon/radeon_ring.c | 55 +- drivers/gpu/drm/radeon/radeon_ucode.h | 129 + drivers/gpu/drm/radeon/radeon_uvd.c | 32 +- drivers/gpu/drm/radeon/rs690.c | 291 +- drivers/gpu/drm/radeon/rs780_dpm.c | 963 ++++ drivers/gpu/drm/radeon/rs780_dpm.h | 109 + drivers/gpu/drm/radeon/rs780d.h | 168 + drivers/gpu/drm/radeon/rv515.c | 224 +- drivers/gpu/drm/radeon/rv6xx_dpm.c | 2059 ++++++++ drivers/gpu/drm/radeon/rv6xx_dpm.h | 95 + drivers/gpu/drm/radeon/rv6xxd.h | 246 + drivers/gpu/drm/radeon/rv730_dpm.c | 508 ++ drivers/gpu/drm/radeon/rv730d.h | 165 + drivers/gpu/drm/radeon/rv740_dpm.c | 416 ++ drivers/gpu/drm/radeon/rv740d.h | 117 + drivers/gpu/drm/radeon/rv770_dpm.c | 2446 +++++++++ drivers/gpu/drm/radeon/rv770_dpm.h | 287 + drivers/gpu/drm/radeon/rv770_smc.c | 621 +++ drivers/gpu/drm/radeon/rv770_smc.h | 209 + drivers/gpu/drm/radeon/rv770d.h | 279 +- drivers/gpu/drm/radeon/si.c | 1337 +++++- drivers/gpu/drm/radeon/si_dpm.c | 6369 ++++++++++++++++++++++ drivers/gpu/drm/radeon/si_dpm.h | 227 + drivers/gpu/drm/radeon/si_smc.c | 284 + drivers/gpu/drm/radeon/sid.h | 599 ++- drivers/gpu/drm/radeon/sislands_smc.h | 397 ++ drivers/gpu/drm/radeon/sumo_dpm.c | 1801 +++++++ drivers/gpu/drm/radeon/sumo_dpm.h | 220 + drivers/gpu/drm/radeon/sumo_smc.c | 222 + drivers/gpu/drm/radeon/sumod.h | 372 ++ drivers/gpu/drm/radeon/trinity_dpm.c | 1887 +++++++ drivers/gpu/drm/radeon/trinity_dpm.h | 131 + drivers/gpu/drm/radeon/trinity_smc.c | 115 + drivers/gpu/drm/radeon/trinityd.h | 228 + include/drm/drm_fixed.h | 96 + include/drm/drm_pciids.h | 24 + 88 files changed, 52708 insertions(+), 561 deletions(-) create mode 100644 drivers/gpu/drm/radeon/btc_dpm.c create mode 100644 drivers/gpu/drm/radeon/btc_dpm.h create mode 100644 drivers/gpu/drm/radeon/btcd.h create mode 100644 drivers/gpu/drm/radeon/cik.c create mode 100644 drivers/gpu/drm/radeon/cik_blit_shaders.c create mode 100644 drivers/gpu/drm/radeon/cik_blit_shaders.h create mode 100644 drivers/gpu/drm/radeon/cik_reg.h create mode 100644 drivers/gpu/drm/radeon/cikd.h create mode 100644 drivers/gpu/drm/radeon/clearstate_cayman.h create mode 100644 drivers/gpu/drm/radeon/clearstate_defs.h create mode 100644 drivers/gpu/drm/radeon/clearstate_evergreen.h create mode 100644 drivers/gpu/drm/radeon/clearstate_si.h create mode 100644 drivers/gpu/drm/radeon/cypress_dpm.c create mode 100644 drivers/gpu/drm/radeon/cypress_dpm.h create mode 100644 drivers/gpu/drm/radeon/evergreen_smc.h create mode 100644 drivers/gpu/drm/radeon/ni_dpm.c create mode 100644 drivers/gpu/drm/radeon/ni_dpm.h create mode 100644 drivers/gpu/drm/radeon/nislands_smc.h create mode 100644 drivers/gpu/drm/radeon/ppsmc.h create mode 100644 drivers/gpu/drm/radeon/r600_dpm.c create mode 100644 drivers/gpu/drm/radeon/r600_dpm.h create mode 100644 drivers/gpu/drm/radeon/radeon_ucode.h create mode 100644 drivers/gpu/drm/radeon/rs780_dpm.c create mode 100644 drivers/gpu/drm/radeon/rs780_dpm.h create mode 100644 drivers/gpu/drm/radeon/rs780d.h create mode 100644 drivers/gpu/drm/radeon/rv6xx_dpm.c create mode 100644 drivers/gpu/drm/radeon/rv6xx_dpm.h create mode 100644 drivers/gpu/drm/radeon/rv6xxd.h create mode 100644 drivers/gpu/drm/radeon/rv730_dpm.c create mode 100644 drivers/gpu/drm/radeon/rv730d.h create mode 100644 drivers/gpu/drm/radeon/rv740_dpm.c create mode 100644 drivers/gpu/drm/radeon/rv740d.h create mode 100644 drivers/gpu/drm/radeon/rv770_dpm.c create mode 100644 drivers/gpu/drm/radeon/rv770_dpm.h create mode 100644 drivers/gpu/drm/radeon/rv770_smc.c create mode 100644 drivers/gpu/drm/radeon/rv770_smc.h create mode 100644 drivers/gpu/drm/radeon/si_dpm.c create mode 100644 drivers/gpu/drm/radeon/si_dpm.h create mode 100644 drivers/gpu/drm/radeon/si_smc.c create mode 100644 drivers/gpu/drm/radeon/sislands_smc.h create mode 100644 drivers/gpu/drm/radeon/sumo_dpm.c create mode 100644 drivers/gpu/drm/radeon/sumo_dpm.h create mode 100644 drivers/gpu/drm/radeon/sumo_smc.c create mode 100644 drivers/gpu/drm/radeon/sumod.h create mode 100644 drivers/gpu/drm/radeon/trinity_dpm.c create mode 100644 drivers/gpu/drm/radeon/trinity_dpm.h create mode 100644 drivers/gpu/drm/radeon/trinity_smc.c create mode 100644 drivers/gpu/drm/radeon/trinityd.h
-- 1.7.7.5
dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
From: Alex Deucher alexander.deucher@amd.com
- remove adding 2 to checksum, this is incorrect.
This was incorrectly introduced in: 92db7f6c860b8190571a9dc1fcbc16d003422fe8 http://lists.freedesktop.org/archives/dri-devel/2011-December/017717.html However, the off by 2 was due to adding the version twice.
From the examples in the URL above:
[Rafał Miłecki][RV620] fglrx: 0x7454: 00 A8 5E 79 R600_HDMI_VIDEOINFOFRAME_0 0x7458: 00 28 00 10 R600_HDMI_VIDEOINFOFRAME_1 0x745C: 00 48 00 28 R600_HDMI_VIDEOINFOFRAME_2 0x7460: 02 00 00 48 R600_HDMI_VIDEOINFOFRAME_3 =================== (0x82 + 0x2 + 0xD) + 0x1F8 = 0x289 -0x289 = 0x77
However, the payload sum is not 0x1f8, it's 0x1f6. 00 + A8 + 5E + 00 + 00 + 28 + 00 + 10 + 00 + 48 + 00 + 28 + 00 + 48 = 0x1f6
Bits 25:24 of HDMI_VIDEOINFOFRAME_3 are the packet version, not part of the payload. So the total would be: (0x82 + 0x2 + 0xD) + 0x1f6 = 0x287 -0x287 = 0x79
- properly emit the AVI infoframe version. This was not being emitted previous which is probably what caused the issue above.
This should fix blank screen when HDMI audio is enabled on certain monitors.
Signed-off-by: Alex Deucher alexander.deucher@amd.com Cc: stable@vger.kernel.org Cc: Rafał Miłecki zajec5@gmail.com --- drivers/gpu/drm/radeon/evergreen_hdmi.c | 11 ++--------- drivers/gpu/drm/radeon/r600_hdmi.c | 11 ++--------- 2 files changed, 4 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/radeon/evergreen_hdmi.c b/drivers/gpu/drm/radeon/evergreen_hdmi.c index ed7c8a7..b9c6f76 100644 --- a/drivers/gpu/drm/radeon/evergreen_hdmi.c +++ b/drivers/gpu/drm/radeon/evergreen_hdmi.c @@ -128,14 +128,7 @@ static void evergreen_hdmi_update_avi_infoframe(struct drm_encoder *encoder, struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; uint32_t offset = dig->afmt->offset; uint8_t *frame = buffer + 3; - - /* Our header values (type, version, length) should be alright, Intel - * is using the same. Checksum function also seems to be OK, it works - * fine for audio infoframe. However calculated value is always lower - * by 2 in comparison to fglrx. It breaks displaying anything in case - * of TVs that strictly check the checksum. Hack it manually here to - * workaround this issue. */ - frame[0x0] += 2; + uint8_t *header = buffer;
WREG32(AFMT_AVI_INFO0 + offset, frame[0x0] | (frame[0x1] << 8) | (frame[0x2] << 16) | (frame[0x3] << 24)); @@ -144,7 +137,7 @@ static void evergreen_hdmi_update_avi_infoframe(struct drm_encoder *encoder, WREG32(AFMT_AVI_INFO2 + offset, frame[0x8] | (frame[0x9] << 8) | (frame[0xA] << 16) | (frame[0xB] << 24)); WREG32(AFMT_AVI_INFO3 + offset, - frame[0xC] | (frame[0xD] << 8)); + frame[0xC] | (frame[0xD] << 8) | (header[1] << 24)); }
static void evergreen_audio_set_dto(struct drm_encoder *encoder, u32 clock) diff --git a/drivers/gpu/drm/radeon/r600_hdmi.c b/drivers/gpu/drm/radeon/r600_hdmi.c index 456750a..e73b2a7 100644 --- a/drivers/gpu/drm/radeon/r600_hdmi.c +++ b/drivers/gpu/drm/radeon/r600_hdmi.c @@ -133,14 +133,7 @@ static void r600_hdmi_update_avi_infoframe(struct drm_encoder *encoder, struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; uint32_t offset = dig->afmt->offset; uint8_t *frame = buffer + 3; - - /* Our header values (type, version, length) should be alright, Intel - * is using the same. Checksum function also seems to be OK, it works - * fine for audio infoframe. However calculated value is always lower - * by 2 in comparison to fglrx. It breaks displaying anything in case - * of TVs that strictly check the checksum. Hack it manually here to - * workaround this issue. */ - frame[0x0] += 2; + uint8_t *header = buffer;
WREG32(HDMI0_AVI_INFO0 + offset, frame[0x0] | (frame[0x1] << 8) | (frame[0x2] << 16) | (frame[0x3] << 24)); @@ -149,7 +142,7 @@ static void r600_hdmi_update_avi_infoframe(struct drm_encoder *encoder, WREG32(HDMI0_AVI_INFO2 + offset, frame[0x8] | (frame[0x9] << 8) | (frame[0xA] << 16) | (frame[0xB] << 24)); WREG32(HDMI0_AVI_INFO3 + offset, - frame[0xC] | (frame[0xD] << 8)); + frame[0xC] | (frame[0xD] << 8) | (header[1] << 24)); }
/*
From: Alex Deucher alexander.deucher@amd.com
Mac laptops with multiple GPUs apparently use the gmux driver for backlight control. Don't register a radeon backlight interface. We may need to add other pci ids for other hybrid mac laptops.
Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=65377
Signed-off-by: Alex Deucher alexander.deucher@amd.com Cc: stable@vger.kernel.org --- drivers/gpu/drm/radeon/atombios_encoders.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c index 8406c82..4120d35 100644 --- a/drivers/gpu/drm/radeon/atombios_encoders.c +++ b/drivers/gpu/drm/radeon/atombios_encoders.c @@ -186,6 +186,13 @@ void radeon_atom_backlight_init(struct radeon_encoder *radeon_encoder, u8 backlight_level; char bl_name[16];
+ /* Mac laptops with multiple GPUs use the gmux driver for backlight + * so don't register a backlight device + */ + if ((rdev->pdev->subsystem_vendor == PCI_VENDOR_ID_APPLE) && + (rdev->pdev->device == 0x6741)) + return; + if (!radeon_encoder->enc_priv) return;
From: Alex Deucher alexander.deucher@amd.com
New asics support non-privileged IBs. This allows us to skip IB checking in the driver since the hardware will check the command buffers for us. When using non-privileged IBs, if the CP encounters an illegal register in the command stream, it will halt and generate an interrupt. The CP needs to be reset to continue. For now just do a full GPU reset when this happens.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/radeon.h | 1 + drivers/gpu/drm/radeon/radeon_irq_kms.c | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 142ce6c..f5fccbb 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1691,6 +1691,7 @@ struct radeon_device { struct si_rlc rlc; struct work_struct hotplug_work; struct work_struct audio_work; + struct work_struct reset_work; int num_crtc; /* number of crtcs */ struct mutex dc_hw_i2c_mutex; /* display controller hw i2c mutex */ bool audio_enabled; diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c index 5a99d43..dbffeca 100644 --- a/drivers/gpu/drm/radeon/radeon_irq_kms.c +++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c @@ -82,6 +82,23 @@ static void radeon_hotplug_work_func(struct work_struct *work) }
/** + * radeon_irq_reset_work_func - execute gpu reset + * + * @work: work struct + * + * Execute scheduled gpu reset (cayman+). + * This function is called when the irq handler + * thinks we need a gpu reset. + */ +static void radeon_irq_reset_work_func(struct work_struct *work) +{ + struct radeon_device *rdev = container_of(work, struct radeon_device, + reset_work); + + radeon_gpu_reset(rdev); +} + +/** * radeon_driver_irq_preinstall_kms - drm irq preinstall callback * * @dev: drm dev pointer @@ -243,6 +260,7 @@ int radeon_irq_kms_init(struct radeon_device *rdev)
INIT_WORK(&rdev->hotplug_work, radeon_hotplug_work_func); INIT_WORK(&rdev->audio_work, r600_audio_update_hdmi); + INIT_WORK(&rdev->reset_work, radeon_irq_reset_work_func);
spin_lock_init(&rdev->irq.lock); r = drm_vblank_init(rdev->ddev, rdev->num_crtc);
From: Alex Deucher alexander.deucher@amd.com
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/radeon_device.c | 3 +++ drivers/gpu/drm/radeon/radeon_family.h | 3 +++ 2 files changed, 6 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index b0dc0b6..c24056b 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -95,6 +95,9 @@ static const char radeon_family_name[][16] = { "VERDE", "OLAND", "HAINAN", + "BONAIRE", + "KAVERI", + "KABINI", "LAST", };
diff --git a/drivers/gpu/drm/radeon/radeon_family.h b/drivers/gpu/drm/radeon/radeon_family.h index 36e9803..3c82890 100644 --- a/drivers/gpu/drm/radeon/radeon_family.h +++ b/drivers/gpu/drm/radeon/radeon_family.h @@ -93,6 +93,9 @@ enum radeon_family { CHIP_VERDE, CHIP_OLAND, CHIP_HAINAN, + CHIP_BONAIRE, + CHIP_KAVERI, + CHIP_KABINI, CHIP_LAST, };
From: Alex Deucher alexander.deucher@amd.com
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/radeon.h | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index f5fccbb..b50a786 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1841,6 +1841,7 @@ void r100_pll_errata_after_index(struct radeon_device *rdev); (rdev->flags & RADEON_IS_IGP)) #define ASIC_IS_DCE64(rdev) ((rdev->family == CHIP_OLAND)) #define ASIC_IS_NODCE(rdev) ((rdev->family == CHIP_HAINAN)) +#define ASIC_IS_DCE8(rdev) ((rdev->family >= CHIP_BONAIRE))
/* * BIOS helpers.
From: Alex Deucher alexander.deucher@amd.com
register BAR is now at PCI BAR 5.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/radeon_device.c | 9 +++++++-- 1 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index c24056b..4e97ff7 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -1148,8 +1148,13 @@ int radeon_device_init(struct radeon_device *rdev, /* Registers mapping */ /* TODO: block userspace mapping of io register */ spin_lock_init(&rdev->mmio_idx_lock); - rdev->rmmio_base = pci_resource_start(rdev->pdev, 2); - rdev->rmmio_size = pci_resource_len(rdev->pdev, 2); + if (rdev->family >= CHIP_BONAIRE) { + rdev->rmmio_base = pci_resource_start(rdev->pdev, 5); + rdev->rmmio_size = pci_resource_len(rdev->pdev, 5); + } else { + rdev->rmmio_base = pci_resource_start(rdev->pdev, 2); + rdev->rmmio_size = pci_resource_len(rdev->pdev, 2); + } rdev->rmmio = ioremap(rdev->rmmio_base, rdev->rmmio_size); if (rdev->rmmio == NULL) { return -ENOMEM;
From: Alex Deucher alexander.deucher@amd.com
v2: tiling fixes v3: more tiling fixes v4: more tiling fixes v5: additional register init v6: rebase v7: fix gb_addr_config for KV/KB v8: drop wip KV bits for now, add missing config reg v9: fix cu count on Bonaire
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/Makefile | 2 +- drivers/gpu/drm/radeon/cik.c | 1192 +++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/cikd.h | 253 +++++++++ drivers/gpu/drm/radeon/radeon.h | 30 + 4 files changed, 1476 insertions(+), 1 deletions(-) create mode 100644 drivers/gpu/drm/radeon/cik.c create mode 100644 drivers/gpu/drm/radeon/cikd.h
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index 86c5e36..88d0601 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile @@ -76,7 +76,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \ evergreen.o evergreen_cs.o evergreen_blit_shaders.o evergreen_blit_kms.o \ evergreen_hdmi.o radeon_trace_points.o ni.o cayman_blit_shaders.o \ atombios_encoders.o radeon_semaphore.o radeon_sa.o atombios_i2c.o si.o \ - si_blit_shaders.o radeon_prime.o radeon_uvd.o + si_blit_shaders.o radeon_prime.o radeon_uvd.o cik.o
radeon-$(CONFIG_COMPAT) += radeon_ioc32.o radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c new file mode 100644 index 0000000..28f68dc --- /dev/null +++ b/drivers/gpu/drm/radeon/cik.c @@ -0,0 +1,1192 @@ +/* + * Copyright 2012 Advanced Micro Devices, 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: Alex Deucher + */ +#include <linux/firmware.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/module.h> +#include "drmP.h" +#include "radeon.h" +#include "cikd.h" +#include "atom.h" + +/* + * Core functions + */ +/** + * cik_tiling_mode_table_init - init the hw tiling table + * + * @rdev: radeon_device pointer + * + * Starting with SI, the tiling setup is done globally in a + * set of 32 tiling modes. Rather than selecting each set of + * parameters per surface as on older asics, we just select + * which index in the tiling table we want to use, and the + * surface uses those parameters (CIK). + */ +static void cik_tiling_mode_table_init(struct radeon_device *rdev) +{ + const u32 num_tile_mode_states = 32; + const u32 num_secondary_tile_mode_states = 16; + u32 reg_offset, gb_tile_moden, split_equal_to_row_size; + u32 num_pipe_configs; + u32 num_rbs = rdev->config.cik.max_backends_per_se * + rdev->config.cik.max_shader_engines; + + switch (rdev->config.cik.mem_row_size_in_kb) { + case 1: + split_equal_to_row_size = ADDR_SURF_TILE_SPLIT_1KB; + break; + case 2: + default: + split_equal_to_row_size = ADDR_SURF_TILE_SPLIT_2KB; + break; + case 4: + split_equal_to_row_size = ADDR_SURF_TILE_SPLIT_4KB; + break; + } + + num_pipe_configs = rdev->config.cik.max_tile_pipes; + if (num_pipe_configs > 8) + num_pipe_configs = 8; /* ??? */ + + if (num_pipe_configs == 8) { + for (reg_offset = 0; reg_offset < num_tile_mode_states; reg_offset++) { + switch (reg_offset) { + case 0: + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_16x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B)); + break; + case 1: + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_16x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_128B)); + break; + case 2: + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_16x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B)); + break; + case 3: + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_16x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_512B)); + break; + case 4: + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_16x16) | + TILE_SPLIT(split_equal_to_row_size)); + break; + case 5: + gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING)); + break; + case 6: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_16x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B)); + break; + case 7: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_16x16) | + TILE_SPLIT(split_equal_to_row_size)); + break; + case 8: + gb_tile_moden = (ARRAY_MODE(ARRAY_LINEAR_ALIGNED) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_16x16)); + break; + case 9: + gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DISPLAY_MICRO_TILING)); + break; + case 10: + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DISPLAY_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_16x16) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + case 11: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DISPLAY_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + case 12: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DISPLAY_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_16x16) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + case 13: + gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_THIN_MICRO_TILING)); + break; + case 14: + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_THIN_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_16x16) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + case 16: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_THIN_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + case 17: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_THIN_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_16x16) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + case 27: + gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_ROTATED_MICRO_TILING)); + break; + case 28: + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_ROTATED_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_16x16) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + case 29: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_ROTATED_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + case 30: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_ROTATED_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P8_32x32_16x16) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + default: + gb_tile_moden = 0; + break; + } + WREG32(GB_TILE_MODE0 + (reg_offset * 4), gb_tile_moden); + } + for (reg_offset = 0; reg_offset < num_secondary_tile_mode_states; reg_offset++) { + switch (reg_offset) { + case 0: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) | + NUM_BANKS(ADDR_SURF_16_BANK)); + break; + case 1: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2) | + NUM_BANKS(ADDR_SURF_16_BANK)); + break; + case 2: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2) | + NUM_BANKS(ADDR_SURF_16_BANK)); + break; + case 3: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2) | + NUM_BANKS(ADDR_SURF_16_BANK)); + break; + case 4: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1) | + NUM_BANKS(ADDR_SURF_8_BANK)); + break; + case 5: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1) | + NUM_BANKS(ADDR_SURF_4_BANK)); + break; + case 6: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1) | + NUM_BANKS(ADDR_SURF_2_BANK)); + break; + case 8: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_8) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) | + NUM_BANKS(ADDR_SURF_16_BANK)); + break; + case 9: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) | + NUM_BANKS(ADDR_SURF_16_BANK)); + break; + case 10: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2) | + NUM_BANKS(ADDR_SURF_16_BANK)); + break; + case 11: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2) | + NUM_BANKS(ADDR_SURF_16_BANK)); + break; + case 12: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1) | + NUM_BANKS(ADDR_SURF_8_BANK)); + break; + case 13: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1) | + NUM_BANKS(ADDR_SURF_4_BANK)); + break; + case 14: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1) | + NUM_BANKS(ADDR_SURF_2_BANK)); + break; + default: + gb_tile_moden = 0; + break; + } + WREG32(GB_MACROTILE_MODE0 + (reg_offset * 4), gb_tile_moden); + } + } else if (num_pipe_configs == 4) { + if (num_rbs == 4) { + for (reg_offset = 0; reg_offset < num_tile_mode_states; reg_offset++) { + switch (reg_offset) { + case 0: + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_16x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B)); + break; + case 1: + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_16x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_128B)); + break; + case 2: + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_16x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B)); + break; + case 3: + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_16x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_512B)); + break; + case 4: + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_16x16) | + TILE_SPLIT(split_equal_to_row_size)); + break; + case 5: + gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING)); + break; + case 6: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_16x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B)); + break; + case 7: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_16x16) | + TILE_SPLIT(split_equal_to_row_size)); + break; + case 8: + gb_tile_moden = (ARRAY_MODE(ARRAY_LINEAR_ALIGNED) | + PIPE_CONFIG(ADDR_SURF_P4_16x16)); + break; + case 9: + gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DISPLAY_MICRO_TILING)); + break; + case 10: + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DISPLAY_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_16x16) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + case 11: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DISPLAY_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + case 12: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DISPLAY_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_16x16) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + case 13: + gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_THIN_MICRO_TILING)); + break; + case 14: + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_THIN_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_16x16) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + case 16: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_THIN_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + case 17: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_THIN_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_16x16) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + case 27: + gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_ROTATED_MICRO_TILING)); + break; + case 28: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_ROTATED_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_16x16) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + case 29: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_ROTATED_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + case 30: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_ROTATED_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_16x16) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + default: + gb_tile_moden = 0; + break; + } + WREG32(GB_TILE_MODE0 + (reg_offset * 4), gb_tile_moden); + } + } else if (num_rbs < 4) { + for (reg_offset = 0; reg_offset < num_tile_mode_states; reg_offset++) { + switch (reg_offset) { + case 0: + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B)); + break; + case 1: + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_128B)); + break; + case 2: + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B)); + break; + case 3: + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_512B)); + break; + case 4: + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + TILE_SPLIT(split_equal_to_row_size)); + break; + case 5: + gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING)); + break; + case 6: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B)); + break; + case 7: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + TILE_SPLIT(split_equal_to_row_size)); + break; + case 8: + gb_tile_moden = (ARRAY_MODE(ARRAY_LINEAR_ALIGNED) | + PIPE_CONFIG(ADDR_SURF_P4_8x16)); + break; + case 9: + gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DISPLAY_MICRO_TILING)); + break; + case 10: + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DISPLAY_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + case 11: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DISPLAY_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + case 12: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DISPLAY_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + case 13: + gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_THIN_MICRO_TILING)); + break; + case 14: + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_THIN_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + case 16: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_THIN_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + case 17: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_THIN_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + case 27: + gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_ROTATED_MICRO_TILING)); + break; + case 28: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_ROTATED_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + case 29: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_ROTATED_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + case 30: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_ROTATED_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P4_8x16) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + default: + gb_tile_moden = 0; + break; + } + WREG32(GB_TILE_MODE0 + (reg_offset * 4), gb_tile_moden); + } + } + for (reg_offset = 0; reg_offset < num_secondary_tile_mode_states; reg_offset++) { + switch (reg_offset) { + case 0: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) | + NUM_BANKS(ADDR_SURF_16_BANK)); + break; + case 1: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) | + NUM_BANKS(ADDR_SURF_16_BANK)); + break; + case 2: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2) | + NUM_BANKS(ADDR_SURF_16_BANK)); + break; + case 3: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2) | + NUM_BANKS(ADDR_SURF_16_BANK)); + break; + case 4: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2) | + NUM_BANKS(ADDR_SURF_16_BANK)); + break; + case 5: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2) | + NUM_BANKS(ADDR_SURF_8_BANK)); + break; + case 6: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1) | + NUM_BANKS(ADDR_SURF_4_BANK)); + break; + case 8: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_2) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_8) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) | + NUM_BANKS(ADDR_SURF_16_BANK)); + break; + case 9: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_2) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) | + NUM_BANKS(ADDR_SURF_16_BANK)); + break; + case 10: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) | + NUM_BANKS(ADDR_SURF_16_BANK)); + break; + case 11: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) | + NUM_BANKS(ADDR_SURF_16_BANK)); + break; + case 12: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2) | + NUM_BANKS(ADDR_SURF_16_BANK)); + break; + case 13: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2) | + NUM_BANKS(ADDR_SURF_8_BANK)); + break; + case 14: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1) | + NUM_BANKS(ADDR_SURF_4_BANK)); + break; + default: + gb_tile_moden = 0; + break; + } + WREG32(GB_MACROTILE_MODE0 + (reg_offset * 4), gb_tile_moden); + } + } else if (num_pipe_configs == 2) { + for (reg_offset = 0; reg_offset < num_tile_mode_states; reg_offset++) { + switch (reg_offset) { + case 0: + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P2) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B)); + break; + case 1: + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P2) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_128B)); + break; + case 2: + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P2) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B)); + break; + case 3: + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P2) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_512B)); + break; + case 4: + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P2) | + TILE_SPLIT(split_equal_to_row_size)); + break; + case 5: + gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING)); + break; + case 6: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P2) | + TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B)); + break; + case 7: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DEPTH_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P2) | + TILE_SPLIT(split_equal_to_row_size)); + break; + case 8: + gb_tile_moden = ARRAY_MODE(ARRAY_LINEAR_ALIGNED); + break; + case 9: + gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DISPLAY_MICRO_TILING)); + break; + case 10: + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DISPLAY_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P2) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + case 11: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DISPLAY_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P2) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + case 12: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_DISPLAY_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P2) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + case 13: + gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_THIN_MICRO_TILING)); + break; + case 14: + gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_THIN_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P2) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + case 16: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_THIN_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P2) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + case 17: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_THIN_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P2) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + case 27: + gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_ROTATED_MICRO_TILING)); + break; + case 28: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_ROTATED_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P2) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + case 29: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_ROTATED_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P2) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + case 30: + gb_tile_moden = (ARRAY_MODE(ARRAY_PRT_2D_TILED_THIN1) | + MICRO_TILE_MODE_NEW(ADDR_SURF_ROTATED_MICRO_TILING) | + PIPE_CONFIG(ADDR_SURF_P2) | + SAMPLE_SPLIT(ADDR_SURF_SAMPLE_SPLIT_2)); + break; + default: + gb_tile_moden = 0; + break; + } + WREG32(GB_TILE_MODE0 + (reg_offset * 4), gb_tile_moden); + } + for (reg_offset = 0; reg_offset < num_secondary_tile_mode_states; reg_offset++) { + switch (reg_offset) { + case 0: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_2) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) | + NUM_BANKS(ADDR_SURF_16_BANK)); + break; + case 1: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_2) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) | + NUM_BANKS(ADDR_SURF_16_BANK)); + break; + case 2: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) | + NUM_BANKS(ADDR_SURF_16_BANK)); + break; + case 3: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) | + NUM_BANKS(ADDR_SURF_16_BANK)); + break; + case 4: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) | + NUM_BANKS(ADDR_SURF_16_BANK)); + break; + case 5: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) | + NUM_BANKS(ADDR_SURF_16_BANK)); + break; + case 6: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2) | + NUM_BANKS(ADDR_SURF_8_BANK)); + break; + case 8: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_4) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_8) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) | + NUM_BANKS(ADDR_SURF_16_BANK)); + break; + case 9: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_4) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) | + NUM_BANKS(ADDR_SURF_16_BANK)); + break; + case 10: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_2) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) | + NUM_BANKS(ADDR_SURF_16_BANK)); + break; + case 11: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_2) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) | + NUM_BANKS(ADDR_SURF_16_BANK)); + break; + case 12: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) | + NUM_BANKS(ADDR_SURF_16_BANK)); + break; + case 13: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4) | + NUM_BANKS(ADDR_SURF_16_BANK)); + break; + case 14: + gb_tile_moden = (BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) | + BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) | + MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2) | + NUM_BANKS(ADDR_SURF_8_BANK)); + break; + default: + gb_tile_moden = 0; + break; + } + WREG32(GB_MACROTILE_MODE0 + (reg_offset * 4), gb_tile_moden); + } + } else + DRM_ERROR("unknown num pipe config: 0x%x\n", num_pipe_configs); +} + +/** + * cik_select_se_sh - select which SE, SH to address + * + * @rdev: radeon_device pointer + * @se_num: shader engine to address + * @sh_num: sh block to address + * + * Select which SE, SH combinations to address. Certain + * registers are instanced per SE or SH. 0xffffffff means + * broadcast to all SEs or SHs (CIK). + */ +static void cik_select_se_sh(struct radeon_device *rdev, + u32 se_num, u32 sh_num) +{ + u32 data = INSTANCE_BROADCAST_WRITES; + + if ((se_num == 0xffffffff) && (sh_num == 0xffffffff)) + data = SH_BROADCAST_WRITES | SE_BROADCAST_WRITES; + else if (se_num == 0xffffffff) + data |= SE_BROADCAST_WRITES | SH_INDEX(sh_num); + else if (sh_num == 0xffffffff) + data |= SH_BROADCAST_WRITES | SE_INDEX(se_num); + else + data |= SH_INDEX(sh_num) | SE_INDEX(se_num); + WREG32(GRBM_GFX_INDEX, data); +} + +/** + * cik_create_bitmask - create a bitmask + * + * @bit_width: length of the mask + * + * create a variable length bit mask (CIK). + * Returns the bitmask. + */ +static u32 cik_create_bitmask(u32 bit_width) +{ + u32 i, mask = 0; + + for (i = 0; i < bit_width; i++) { + mask <<= 1; + mask |= 1; + } + return mask; +} + +/** + * cik_select_se_sh - select which SE, SH to address + * + * @rdev: radeon_device pointer + * @max_rb_num: max RBs (render backends) for the asic + * @se_num: number of SEs (shader engines) for the asic + * @sh_per_se: number of SH blocks per SE for the asic + * + * Calculates the bitmask of disabled RBs (CIK). + * Returns the disabled RB bitmask. + */ +static u32 cik_get_rb_disabled(struct radeon_device *rdev, + u32 max_rb_num, u32 se_num, + u32 sh_per_se) +{ + u32 data, mask; + + data = RREG32(CC_RB_BACKEND_DISABLE); + if (data & 1) + data &= BACKEND_DISABLE_MASK; + else + data = 0; + data |= RREG32(GC_USER_RB_BACKEND_DISABLE); + + data >>= BACKEND_DISABLE_SHIFT; + + mask = cik_create_bitmask(max_rb_num / se_num / sh_per_se); + + return data & mask; +} + +/** + * cik_setup_rb - setup the RBs on the asic + * + * @rdev: radeon_device pointer + * @se_num: number of SEs (shader engines) for the asic + * @sh_per_se: number of SH blocks per SE for the asic + * @max_rb_num: max RBs (render backends) for the asic + * + * Configures per-SE/SH RB registers (CIK). + */ +static void cik_setup_rb(struct radeon_device *rdev, + u32 se_num, u32 sh_per_se, + u32 max_rb_num) +{ + int i, j; + u32 data, mask; + u32 disabled_rbs = 0; + u32 enabled_rbs = 0; + + for (i = 0; i < se_num; i++) { + for (j = 0; j < sh_per_se; j++) { + cik_select_se_sh(rdev, i, j); + data = cik_get_rb_disabled(rdev, max_rb_num, se_num, sh_per_se); + disabled_rbs |= data << ((i * sh_per_se + j) * CIK_RB_BITMAP_WIDTH_PER_SH); + } + } + cik_select_se_sh(rdev, 0xffffffff, 0xffffffff); + + mask = 1; + for (i = 0; i < max_rb_num; i++) { + if (!(disabled_rbs & mask)) + enabled_rbs |= mask; + mask <<= 1; + } + + for (i = 0; i < se_num; i++) { + cik_select_se_sh(rdev, i, 0xffffffff); + data = 0; + for (j = 0; j < sh_per_se; j++) { + switch (enabled_rbs & 3) { + case 1: + data |= (RASTER_CONFIG_RB_MAP_0 << (i * sh_per_se + j) * 2); + break; + case 2: + data |= (RASTER_CONFIG_RB_MAP_3 << (i * sh_per_se + j) * 2); + break; + case 3: + default: + data |= (RASTER_CONFIG_RB_MAP_2 << (i * sh_per_se + j) * 2); + break; + } + enabled_rbs >>= 2; + } + WREG32(PA_SC_RASTER_CONFIG, data); + } + cik_select_se_sh(rdev, 0xffffffff, 0xffffffff); +} + +/** + * cik_gpu_init - setup the 3D engine + * + * @rdev: radeon_device pointer + * + * Configures the 3D engine and tiling configuration + * registers so that the 3D engine is usable. + */ +static void cik_gpu_init(struct radeon_device *rdev) +{ + u32 gb_addr_config = RREG32(GB_ADDR_CONFIG); + u32 mc_shared_chmap, mc_arb_ramcfg; + u32 hdp_host_path_cntl; + u32 tmp; + int i, j; + + switch (rdev->family) { + case CHIP_BONAIRE: + rdev->config.cik.max_shader_engines = 2; + rdev->config.cik.max_tile_pipes = 4; + rdev->config.cik.max_cu_per_sh = 7; + rdev->config.cik.max_sh_per_se = 1; + rdev->config.cik.max_backends_per_se = 2; + rdev->config.cik.max_texture_channel_caches = 4; + rdev->config.cik.max_gprs = 256; + rdev->config.cik.max_gs_threads = 32; + rdev->config.cik.max_hw_contexts = 8; + + rdev->config.cik.sc_prim_fifo_size_frontend = 0x20; + rdev->config.cik.sc_prim_fifo_size_backend = 0x100; + rdev->config.cik.sc_hiz_tile_fifo_size = 0x30; + rdev->config.cik.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = BONAIRE_GB_ADDR_CONFIG_GOLDEN; + break; + case CHIP_KAVERI: + /* TODO */ + break; + case CHIP_KABINI: + default: + rdev->config.cik.max_shader_engines = 1; + rdev->config.cik.max_tile_pipes = 2; + rdev->config.cik.max_cu_per_sh = 2; + rdev->config.cik.max_sh_per_se = 1; + rdev->config.cik.max_backends_per_se = 1; + rdev->config.cik.max_texture_channel_caches = 2; + rdev->config.cik.max_gprs = 256; + rdev->config.cik.max_gs_threads = 16; + rdev->config.cik.max_hw_contexts = 8; + + rdev->config.cik.sc_prim_fifo_size_frontend = 0x20; + rdev->config.cik.sc_prim_fifo_size_backend = 0x100; + rdev->config.cik.sc_hiz_tile_fifo_size = 0x30; + rdev->config.cik.sc_earlyz_tile_fifo_size = 0x130; + gb_addr_config = BONAIRE_GB_ADDR_CONFIG_GOLDEN; + break; + } + + /* Initialize HDP */ + for (i = 0, j = 0; i < 32; i++, j += 0x18) { + WREG32((0x2c14 + j), 0x00000000); + WREG32((0x2c18 + j), 0x00000000); + WREG32((0x2c1c + j), 0x00000000); + WREG32((0x2c20 + j), 0x00000000); + WREG32((0x2c24 + j), 0x00000000); + } + + WREG32(GRBM_CNTL, GRBM_READ_TIMEOUT(0xff)); + + WREG32(BIF_FB_EN, FB_READ_EN | FB_WRITE_EN); + + mc_shared_chmap = RREG32(MC_SHARED_CHMAP); + mc_arb_ramcfg = RREG32(MC_ARB_RAMCFG); + + rdev->config.cik.num_tile_pipes = rdev->config.cik.max_tile_pipes; + rdev->config.cik.mem_max_burst_length_bytes = 256; + tmp = (mc_arb_ramcfg & NOOFCOLS_MASK) >> NOOFCOLS_SHIFT; + rdev->config.cik.mem_row_size_in_kb = (4 * (1 << (8 + tmp))) / 1024; + if (rdev->config.cik.mem_row_size_in_kb > 4) + rdev->config.cik.mem_row_size_in_kb = 4; + /* XXX use MC settings? */ + rdev->config.cik.shader_engine_tile_size = 32; + rdev->config.cik.num_gpus = 1; + rdev->config.cik.multi_gpu_tile_size = 64; + + /* fix up row size */ + gb_addr_config &= ~ROW_SIZE_MASK; + switch (rdev->config.cik.mem_row_size_in_kb) { + case 1: + default: + gb_addr_config |= ROW_SIZE(0); + break; + case 2: + gb_addr_config |= ROW_SIZE(1); + break; + case 4: + gb_addr_config |= ROW_SIZE(2); + break; + } + + /* setup tiling info dword. gb_addr_config is not adequate since it does + * not have bank info, so create a custom tiling dword. + * bits 3:0 num_pipes + * bits 7:4 num_banks + * bits 11:8 group_size + * bits 15:12 row_size + */ + rdev->config.cik.tile_config = 0; + switch (rdev->config.cik.num_tile_pipes) { + case 1: + rdev->config.cik.tile_config |= (0 << 0); + break; + case 2: + rdev->config.cik.tile_config |= (1 << 0); + break; + case 4: + rdev->config.cik.tile_config |= (2 << 0); + break; + case 8: + default: + /* XXX what about 12? */ + rdev->config.cik.tile_config |= (3 << 0); + break; + } + if ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) + rdev->config.cik.tile_config |= 1 << 4; + else + rdev->config.cik.tile_config |= 0 << 4; + rdev->config.cik.tile_config |= + ((gb_addr_config & PIPE_INTERLEAVE_SIZE_MASK) >> PIPE_INTERLEAVE_SIZE_SHIFT) << 8; + rdev->config.cik.tile_config |= + ((gb_addr_config & ROW_SIZE_MASK) >> ROW_SIZE_SHIFT) << 12; + + WREG32(GB_ADDR_CONFIG, gb_addr_config); + WREG32(HDP_ADDR_CONFIG, gb_addr_config); + WREG32(DMIF_ADDR_CALC, gb_addr_config); + + cik_tiling_mode_table_init(rdev); + + cik_setup_rb(rdev, rdev->config.cik.max_shader_engines, + rdev->config.cik.max_sh_per_se, + rdev->config.cik.max_backends_per_se); + + /* set HW defaults for 3D engine */ + WREG32(CP_MEQ_THRESHOLDS, MEQ1_START(0x30) | MEQ2_START(0x60)); + + WREG32(SX_DEBUG_1, 0x20); + + WREG32(TA_CNTL_AUX, 0x00010000); + + tmp = RREG32(SPI_CONFIG_CNTL); + tmp |= 0x03000000; + WREG32(SPI_CONFIG_CNTL, tmp); + + WREG32(SQ_CONFIG, 1); + + WREG32(DB_DEBUG, 0); + + tmp = RREG32(DB_DEBUG2) & ~0xf00fffff; + tmp |= 0x00000400; + WREG32(DB_DEBUG2, tmp); + + tmp = RREG32(DB_DEBUG3) & ~0x0002021c; + tmp |= 0x00020200; + WREG32(DB_DEBUG3, tmp); + + tmp = RREG32(CB_HW_CONTROL) & ~0x00010000; + tmp |= 0x00018208; + WREG32(CB_HW_CONTROL, tmp); + + WREG32(SPI_CONFIG_CNTL_1, VTX_DONE_DELAY(4)); + + WREG32(PA_SC_FIFO_SIZE, (SC_FRONTEND_PRIM_FIFO_SIZE(rdev->config.cik.sc_prim_fifo_size_frontend) | + SC_BACKEND_PRIM_FIFO_SIZE(rdev->config.cik.sc_prim_fifo_size_backend) | + SC_HIZ_TILE_FIFO_SIZE(rdev->config.cik.sc_hiz_tile_fifo_size) | + SC_EARLYZ_TILE_FIFO_SIZE(rdev->config.cik.sc_earlyz_tile_fifo_size))); + + WREG32(VGT_NUM_INSTANCES, 1); + + WREG32(CP_PERFMON_CNTL, 0); + + WREG32(SQ_CONFIG, 0); + + WREG32(PA_SC_FORCE_EOV_MAX_CNTS, (FORCE_EOV_MAX_CLK_CNT(4095) | + FORCE_EOV_MAX_REZ_CNT(255))); + + WREG32(VGT_CACHE_INVALIDATION, CACHE_INVALIDATION(VC_AND_TC) | + AUTO_INVLD_EN(ES_AND_GS_AUTO)); + + WREG32(VGT_GS_VERTEX_REUSE, 16); + WREG32(PA_SC_LINE_STIPPLE_STATE, 0); + + tmp = RREG32(HDP_MISC_CNTL); + tmp |= HDP_FLUSH_INVALIDATE_CACHE; + WREG32(HDP_MISC_CNTL, tmp); + + hdp_host_path_cntl = RREG32(HDP_HOST_PATH_CNTL); + WREG32(HDP_HOST_PATH_CNTL, hdp_host_path_cntl); + + WREG32(PA_CL_ENHANCE, CLIP_VTX_REORDER_ENA | NUM_CLIP_SEQ(3)); + WREG32(PA_SC_ENHANCE, ENABLE_PA_SC_OUT_OF_ORDER); + + udelay(50); +} + diff --git a/drivers/gpu/drm/radeon/cikd.h b/drivers/gpu/drm/radeon/cikd.h new file mode 100644 index 0000000..e51fb41 --- /dev/null +++ b/drivers/gpu/drm/radeon/cikd.h @@ -0,0 +1,253 @@ +/* + * Copyright 2012 Advanced Micro Devices, 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: Alex Deucher + */ +#ifndef CIK_H +#define CIK_H + +#define BONAIRE_GB_ADDR_CONFIG_GOLDEN 0x12010001 + +#define CIK_RB_BITMAP_WIDTH_PER_SH 2 + +#define DMIF_ADDR_CALC 0xC00 + +#define MC_SHARED_CHMAP 0x2004 +#define NOOFCHAN_SHIFT 12 +#define NOOFCHAN_MASK 0x0000f000 +#define MC_SHARED_CHREMAP 0x2008 + +#define MC_ARB_RAMCFG 0x2760 +#define NOOFBANK_SHIFT 0 +#define NOOFBANK_MASK 0x00000003 +#define NOOFRANK_SHIFT 2 +#define NOOFRANK_MASK 0x00000004 +#define NOOFROWS_SHIFT 3 +#define NOOFROWS_MASK 0x00000038 +#define NOOFCOLS_SHIFT 6 +#define NOOFCOLS_MASK 0x000000C0 +#define CHANSIZE_SHIFT 8 +#define CHANSIZE_MASK 0x00000100 +#define NOOFGROUPS_SHIFT 12 +#define NOOFGROUPS_MASK 0x00001000 + +#define HDP_HOST_PATH_CNTL 0x2C00 +#define HDP_NONSURFACE_BASE 0x2C04 +#define HDP_NONSURFACE_INFO 0x2C08 +#define HDP_NONSURFACE_SIZE 0x2C0C + +#define HDP_ADDR_CONFIG 0x2F48 +#define HDP_MISC_CNTL 0x2F4C +#define HDP_FLUSH_INVALIDATE_CACHE (1 << 0) + +#define BIF_FB_EN 0x5490 +#define FB_READ_EN (1 << 0) +#define FB_WRITE_EN (1 << 1) + +#define GRBM_CNTL 0x8000 +#define GRBM_READ_TIMEOUT(x) ((x) << 0) + +#define CP_MEQ_THRESHOLDS 0x8764 +#define MEQ1_START(x) ((x) << 0) +#define MEQ2_START(x) ((x) << 8) + +#define VGT_VTX_VECT_EJECT_REG 0x88B0 + +#define VGT_CACHE_INVALIDATION 0x88C4 +#define CACHE_INVALIDATION(x) ((x) << 0) +#define VC_ONLY 0 +#define TC_ONLY 1 +#define VC_AND_TC 2 +#define AUTO_INVLD_EN(x) ((x) << 6) +#define NO_AUTO 0 +#define ES_AUTO 1 +#define GS_AUTO 2 +#define ES_AND_GS_AUTO 3 + +#define VGT_GS_VERTEX_REUSE 0x88D4 + +#define CC_GC_SHADER_ARRAY_CONFIG 0x89bc +#define INACTIVE_CUS_MASK 0xFFFF0000 +#define INACTIVE_CUS_SHIFT 16 +#define GC_USER_SHADER_ARRAY_CONFIG 0x89c0 + +#define PA_CL_ENHANCE 0x8A14 +#define CLIP_VTX_REORDER_ENA (1 << 0) +#define NUM_CLIP_SEQ(x) ((x) << 1) + +#define PA_SC_FORCE_EOV_MAX_CNTS 0x8B24 +#define FORCE_EOV_MAX_CLK_CNT(x) ((x) << 0) +#define FORCE_EOV_MAX_REZ_CNT(x) ((x) << 16) + +#define PA_SC_FIFO_SIZE 0x8BCC +#define SC_FRONTEND_PRIM_FIFO_SIZE(x) ((x) << 0) +#define SC_BACKEND_PRIM_FIFO_SIZE(x) ((x) << 6) +#define SC_HIZ_TILE_FIFO_SIZE(x) ((x) << 15) +#define SC_EARLYZ_TILE_FIFO_SIZE(x) ((x) << 23) + +#define PA_SC_ENHANCE 0x8BF0 +#define ENABLE_PA_SC_OUT_OF_ORDER (1 << 0) +#define DISABLE_PA_SC_GUIDANCE (1 << 13) + +#define SQ_CONFIG 0x8C00 + +#define SX_DEBUG_1 0x9060 + +#define SPI_CONFIG_CNTL 0x9100 + +#define SPI_CONFIG_CNTL_1 0x913C +#define VTX_DONE_DELAY(x) ((x) << 0) +#define INTERP_ONE_PRIM_PER_ROW (1 << 4) + +#define TA_CNTL_AUX 0x9508 + +#define DB_DEBUG 0x9830 +#define DB_DEBUG2 0x9834 +#define DB_DEBUG3 0x9838 + +#define CC_RB_BACKEND_DISABLE 0x98F4 +#define BACKEND_DISABLE(x) ((x) << 16) +#define GB_ADDR_CONFIG 0x98F8 +#define NUM_PIPES(x) ((x) << 0) +#define NUM_PIPES_MASK 0x00000007 +#define NUM_PIPES_SHIFT 0 +#define PIPE_INTERLEAVE_SIZE(x) ((x) << 4) +#define PIPE_INTERLEAVE_SIZE_MASK 0x00000070 +#define PIPE_INTERLEAVE_SIZE_SHIFT 4 +#define NUM_SHADER_ENGINES(x) ((x) << 12) +#define NUM_SHADER_ENGINES_MASK 0x00003000 +#define NUM_SHADER_ENGINES_SHIFT 12 +#define SHADER_ENGINE_TILE_SIZE(x) ((x) << 16) +#define SHADER_ENGINE_TILE_SIZE_MASK 0x00070000 +#define SHADER_ENGINE_TILE_SIZE_SHIFT 16 +#define ROW_SIZE(x) ((x) << 28) +#define ROW_SIZE_MASK 0x30000000 +#define ROW_SIZE_SHIFT 28 + +#define GB_TILE_MODE0 0x9910 +# define ARRAY_MODE(x) ((x) << 2) +# define ARRAY_LINEAR_GENERAL 0 +# define ARRAY_LINEAR_ALIGNED 1 +# define ARRAY_1D_TILED_THIN1 2 +# define ARRAY_2D_TILED_THIN1 4 +# define ARRAY_PRT_TILED_THIN1 5 +# define ARRAY_PRT_2D_TILED_THIN1 6 +# define PIPE_CONFIG(x) ((x) << 6) +# define ADDR_SURF_P2 0 +# define ADDR_SURF_P4_8x16 4 +# define ADDR_SURF_P4_16x16 5 +# define ADDR_SURF_P4_16x32 6 +# define ADDR_SURF_P4_32x32 7 +# define ADDR_SURF_P8_16x16_8x16 8 +# define ADDR_SURF_P8_16x32_8x16 9 +# define ADDR_SURF_P8_32x32_8x16 10 +# define ADDR_SURF_P8_16x32_16x16 11 +# define ADDR_SURF_P8_32x32_16x16 12 +# define ADDR_SURF_P8_32x32_16x32 13 +# define ADDR_SURF_P8_32x64_32x32 14 +# define TILE_SPLIT(x) ((x) << 11) +# define ADDR_SURF_TILE_SPLIT_64B 0 +# define ADDR_SURF_TILE_SPLIT_128B 1 +# define ADDR_SURF_TILE_SPLIT_256B 2 +# define ADDR_SURF_TILE_SPLIT_512B 3 +# define ADDR_SURF_TILE_SPLIT_1KB 4 +# define ADDR_SURF_TILE_SPLIT_2KB 5 +# define ADDR_SURF_TILE_SPLIT_4KB 6 +# define MICRO_TILE_MODE_NEW(x) ((x) << 22) +# define ADDR_SURF_DISPLAY_MICRO_TILING 0 +# define ADDR_SURF_THIN_MICRO_TILING 1 +# define ADDR_SURF_DEPTH_MICRO_TILING 2 +# define ADDR_SURF_ROTATED_MICRO_TILING 3 +# define SAMPLE_SPLIT(x) ((x) << 25) +# define ADDR_SURF_SAMPLE_SPLIT_1 0 +# define ADDR_SURF_SAMPLE_SPLIT_2 1 +# define ADDR_SURF_SAMPLE_SPLIT_4 2 +# define ADDR_SURF_SAMPLE_SPLIT_8 3 + +#define GB_MACROTILE_MODE0 0x9990 +# define BANK_WIDTH(x) ((x) << 0) +# define ADDR_SURF_BANK_WIDTH_1 0 +# define ADDR_SURF_BANK_WIDTH_2 1 +# define ADDR_SURF_BANK_WIDTH_4 2 +# define ADDR_SURF_BANK_WIDTH_8 3 +# define BANK_HEIGHT(x) ((x) << 2) +# define ADDR_SURF_BANK_HEIGHT_1 0 +# define ADDR_SURF_BANK_HEIGHT_2 1 +# define ADDR_SURF_BANK_HEIGHT_4 2 +# define ADDR_SURF_BANK_HEIGHT_8 3 +# define MACRO_TILE_ASPECT(x) ((x) << 4) +# define ADDR_SURF_MACRO_ASPECT_1 0 +# define ADDR_SURF_MACRO_ASPECT_2 1 +# define ADDR_SURF_MACRO_ASPECT_4 2 +# define ADDR_SURF_MACRO_ASPECT_8 3 +# define NUM_BANKS(x) ((x) << 6) +# define ADDR_SURF_2_BANK 0 +# define ADDR_SURF_4_BANK 1 +# define ADDR_SURF_8_BANK 2 +# define ADDR_SURF_16_BANK 3 + +#define CB_HW_CONTROL 0x9A10 + +#define GC_USER_RB_BACKEND_DISABLE 0x9B7C +#define BACKEND_DISABLE_MASK 0x00FF0000 +#define BACKEND_DISABLE_SHIFT 16 + +#define TCP_CHAN_STEER_LO 0xac0c +#define TCP_CHAN_STEER_HI 0xac10 + +#define PA_SC_RASTER_CONFIG 0x28350 +# define RASTER_CONFIG_RB_MAP_0 0 +# define RASTER_CONFIG_RB_MAP_1 1 +# define RASTER_CONFIG_RB_MAP_2 2 +# define RASTER_CONFIG_RB_MAP_3 3 + +#define GRBM_GFX_INDEX 0x30800 +#define INSTANCE_INDEX(x) ((x) << 0) +#define SH_INDEX(x) ((x) << 8) +#define SE_INDEX(x) ((x) << 16) +#define SH_BROADCAST_WRITES (1 << 29) +#define INSTANCE_BROADCAST_WRITES (1 << 30) +#define SE_BROADCAST_WRITES (1 << 31) + +#define VGT_ESGS_RING_SIZE 0x30900 +#define VGT_GSVS_RING_SIZE 0x30904 +#define VGT_PRIMITIVE_TYPE 0x30908 +#define VGT_INDEX_TYPE 0x3090C + +#define VGT_NUM_INDICES 0x30930 +#define VGT_NUM_INSTANCES 0x30934 +#define VGT_TF_RING_SIZE 0x30938 +#define VGT_HS_OFFCHIP_PARAM 0x3093C +#define VGT_TF_MEMORY_BASE 0x30940 + +#define PA_SU_LINE_STIPPLE_VALUE 0x30a00 +#define PA_SC_LINE_STIPPLE_STATE 0x30a04 + +#define SQC_CACHES 0x30d20 + +#define CP_PERFMON_CNTL 0x36020 + +#define CGTS_TCC_DISABLE 0x3c00c +#define CGTS_USER_TCC_DISABLE 0x3c010 +#define TCC_DISABLE_MASK 0xFFFF0000 +#define TCC_DISABLE_SHIFT 16 + +#endif diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index b50a786..b1a2230 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1505,6 +1505,35 @@ struct si_asic { uint32_t tile_mode_array[32]; };
+struct cik_asic { + unsigned max_shader_engines; + unsigned max_tile_pipes; + unsigned max_cu_per_sh; + unsigned max_sh_per_se; + unsigned max_backends_per_se; + unsigned max_texture_channel_caches; + unsigned max_gprs; + unsigned max_gs_threads; + unsigned max_hw_contexts; + unsigned sc_prim_fifo_size_frontend; + unsigned sc_prim_fifo_size_backend; + unsigned sc_hiz_tile_fifo_size; + unsigned sc_earlyz_tile_fifo_size; + + unsigned num_tile_pipes; + unsigned num_backends_per_se; + unsigned backend_disable_mask_per_asic; + unsigned backend_map; + unsigned num_texture_channel_caches; + unsigned mem_max_burst_length_bytes; + unsigned mem_row_size_in_kb; + unsigned shader_engine_tile_size; + unsigned num_gpus; + unsigned multi_gpu_tile_size; + + unsigned tile_config; +}; + union radeon_asic_config { struct r300_asic r300; struct r100_asic r100; @@ -1513,6 +1542,7 @@ union radeon_asic_config { struct evergreen_asic evergreen; struct cayman_asic cayman; struct si_asic si; + struct cik_asic cik; };
/*
From: Alex Deucher alexander.deucher@amd.com
v2: split soft reset into compute and gfx. Still need to make reset more fine grained, but this should be a start.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/cik.c | 197 +++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/cikd.h | 80 +++++++++++++++++ 2 files changed, 277 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 28f68dc..e448ae2 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -27,9 +27,13 @@ #include <linux/module.h> #include "drmP.h" #include "radeon.h" +#include "radeon_asic.h" #include "cikd.h" #include "atom.h"
+extern void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_save *save); +extern void evergreen_mc_resume(struct radeon_device *rdev, struct evergreen_mc_save *save); + /* * Core functions */ @@ -1190,3 +1194,196 @@ static void cik_gpu_init(struct radeon_device *rdev) udelay(50); }
+/** + * cik_gpu_is_lockup - check if the 3D engine is locked up + * + * @rdev: radeon_device pointer + * @ring: radeon_ring structure holding ring information + * + * Check if the 3D engine is locked up (CIK). + * Returns true if the engine is locked, false if not. + */ +bool cik_gpu_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring) +{ + u32 srbm_status, srbm_status2; + u32 grbm_status, grbm_status2; + u32 grbm_status_se0, grbm_status_se1, grbm_status_se2, grbm_status_se3; + + srbm_status = RREG32(SRBM_STATUS); + srbm_status2 = RREG32(SRBM_STATUS2); + grbm_status = RREG32(GRBM_STATUS); + grbm_status2 = RREG32(GRBM_STATUS2); + grbm_status_se0 = RREG32(GRBM_STATUS_SE0); + grbm_status_se1 = RREG32(GRBM_STATUS_SE1); + grbm_status_se2 = RREG32(GRBM_STATUS_SE2); + grbm_status_se3 = RREG32(GRBM_STATUS_SE3); + if (!(grbm_status & GUI_ACTIVE)) { + radeon_ring_lockup_update(ring); + return false; + } + /* force CP activities */ + radeon_ring_force_activity(rdev, ring); + return radeon_ring_test_lockup(rdev, ring); +} + +/** + * cik_gfx_gpu_soft_reset - soft reset the 3D engine and CPG + * + * @rdev: radeon_device pointer + * + * Soft reset the GFX engine and CPG blocks (CIK). + * XXX: deal with reseting RLC and CPF + * Returns 0 for success. + */ +static int cik_gfx_gpu_soft_reset(struct radeon_device *rdev) +{ + struct evergreen_mc_save save; + u32 grbm_reset = 0; + + if (!(RREG32(GRBM_STATUS) & GUI_ACTIVE)) + return 0; + + dev_info(rdev->dev, "GPU GFX softreset \n"); + dev_info(rdev->dev, " GRBM_STATUS=0x%08X\n", + RREG32(GRBM_STATUS)); + dev_info(rdev->dev, " GRBM_STATUS2=0x%08X\n", + RREG32(GRBM_STATUS2)); + dev_info(rdev->dev, " GRBM_STATUS_SE0=0x%08X\n", + RREG32(GRBM_STATUS_SE0)); + dev_info(rdev->dev, " GRBM_STATUS_SE1=0x%08X\n", + RREG32(GRBM_STATUS_SE1)); + dev_info(rdev->dev, " GRBM_STATUS_SE2=0x%08X\n", + RREG32(GRBM_STATUS_SE2)); + dev_info(rdev->dev, " GRBM_STATUS_SE3=0x%08X\n", + RREG32(GRBM_STATUS_SE3)); + dev_info(rdev->dev, " SRBM_STATUS=0x%08X\n", + RREG32(SRBM_STATUS)); + dev_info(rdev->dev, " SRBM_STATUS2=0x%08X\n", + RREG32(SRBM_STATUS2)); + evergreen_mc_stop(rdev, &save); + if (radeon_mc_wait_for_idle(rdev)) { + dev_warn(rdev->dev, "Wait for MC idle timedout !\n"); + } + /* Disable CP parsing/prefetching */ + WREG32(CP_ME_CNTL, CP_ME_HALT | CP_PFP_HALT | CP_CE_HALT); + + /* reset all the gfx block and all CPG blocks */ + grbm_reset = SOFT_RESET_CPG | SOFT_RESET_GFX; + + dev_info(rdev->dev, " GRBM_SOFT_RESET=0x%08X\n", grbm_reset); + WREG32(GRBM_SOFT_RESET, grbm_reset); + (void)RREG32(GRBM_SOFT_RESET); + udelay(50); + WREG32(GRBM_SOFT_RESET, 0); + (void)RREG32(GRBM_SOFT_RESET); + /* Wait a little for things to settle down */ + udelay(50); + dev_info(rdev->dev, " GRBM_STATUS=0x%08X\n", + RREG32(GRBM_STATUS)); + dev_info(rdev->dev, " GRBM_STATUS2=0x%08X\n", + RREG32(GRBM_STATUS2)); + dev_info(rdev->dev, " GRBM_STATUS_SE0=0x%08X\n", + RREG32(GRBM_STATUS_SE0)); + dev_info(rdev->dev, " GRBM_STATUS_SE1=0x%08X\n", + RREG32(GRBM_STATUS_SE1)); + dev_info(rdev->dev, " GRBM_STATUS_SE2=0x%08X\n", + RREG32(GRBM_STATUS_SE2)); + dev_info(rdev->dev, " GRBM_STATUS_SE3=0x%08X\n", + RREG32(GRBM_STATUS_SE3)); + dev_info(rdev->dev, " SRBM_STATUS=0x%08X\n", + RREG32(SRBM_STATUS)); + dev_info(rdev->dev, " SRBM_STATUS2=0x%08X\n", + RREG32(SRBM_STATUS2)); + evergreen_mc_resume(rdev, &save); + return 0; +} + +/** + * cik_compute_gpu_soft_reset - soft reset CPC + * + * @rdev: radeon_device pointer + * + * Soft reset the CPC blocks (CIK). + * XXX: deal with reseting RLC and CPF + * Returns 0 for success. + */ +static int cik_compute_gpu_soft_reset(struct radeon_device *rdev) +{ + struct evergreen_mc_save save; + u32 grbm_reset = 0; + + dev_info(rdev->dev, "GPU compute softreset \n"); + dev_info(rdev->dev, " GRBM_STATUS=0x%08X\n", + RREG32(GRBM_STATUS)); + dev_info(rdev->dev, " GRBM_STATUS2=0x%08X\n", + RREG32(GRBM_STATUS2)); + dev_info(rdev->dev, " GRBM_STATUS_SE0=0x%08X\n", + RREG32(GRBM_STATUS_SE0)); + dev_info(rdev->dev, " GRBM_STATUS_SE1=0x%08X\n", + RREG32(GRBM_STATUS_SE1)); + dev_info(rdev->dev, " GRBM_STATUS_SE2=0x%08X\n", + RREG32(GRBM_STATUS_SE2)); + dev_info(rdev->dev, " GRBM_STATUS_SE3=0x%08X\n", + RREG32(GRBM_STATUS_SE3)); + dev_info(rdev->dev, " SRBM_STATUS=0x%08X\n", + RREG32(SRBM_STATUS)); + dev_info(rdev->dev, " SRBM_STATUS2=0x%08X\n", + RREG32(SRBM_STATUS2)); + evergreen_mc_stop(rdev, &save); + if (radeon_mc_wait_for_idle(rdev)) { + dev_warn(rdev->dev, "Wait for MC idle timedout !\n"); + } + /* Disable CP parsing/prefetching */ + WREG32(CP_MEC_CNTL, MEC_ME1_HALT | MEC_ME2_HALT); + + /* reset all the CPC blocks */ + grbm_reset = SOFT_RESET_CPG; + + dev_info(rdev->dev, " GRBM_SOFT_RESET=0x%08X\n", grbm_reset); + WREG32(GRBM_SOFT_RESET, grbm_reset); + (void)RREG32(GRBM_SOFT_RESET); + udelay(50); + WREG32(GRBM_SOFT_RESET, 0); + (void)RREG32(GRBM_SOFT_RESET); + /* Wait a little for things to settle down */ + udelay(50); + dev_info(rdev->dev, " GRBM_STATUS=0x%08X\n", + RREG32(GRBM_STATUS)); + dev_info(rdev->dev, " GRBM_STATUS2=0x%08X\n", + RREG32(GRBM_STATUS2)); + dev_info(rdev->dev, " GRBM_STATUS_SE0=0x%08X\n", + RREG32(GRBM_STATUS_SE0)); + dev_info(rdev->dev, " GRBM_STATUS_SE1=0x%08X\n", + RREG32(GRBM_STATUS_SE1)); + dev_info(rdev->dev, " GRBM_STATUS_SE2=0x%08X\n", + RREG32(GRBM_STATUS_SE2)); + dev_info(rdev->dev, " GRBM_STATUS_SE3=0x%08X\n", + RREG32(GRBM_STATUS_SE3)); + dev_info(rdev->dev, " SRBM_STATUS=0x%08X\n", + RREG32(SRBM_STATUS)); + dev_info(rdev->dev, " SRBM_STATUS2=0x%08X\n", + RREG32(SRBM_STATUS2)); + evergreen_mc_resume(rdev, &save); + return 0; +} + +/** + * cik_asic_reset - soft reset compute and gfx + * + * @rdev: radeon_device pointer + * + * Soft reset the CPC blocks (CIK). + * XXX: make this more fine grained and only reset + * what is necessary. + * Returns 0 for success. + */ +int cik_asic_reset(struct radeon_device *rdev) +{ + int r; + + r = cik_compute_gpu_soft_reset(rdev); + if (r) + dev_info(rdev->dev, "Compute reset failed!\n"); + + return cik_gfx_gpu_soft_reset(rdev); +} diff --git a/drivers/gpu/drm/radeon/cikd.h b/drivers/gpu/drm/radeon/cikd.h index e51fb41..41b2316 100644 --- a/drivers/gpu/drm/radeon/cikd.h +++ b/drivers/gpu/drm/radeon/cikd.h @@ -30,6 +30,9 @@
#define DMIF_ADDR_CALC 0xC00
+#define SRBM_STATUS2 0xE4C +#define SRBM_STATUS 0xE50 + #define MC_SHARED_CHMAP 0x2004 #define NOOFCHAN_SHIFT 12 #define NOOFCHAN_MASK 0x0000f000 @@ -65,6 +68,83 @@ #define GRBM_CNTL 0x8000 #define GRBM_READ_TIMEOUT(x) ((x) << 0)
+#define GRBM_STATUS2 0x8008 +#define ME0PIPE1_CMDFIFO_AVAIL_MASK 0x0000000F +#define ME0PIPE1_CF_RQ_PENDING (1 << 4) +#define ME0PIPE1_PF_RQ_PENDING (1 << 5) +#define ME1PIPE0_RQ_PENDING (1 << 6) +#define ME1PIPE1_RQ_PENDING (1 << 7) +#define ME1PIPE2_RQ_PENDING (1 << 8) +#define ME1PIPE3_RQ_PENDING (1 << 9) +#define ME2PIPE0_RQ_PENDING (1 << 10) +#define ME2PIPE1_RQ_PENDING (1 << 11) +#define ME2PIPE2_RQ_PENDING (1 << 12) +#define ME2PIPE3_RQ_PENDING (1 << 13) +#define RLC_RQ_PENDING (1 << 14) +#define RLC_BUSY (1 << 24) +#define TC_BUSY (1 << 25) +#define CPF_BUSY (1 << 28) +#define CPC_BUSY (1 << 29) +#define CPG_BUSY (1 << 30) + +#define GRBM_STATUS 0x8010 +#define ME0PIPE0_CMDFIFO_AVAIL_MASK 0x0000000F +#define SRBM_RQ_PENDING (1 << 5) +#define ME0PIPE0_CF_RQ_PENDING (1 << 7) +#define ME0PIPE0_PF_RQ_PENDING (1 << 8) +#define GDS_DMA_RQ_PENDING (1 << 9) +#define DB_CLEAN (1 << 12) +#define CB_CLEAN (1 << 13) +#define TA_BUSY (1 << 14) +#define GDS_BUSY (1 << 15) +#define WD_BUSY_NO_DMA (1 << 16) +#define VGT_BUSY (1 << 17) +#define IA_BUSY_NO_DMA (1 << 18) +#define IA_BUSY (1 << 19) +#define SX_BUSY (1 << 20) +#define WD_BUSY (1 << 21) +#define SPI_BUSY (1 << 22) +#define BCI_BUSY (1 << 23) +#define SC_BUSY (1 << 24) +#define PA_BUSY (1 << 25) +#define DB_BUSY (1 << 26) +#define CP_COHERENCY_BUSY (1 << 28) +#define CP_BUSY (1 << 29) +#define CB_BUSY (1 << 30) +#define GUI_ACTIVE (1 << 31) +#define GRBM_STATUS_SE0 0x8014 +#define GRBM_STATUS_SE1 0x8018 +#define GRBM_STATUS_SE2 0x8038 +#define GRBM_STATUS_SE3 0x803C +#define SE_DB_CLEAN (1 << 1) +#define SE_CB_CLEAN (1 << 2) +#define SE_BCI_BUSY (1 << 22) +#define SE_VGT_BUSY (1 << 23) +#define SE_PA_BUSY (1 << 24) +#define SE_TA_BUSY (1 << 25) +#define SE_SX_BUSY (1 << 26) +#define SE_SPI_BUSY (1 << 27) +#define SE_SC_BUSY (1 << 29) +#define SE_DB_BUSY (1 << 30) +#define SE_CB_BUSY (1 << 31) + +#define GRBM_SOFT_RESET 0x8020 +#define SOFT_RESET_CP (1 << 0) /* All CP blocks */ +#define SOFT_RESET_RLC (1 << 2) /* RLC */ +#define SOFT_RESET_GFX (1 << 16) /* GFX */ +#define SOFT_RESET_CPF (1 << 17) /* CP fetcher shared by gfx and compute */ +#define SOFT_RESET_CPC (1 << 18) /* CP Compute (MEC1/2) */ +#define SOFT_RESET_CPG (1 << 19) /* CP GFX (PFP, ME, CE) */ + +#define CP_MEC_CNTL 0x8234 +#define MEC_ME2_HALT (1 << 28) +#define MEC_ME1_HALT (1 << 30) + +#define CP_ME_CNTL 0x86D8 +#define CP_CE_HALT (1 << 24) +#define CP_PFP_HALT (1 << 26) +#define CP_ME_HALT (1 << 28) + #define CP_MEQ_THRESHOLDS 0x8764 #define MEQ1_START(x) ((x) << 0) #define MEQ2_START(x) ((x) << 8)
From: Alex Deucher alexander.deucher@amd.com
The vm callbacks are the same as the SI ones right now (same regs and bits). We could share the SI variants, and I may yet do that, but I figured I would add CIK specific ones for now in case we need to change anything.
V2: add documentation, minor fixes. V3: integrate vram offset fixes for APUs V4: enable 2 level VM PTs V5: index SH_MEM_* regs properly V6: add ib_parse()
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/cik.c | 361 +++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/cikd.h | 125 ++++++++++++++ drivers/gpu/drm/radeon/si.c | 4 +- 3 files changed, 488 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index e448ae2..a4e1b95 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -33,6 +33,7 @@
extern void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_save *save); extern void evergreen_mc_resume(struct radeon_device *rdev, struct evergreen_mc_save *save); +extern void si_vram_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc);
/* * Core functions @@ -1387,3 +1388,363 @@ int cik_asic_reset(struct radeon_device *rdev)
return cik_gfx_gpu_soft_reset(rdev); } + +/* MC */ +/** + * cik_mc_program - program the GPU memory controller + * + * @rdev: radeon_device pointer + * + * Set the location of vram, gart, and AGP in the GPU's + * physical address space (CIK). + */ +static void cik_mc_program(struct radeon_device *rdev) +{ + struct evergreen_mc_save save; + u32 tmp; + int i, j; + + /* Initialize HDP */ + for (i = 0, j = 0; i < 32; i++, j += 0x18) { + WREG32((0x2c14 + j), 0x00000000); + WREG32((0x2c18 + j), 0x00000000); + WREG32((0x2c1c + j), 0x00000000); + WREG32((0x2c20 + j), 0x00000000); + WREG32((0x2c24 + j), 0x00000000); + } + WREG32(HDP_REG_COHERENCY_FLUSH_CNTL, 0); + + evergreen_mc_stop(rdev, &save); + if (radeon_mc_wait_for_idle(rdev)) { + dev_warn(rdev->dev, "Wait for MC idle timedout !\n"); + } + /* Lockout access through VGA aperture*/ + WREG32(VGA_HDP_CONTROL, VGA_MEMORY_DISABLE); + /* Update configuration */ + WREG32(MC_VM_SYSTEM_APERTURE_LOW_ADDR, + rdev->mc.vram_start >> 12); + WREG32(MC_VM_SYSTEM_APERTURE_HIGH_ADDR, + rdev->mc.vram_end >> 12); + WREG32(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR, + rdev->vram_scratch.gpu_addr >> 12); + tmp = ((rdev->mc.vram_end >> 24) & 0xFFFF) << 16; + tmp |= ((rdev->mc.vram_start >> 24) & 0xFFFF); + WREG32(MC_VM_FB_LOCATION, tmp); + /* XXX double check these! */ + WREG32(HDP_NONSURFACE_BASE, (rdev->mc.vram_start >> 8)); + WREG32(HDP_NONSURFACE_INFO, (2 << 7) | (1 << 30)); + WREG32(HDP_NONSURFACE_SIZE, 0x3FFFFFFF); + WREG32(MC_VM_AGP_BASE, 0); + WREG32(MC_VM_AGP_TOP, 0x0FFFFFFF); + WREG32(MC_VM_AGP_BOT, 0x0FFFFFFF); + if (radeon_mc_wait_for_idle(rdev)) { + dev_warn(rdev->dev, "Wait for MC idle timedout !\n"); + } + evergreen_mc_resume(rdev, &save); + /* we need to own VRAM, so turn off the VGA renderer here + * to stop it overwriting our objects */ + rv515_vga_render_disable(rdev); +} + +/** + * cik_mc_init - initialize the memory controller driver params + * + * @rdev: radeon_device pointer + * + * Look up the amount of vram, vram width, and decide how to place + * vram and gart within the GPU's physical address space (CIK). + * Returns 0 for success. + */ +static int cik_mc_init(struct radeon_device *rdev) +{ + u32 tmp; + int chansize, numchan; + + /* Get VRAM informations */ + rdev->mc.vram_is_ddr = true; + tmp = RREG32(MC_ARB_RAMCFG); + if (tmp & CHANSIZE_MASK) { + chansize = 64; + } else { + chansize = 32; + } + tmp = RREG32(MC_SHARED_CHMAP); + switch ((tmp & NOOFCHAN_MASK) >> NOOFCHAN_SHIFT) { + case 0: + default: + numchan = 1; + break; + case 1: + numchan = 2; + break; + case 2: + numchan = 4; + break; + case 3: + numchan = 8; + break; + case 4: + numchan = 3; + break; + case 5: + numchan = 6; + break; + case 6: + numchan = 10; + break; + case 7: + numchan = 12; + break; + case 8: + numchan = 16; + break; + } + rdev->mc.vram_width = numchan * chansize; + /* Could aper size report 0 ? */ + rdev->mc.aper_base = pci_resource_start(rdev->pdev, 0); + rdev->mc.aper_size = pci_resource_len(rdev->pdev, 0); + /* size in MB on si */ + rdev->mc.mc_vram_size = RREG32(CONFIG_MEMSIZE) * 1024 * 1024; + rdev->mc.real_vram_size = RREG32(CONFIG_MEMSIZE) * 1024 * 1024; + rdev->mc.visible_vram_size = rdev->mc.aper_size; + si_vram_gtt_location(rdev, &rdev->mc); + radeon_update_bandwidth_info(rdev); + + return 0; +} + +/* + * GART + * VMID 0 is the physical GPU addresses as used by the kernel. + * VMIDs 1-15 are used for userspace clients and are handled + * by the radeon vm/hsa code. + */ +/** + * cik_pcie_gart_tlb_flush - gart tlb flush callback + * + * @rdev: radeon_device pointer + * + * Flush the TLB for the VMID 0 page table (CIK). + */ +void cik_pcie_gart_tlb_flush(struct radeon_device *rdev) +{ + /* flush hdp cache */ + WREG32(HDP_MEM_COHERENCY_FLUSH_CNTL, 0); + + /* bits 0-15 are the VM contexts0-15 */ + WREG32(VM_INVALIDATE_REQUEST, 0x1); +} + +/** + * cik_pcie_gart_enable - gart enable + * + * @rdev: radeon_device pointer + * + * This sets up the TLBs, programs the page tables for VMID0, + * sets up the hw for VMIDs 1-15 which are allocated on + * demand, and sets up the global locations for the LDS, GDS, + * and GPUVM for FSA64 clients (CIK). + * Returns 0 for success, errors for failure. + */ +static int cik_pcie_gart_enable(struct radeon_device *rdev) +{ + int r, i; + + if (rdev->gart.robj == NULL) { + dev_err(rdev->dev, "No VRAM object for PCIE GART.\n"); + return -EINVAL; + } + r = radeon_gart_table_vram_pin(rdev); + if (r) + return r; + radeon_gart_restore(rdev); + /* Setup TLB control */ + WREG32(MC_VM_MX_L1_TLB_CNTL, + (0xA << 7) | + ENABLE_L1_TLB | + SYSTEM_ACCESS_MODE_NOT_IN_SYS | + ENABLE_ADVANCED_DRIVER_MODEL | + SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU); + /* Setup L2 cache */ + WREG32(VM_L2_CNTL, ENABLE_L2_CACHE | + ENABLE_L2_FRAGMENT_PROCESSING | + ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE | + ENABLE_L2_PDE0_CACHE_LRU_UPDATE_BY_WRITE | + EFFECTIVE_L2_QUEUE_SIZE(7) | + CONTEXT1_IDENTITY_ACCESS_MODE(1)); + WREG32(VM_L2_CNTL2, INVALIDATE_ALL_L1_TLBS | INVALIDATE_L2_CACHE); + WREG32(VM_L2_CNTL3, L2_CACHE_BIGK_ASSOCIATIVITY | + L2_CACHE_BIGK_FRAGMENT_SIZE(6)); + /* setup context0 */ + WREG32(VM_CONTEXT0_PAGE_TABLE_START_ADDR, rdev->mc.gtt_start >> 12); + WREG32(VM_CONTEXT0_PAGE_TABLE_END_ADDR, rdev->mc.gtt_end >> 12); + WREG32(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR, rdev->gart.table_addr >> 12); + WREG32(VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR, + (u32)(rdev->dummy_page.addr >> 12)); + WREG32(VM_CONTEXT0_CNTL2, 0); + WREG32(VM_CONTEXT0_CNTL, (ENABLE_CONTEXT | PAGE_TABLE_DEPTH(0) | + RANGE_PROTECTION_FAULT_ENABLE_DEFAULT)); + + WREG32(0x15D4, 0); + WREG32(0x15D8, 0); + WREG32(0x15DC, 0); + + /* empty context1-15 */ + /* FIXME start with 4G, once using 2 level pt switch to full + * vm size space + */ + /* set vm size, must be a multiple of 4 */ + WREG32(VM_CONTEXT1_PAGE_TABLE_START_ADDR, 0); + WREG32(VM_CONTEXT1_PAGE_TABLE_END_ADDR, rdev->vm_manager.max_pfn); + for (i = 1; i < 16; i++) { + if (i < 8) + WREG32(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + (i << 2), + rdev->gart.table_addr >> 12); + else + WREG32(VM_CONTEXT8_PAGE_TABLE_BASE_ADDR + ((i - 8) << 2), + rdev->gart.table_addr >> 12); + } + + /* enable context1-15 */ + WREG32(VM_CONTEXT1_PROTECTION_FAULT_DEFAULT_ADDR, + (u32)(rdev->dummy_page.addr >> 12)); + WREG32(VM_CONTEXT1_CNTL2, 0); + WREG32(VM_CONTEXT1_CNTL, ENABLE_CONTEXT | PAGE_TABLE_DEPTH(1) | + RANGE_PROTECTION_FAULT_ENABLE_DEFAULT); + + /* TC cache setup ??? */ + WREG32(TC_CFG_L1_LOAD_POLICY0, 0); + WREG32(TC_CFG_L1_LOAD_POLICY1, 0); + WREG32(TC_CFG_L1_STORE_POLICY, 0); + + WREG32(TC_CFG_L2_LOAD_POLICY0, 0); + WREG32(TC_CFG_L2_LOAD_POLICY1, 0); + WREG32(TC_CFG_L2_STORE_POLICY0, 0); + WREG32(TC_CFG_L2_STORE_POLICY1, 0); + WREG32(TC_CFG_L2_ATOMIC_POLICY, 0); + + WREG32(TC_CFG_L1_VOLATILE, 0); + WREG32(TC_CFG_L2_VOLATILE, 0); + + if (rdev->family == CHIP_KAVERI) { + u32 tmp = RREG32(CHUB_CONTROL); + tmp &= ~BYPASS_VM; + WREG32(CHUB_CONTROL, tmp); + } + + /* XXX SH_MEM regs */ + /* where to put LDS, scratch, GPUVM in FSA64 space */ + for (i = 0; i < 16; i++) { + WREG32(SRBM_GFX_CNTL, VMID(i)); + WREG32(SH_MEM_CONFIG, 0); + WREG32(SH_MEM_APE1_BASE, 1); + WREG32(SH_MEM_APE1_LIMIT, 0); + WREG32(SH_MEM_BASES, 0); + } + WREG32(SRBM_GFX_CNTL, 0); + + cik_pcie_gart_tlb_flush(rdev); + DRM_INFO("PCIE GART of %uM enabled (table at 0x%016llX).\n", + (unsigned)(rdev->mc.gtt_size >> 20), + (unsigned long long)rdev->gart.table_addr); + rdev->gart.ready = true; + return 0; +} + +/** + * cik_pcie_gart_disable - gart disable + * + * @rdev: radeon_device pointer + * + * This disables all VM page table (CIK). + */ +static void cik_pcie_gart_disable(struct radeon_device *rdev) +{ + /* Disable all tables */ + WREG32(VM_CONTEXT0_CNTL, 0); + WREG32(VM_CONTEXT1_CNTL, 0); + /* Setup TLB control */ + WREG32(MC_VM_MX_L1_TLB_CNTL, SYSTEM_ACCESS_MODE_NOT_IN_SYS | + SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU); + /* Setup L2 cache */ + WREG32(VM_L2_CNTL, + ENABLE_L2_FRAGMENT_PROCESSING | + ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE | + ENABLE_L2_PDE0_CACHE_LRU_UPDATE_BY_WRITE | + EFFECTIVE_L2_QUEUE_SIZE(7) | + CONTEXT1_IDENTITY_ACCESS_MODE(1)); + WREG32(VM_L2_CNTL2, 0); + WREG32(VM_L2_CNTL3, L2_CACHE_BIGK_ASSOCIATIVITY | + L2_CACHE_BIGK_FRAGMENT_SIZE(6)); + radeon_gart_table_vram_unpin(rdev); +} + +/** + * cik_pcie_gart_fini - vm fini callback + * + * @rdev: radeon_device pointer + * + * Tears down the driver GART/VM setup (CIK). + */ +static void cik_pcie_gart_fini(struct radeon_device *rdev) +{ + cik_pcie_gart_disable(rdev); + radeon_gart_table_vram_free(rdev); + radeon_gart_fini(rdev); +} + +/* vm parser */ +/** + * cik_ib_parse - vm ib_parse callback + * + * @rdev: radeon_device pointer + * @ib: indirect buffer pointer + * + * CIK uses hw IB checking so this is a nop (CIK). + */ +int cik_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib) +{ + return 0; +} + +/* + * vm + * VMID 0 is the physical GPU addresses as used by the kernel. + * VMIDs 1-15 are used for userspace clients and are handled + * by the radeon vm/hsa code. + */ +/** + * cik_vm_init - cik vm init callback + * + * @rdev: radeon_device pointer + * + * Inits cik specific vm parameters (number of VMs, base of vram for + * VMIDs 1-15) (CIK). + * Returns 0 for success. + */ +int cik_vm_init(struct radeon_device *rdev) +{ + /* number of VMs */ + rdev->vm_manager.nvm = 16; + /* base offset of vram pages */ + if (rdev->flags & RADEON_IS_IGP) { + u64 tmp = RREG32(MC_VM_FB_OFFSET); + tmp <<= 22; + rdev->vm_manager.vram_base_offset = tmp; + } else + rdev->vm_manager.vram_base_offset = 0; + + return 0; +} + +/** + * cik_vm_fini - cik vm fini callback + * + * @rdev: radeon_device pointer + * + * Tear down any asic specific VM setup (CIK). + */ +void cik_vm_fini(struct radeon_device *rdev) +{ +} + diff --git a/drivers/gpu/drm/radeon/cikd.h b/drivers/gpu/drm/radeon/cikd.h index 41b2316..071a781 100644 --- a/drivers/gpu/drm/radeon/cikd.h +++ b/drivers/gpu/drm/radeon/cikd.h @@ -28,16 +28,106 @@
#define CIK_RB_BITMAP_WIDTH_PER_SH 2
+#define VGA_HDP_CONTROL 0x328 +#define VGA_MEMORY_DISABLE (1 << 4) + #define DMIF_ADDR_CALC 0xC00
+#define SRBM_GFX_CNTL 0xE44 +#define PIPEID(x) ((x) << 0) +#define MEID(x) ((x) << 2) +#define VMID(x) ((x) << 4) +#define QUEUEID(x) ((x) << 8) + #define SRBM_STATUS2 0xE4C #define SRBM_STATUS 0xE50
+#define VM_L2_CNTL 0x1400 +#define ENABLE_L2_CACHE (1 << 0) +#define ENABLE_L2_FRAGMENT_PROCESSING (1 << 1) +#define L2_CACHE_PTE_ENDIAN_SWAP_MODE(x) ((x) << 2) +#define L2_CACHE_PDE_ENDIAN_SWAP_MODE(x) ((x) << 4) +#define ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE (1 << 9) +#define ENABLE_L2_PDE0_CACHE_LRU_UPDATE_BY_WRITE (1 << 10) +#define EFFECTIVE_L2_QUEUE_SIZE(x) (((x) & 7) << 15) +#define CONTEXT1_IDENTITY_ACCESS_MODE(x) (((x) & 3) << 19) +#define VM_L2_CNTL2 0x1404 +#define INVALIDATE_ALL_L1_TLBS (1 << 0) +#define INVALIDATE_L2_CACHE (1 << 1) +#define INVALIDATE_CACHE_MODE(x) ((x) << 26) +#define INVALIDATE_PTE_AND_PDE_CACHES 0 +#define INVALIDATE_ONLY_PTE_CACHES 1 +#define INVALIDATE_ONLY_PDE_CACHES 2 +#define VM_L2_CNTL3 0x1408 +#define BANK_SELECT(x) ((x) << 0) +#define L2_CACHE_UPDATE_MODE(x) ((x) << 6) +#define L2_CACHE_BIGK_FRAGMENT_SIZE(x) ((x) << 15) +#define L2_CACHE_BIGK_ASSOCIATIVITY (1 << 20) +#define VM_L2_STATUS 0x140C +#define L2_BUSY (1 << 0) +#define VM_CONTEXT0_CNTL 0x1410 +#define ENABLE_CONTEXT (1 << 0) +#define PAGE_TABLE_DEPTH(x) (((x) & 3) << 1) +#define RANGE_PROTECTION_FAULT_ENABLE_DEFAULT (1 << 4) +#define VM_CONTEXT1_CNTL 0x1414 +#define VM_CONTEXT0_CNTL2 0x1430 +#define VM_CONTEXT1_CNTL2 0x1434 +#define VM_CONTEXT8_PAGE_TABLE_BASE_ADDR 0x1438 +#define VM_CONTEXT9_PAGE_TABLE_BASE_ADDR 0x143c +#define VM_CONTEXT10_PAGE_TABLE_BASE_ADDR 0x1440 +#define VM_CONTEXT11_PAGE_TABLE_BASE_ADDR 0x1444 +#define VM_CONTEXT12_PAGE_TABLE_BASE_ADDR 0x1448 +#define VM_CONTEXT13_PAGE_TABLE_BASE_ADDR 0x144c +#define VM_CONTEXT14_PAGE_TABLE_BASE_ADDR 0x1450 +#define VM_CONTEXT15_PAGE_TABLE_BASE_ADDR 0x1454 + +#define VM_INVALIDATE_REQUEST 0x1478 +#define VM_INVALIDATE_RESPONSE 0x147c + +#define VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR 0x1518 +#define VM_CONTEXT1_PROTECTION_FAULT_DEFAULT_ADDR 0x151c + +#define VM_CONTEXT0_PAGE_TABLE_BASE_ADDR 0x153c +#define VM_CONTEXT1_PAGE_TABLE_BASE_ADDR 0x1540 +#define VM_CONTEXT2_PAGE_TABLE_BASE_ADDR 0x1544 +#define VM_CONTEXT3_PAGE_TABLE_BASE_ADDR 0x1548 +#define VM_CONTEXT4_PAGE_TABLE_BASE_ADDR 0x154c +#define VM_CONTEXT5_PAGE_TABLE_BASE_ADDR 0x1550 +#define VM_CONTEXT6_PAGE_TABLE_BASE_ADDR 0x1554 +#define VM_CONTEXT7_PAGE_TABLE_BASE_ADDR 0x1558 +#define VM_CONTEXT0_PAGE_TABLE_START_ADDR 0x155c +#define VM_CONTEXT1_PAGE_TABLE_START_ADDR 0x1560 + +#define VM_CONTEXT0_PAGE_TABLE_END_ADDR 0x157C +#define VM_CONTEXT1_PAGE_TABLE_END_ADDR 0x1580 + #define MC_SHARED_CHMAP 0x2004 #define NOOFCHAN_SHIFT 12 #define NOOFCHAN_MASK 0x0000f000 #define MC_SHARED_CHREMAP 0x2008
+#define CHUB_CONTROL 0x1864 +#define BYPASS_VM (1 << 0) + +#define MC_VM_FB_LOCATION 0x2024 +#define MC_VM_AGP_TOP 0x2028 +#define MC_VM_AGP_BOT 0x202C +#define MC_VM_AGP_BASE 0x2030 +#define MC_VM_SYSTEM_APERTURE_LOW_ADDR 0x2034 +#define MC_VM_SYSTEM_APERTURE_HIGH_ADDR 0x2038 +#define MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR 0x203C + +#define MC_VM_MX_L1_TLB_CNTL 0x2064 +#define ENABLE_L1_TLB (1 << 0) +#define ENABLE_L1_FRAGMENT_PROCESSING (1 << 1) +#define SYSTEM_ACCESS_MODE_PA_ONLY (0 << 3) +#define SYSTEM_ACCESS_MODE_USE_SYS_MAP (1 << 3) +#define SYSTEM_ACCESS_MODE_IN_SYS (2 << 3) +#define SYSTEM_ACCESS_MODE_NOT_IN_SYS (3 << 3) +#define SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU (0 << 5) +#define ENABLE_ADVANCED_DRIVER_MODEL (1 << 6) +#define MC_VM_FB_OFFSET 0x2068 + #define MC_ARB_RAMCFG 0x2760 #define NOOFBANK_SHIFT 0 #define NOOFBANK_MASK 0x00000003 @@ -61,10 +151,16 @@ #define HDP_MISC_CNTL 0x2F4C #define HDP_FLUSH_INVALIDATE_CACHE (1 << 0)
+#define CONFIG_MEMSIZE 0x5428 + +#define HDP_MEM_COHERENCY_FLUSH_CNTL 0x5480 + #define BIF_FB_EN 0x5490 #define FB_READ_EN (1 << 0) #define FB_WRITE_EN (1 << 1)
+#define HDP_REG_COHERENCY_FLUSH_CNTL 0x54A0 + #define GRBM_CNTL 0x8000 #define GRBM_READ_TIMEOUT(x) ((x) << 0)
@@ -189,6 +285,24 @@
#define SQ_CONFIG 0x8C00
+#define SH_MEM_BASES 0x8C28 +/* if PTR32, these are the bases for scratch and lds */ +#define PRIVATE_BASE(x) ((x) << 0) /* scratch */ +#define SHARED_BASE(x) ((x) << 16) /* LDS */ +#define SH_MEM_APE1_BASE 0x8C2C +/* if PTR32, this is the base location of GPUVM */ +#define SH_MEM_APE1_LIMIT 0x8C30 +/* if PTR32, this is the upper limit of GPUVM */ +#define SH_MEM_CONFIG 0x8C34 +#define PTR32 (1 << 0) +#define ALIGNMENT_MODE(x) ((x) << 2) +#define SH_MEM_ALIGNMENT_MODE_DWORD 0 +#define SH_MEM_ALIGNMENT_MODE_DWORD_STRICT 1 +#define SH_MEM_ALIGNMENT_MODE_STRICT 2 +#define SH_MEM_ALIGNMENT_MODE_UNALIGNED 3 +#define DEFAULT_MTYPE(x) ((x) << 4) +#define APE1_MTYPE(x) ((x) << 7) + #define SX_DEBUG_1 0x9060
#define SPI_CONFIG_CNTL 0x9100 @@ -293,6 +407,17 @@ #define TCP_CHAN_STEER_LO 0xac0c #define TCP_CHAN_STEER_HI 0xac10
+#define TC_CFG_L1_LOAD_POLICY0 0xAC68 +#define TC_CFG_L1_LOAD_POLICY1 0xAC6C +#define TC_CFG_L1_STORE_POLICY 0xAC70 +#define TC_CFG_L2_LOAD_POLICY0 0xAC74 +#define TC_CFG_L2_LOAD_POLICY1 0xAC78 +#define TC_CFG_L2_STORE_POLICY0 0xAC7C +#define TC_CFG_L2_STORE_POLICY1 0xAC80 +#define TC_CFG_L2_ATOMIC_POLICY 0xAC84 +#define TC_CFG_L1_VOLATILE 0xAC88 +#define TC_CFG_L2_VOLATILE 0xAC8C + #define PA_SC_RASTER_CONFIG 0x28350 # define RASTER_CONFIG_RB_MAP_0 0 # define RASTER_CONFIG_RB_MAP_1 1 diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c index a1b0da6..813a8a9 100644 --- a/drivers/gpu/drm/radeon/si.c +++ b/drivers/gpu/drm/radeon/si.c @@ -3535,8 +3535,8 @@ static void si_mc_program(struct radeon_device *rdev) } }
-static void si_vram_gtt_location(struct radeon_device *rdev, - struct radeon_mc *mc) +void si_vram_gtt_location(struct radeon_device *rdev, + struct radeon_mc *mc) { if (mc->mc_vram_size > 0xFFC0000000ULL) { /* leave room for at least 1024M GTT */
From: Alex Deucher alexander.deucher@amd.com
Redirect invalid memory accesses to the default page instead of locking up the memory controller.
v2: rebase on top of 2 level PTs
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/cik.c | 15 +++++++++++++-- drivers/gpu/drm/radeon/cikd.h | 11 +++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index a4e1b95..28a7531 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -1608,9 +1608,20 @@ static int cik_pcie_gart_enable(struct radeon_device *rdev) /* enable context1-15 */ WREG32(VM_CONTEXT1_PROTECTION_FAULT_DEFAULT_ADDR, (u32)(rdev->dummy_page.addr >> 12)); - WREG32(VM_CONTEXT1_CNTL2, 0); + WREG32(VM_CONTEXT1_CNTL2, 4); WREG32(VM_CONTEXT1_CNTL, ENABLE_CONTEXT | PAGE_TABLE_DEPTH(1) | - RANGE_PROTECTION_FAULT_ENABLE_DEFAULT); + RANGE_PROTECTION_FAULT_ENABLE_INTERRUPT | + RANGE_PROTECTION_FAULT_ENABLE_DEFAULT | + DUMMY_PAGE_PROTECTION_FAULT_ENABLE_INTERRUPT | + DUMMY_PAGE_PROTECTION_FAULT_ENABLE_DEFAULT | + PDE0_PROTECTION_FAULT_ENABLE_INTERRUPT | + PDE0_PROTECTION_FAULT_ENABLE_DEFAULT | + VALID_PROTECTION_FAULT_ENABLE_INTERRUPT | + VALID_PROTECTION_FAULT_ENABLE_DEFAULT | + READ_PROTECTION_FAULT_ENABLE_INTERRUPT | + READ_PROTECTION_FAULT_ENABLE_DEFAULT | + WRITE_PROTECTION_FAULT_ENABLE_INTERRUPT | + WRITE_PROTECTION_FAULT_ENABLE_DEFAULT);
/* TC cache setup ??? */ WREG32(TC_CFG_L1_LOAD_POLICY0, 0); diff --git a/drivers/gpu/drm/radeon/cikd.h b/drivers/gpu/drm/radeon/cikd.h index 071a781..0dab9c5 100644 --- a/drivers/gpu/drm/radeon/cikd.h +++ b/drivers/gpu/drm/radeon/cikd.h @@ -68,7 +68,18 @@ #define VM_CONTEXT0_CNTL 0x1410 #define ENABLE_CONTEXT (1 << 0) #define PAGE_TABLE_DEPTH(x) (((x) & 3) << 1) +#define RANGE_PROTECTION_FAULT_ENABLE_INTERRUPT (1 << 3) #define RANGE_PROTECTION_FAULT_ENABLE_DEFAULT (1 << 4) +#define DUMMY_PAGE_PROTECTION_FAULT_ENABLE_INTERRUPT (1 << 6) +#define DUMMY_PAGE_PROTECTION_FAULT_ENABLE_DEFAULT (1 << 7) +#define PDE0_PROTECTION_FAULT_ENABLE_INTERRUPT (1 << 9) +#define PDE0_PROTECTION_FAULT_ENABLE_DEFAULT (1 << 10) +#define VALID_PROTECTION_FAULT_ENABLE_INTERRUPT (1 << 12) +#define VALID_PROTECTION_FAULT_ENABLE_DEFAULT (1 << 13) +#define READ_PROTECTION_FAULT_ENABLE_INTERRUPT (1 << 15) +#define READ_PROTECTION_FAULT_ENABLE_DEFAULT (1 << 16) +#define WRITE_PROTECTION_FAULT_ENABLE_INTERRUPT (1 << 18) +#define WRITE_PROTECTION_FAULT_ENABLE_DEFAULT (1 << 19) #define VM_CONTEXT1_CNTL 0x1414 #define VM_CONTEXT0_CNTL2 0x1430 #define VM_CONTEXT1_CNTL2 0x1434
From: Alex Deucher alexander.deucher@amd.com
Currently the driver required 6 sets of ucode: 1. pfp - pre-fetch parser, part of the GFX CP 2. me - micro engine, part of the GFX CP 3. ce - constant engine, part of the GFX CP 4. rlc - interrupt, etc. controller 5. mc - memory controller (discrete cards only) 6. mec - compute engines, part of Compute CP
V2: add documentation V3: update MC ucode V4: rebase V5: update mc ucode
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/cik.c | 180 +++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/radeon.h | 1 + 2 files changed, 181 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 28a7531..36e0fc9 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -31,10 +31,190 @@ #include "cikd.h" #include "atom.h"
+/* GFX */ +#define CIK_PFP_UCODE_SIZE 2144 +#define CIK_ME_UCODE_SIZE 2144 +#define CIK_CE_UCODE_SIZE 2144 +/* compute */ +#define CIK_MEC_UCODE_SIZE 4192 +/* interrupts */ +#define BONAIRE_RLC_UCODE_SIZE 2048 +#define KB_RLC_UCODE_SIZE 2560 +#define KV_RLC_UCODE_SIZE 2560 +/* gddr controller */ +#define CIK_MC_UCODE_SIZE 7866 + +MODULE_FIRMWARE("radeon/BONAIRE_pfp.bin"); +MODULE_FIRMWARE("radeon/BONAIRE_me.bin"); +MODULE_FIRMWARE("radeon/BONAIRE_ce.bin"); +MODULE_FIRMWARE("radeon/BONAIRE_mec.bin"); +MODULE_FIRMWARE("radeon/BONAIRE_mc.bin"); +MODULE_FIRMWARE("radeon/BONAIRE_rlc.bin"); +MODULE_FIRMWARE("radeon/KAVERI_pfp.bin"); +MODULE_FIRMWARE("radeon/KAVERI_me.bin"); +MODULE_FIRMWARE("radeon/KAVERI_ce.bin"); +MODULE_FIRMWARE("radeon/KAVERI_mec.bin"); +MODULE_FIRMWARE("radeon/KAVERI_rlc.bin"); +MODULE_FIRMWARE("radeon/KABINI_pfp.bin"); +MODULE_FIRMWARE("radeon/KABINI_me.bin"); +MODULE_FIRMWARE("radeon/KABINI_ce.bin"); +MODULE_FIRMWARE("radeon/KABINI_mec.bin"); +MODULE_FIRMWARE("radeon/KABINI_rlc.bin"); + extern void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_save *save); extern void evergreen_mc_resume(struct radeon_device *rdev, struct evergreen_mc_save *save); extern void si_vram_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc);
+/** + * cik_init_microcode - load ucode images from disk + * + * @rdev: radeon_device pointer + * + * Use the firmware interface to load the ucode images into + * the driver (not loaded into hw). + * Returns 0 on success, error on failure. + */ +static int cik_init_microcode(struct radeon_device *rdev) +{ + struct platform_device *pdev; + const char *chip_name; + size_t pfp_req_size, me_req_size, ce_req_size, + mec_req_size, rlc_req_size, mc_req_size; + char fw_name[30]; + int err; + + DRM_DEBUG("\n"); + + pdev = platform_device_register_simple("radeon_cp", 0, NULL, 0); + err = IS_ERR(pdev); + if (err) { + printk(KERN_ERR "radeon_cp: Failed to register firmware\n"); + return -EINVAL; + } + + switch (rdev->family) { + case CHIP_BONAIRE: + chip_name = "BONAIRE"; + pfp_req_size = CIK_PFP_UCODE_SIZE * 4; + me_req_size = CIK_ME_UCODE_SIZE * 4; + ce_req_size = CIK_CE_UCODE_SIZE * 4; + mec_req_size = CIK_MEC_UCODE_SIZE * 4; + rlc_req_size = BONAIRE_RLC_UCODE_SIZE * 4; + mc_req_size = CIK_MC_UCODE_SIZE * 4; + break; + case CHIP_KAVERI: + chip_name = "KAVERI"; + pfp_req_size = CIK_PFP_UCODE_SIZE * 4; + me_req_size = CIK_ME_UCODE_SIZE * 4; + ce_req_size = CIK_CE_UCODE_SIZE * 4; + mec_req_size = CIK_MEC_UCODE_SIZE * 4; + rlc_req_size = KV_RLC_UCODE_SIZE * 4; + break; + case CHIP_KABINI: + chip_name = "KABINI"; + pfp_req_size = CIK_PFP_UCODE_SIZE * 4; + me_req_size = CIK_ME_UCODE_SIZE * 4; + ce_req_size = CIK_CE_UCODE_SIZE * 4; + mec_req_size = CIK_MEC_UCODE_SIZE * 4; + rlc_req_size = KB_RLC_UCODE_SIZE * 4; + break; + default: BUG(); + } + + DRM_INFO("Loading %s Microcode\n", chip_name); + + snprintf(fw_name, sizeof(fw_name), "radeon/%s_pfp.bin", chip_name); + err = request_firmware(&rdev->pfp_fw, fw_name, &pdev->dev); + if (err) + goto out; + if (rdev->pfp_fw->size != pfp_req_size) { + printk(KERN_ERR + "cik_cp: Bogus length %zu in firmware "%s"\n", + rdev->pfp_fw->size, fw_name); + err = -EINVAL; + goto out; + } + + snprintf(fw_name, sizeof(fw_name), "radeon/%s_me.bin", chip_name); + err = request_firmware(&rdev->me_fw, fw_name, &pdev->dev); + if (err) + goto out; + if (rdev->me_fw->size != me_req_size) { + printk(KERN_ERR + "cik_cp: Bogus length %zu in firmware "%s"\n", + rdev->me_fw->size, fw_name); + err = -EINVAL; + } + + snprintf(fw_name, sizeof(fw_name), "radeon/%s_ce.bin", chip_name); + err = request_firmware(&rdev->ce_fw, fw_name, &pdev->dev); + if (err) + goto out; + if (rdev->ce_fw->size != ce_req_size) { + printk(KERN_ERR + "cik_cp: Bogus length %zu in firmware "%s"\n", + rdev->ce_fw->size, fw_name); + err = -EINVAL; + } + + snprintf(fw_name, sizeof(fw_name), "radeon/%s_mec.bin", chip_name); + err = request_firmware(&rdev->mec_fw, fw_name, &pdev->dev); + if (err) + goto out; + if (rdev->mec_fw->size != mec_req_size) { + printk(KERN_ERR + "cik_cp: Bogus length %zu in firmware "%s"\n", + rdev->mec_fw->size, fw_name); + err = -EINVAL; + } + + snprintf(fw_name, sizeof(fw_name), "radeon/%s_rlc.bin", chip_name); + err = request_firmware(&rdev->rlc_fw, fw_name, &pdev->dev); + if (err) + goto out; + if (rdev->rlc_fw->size != rlc_req_size) { + printk(KERN_ERR + "cik_rlc: Bogus length %zu in firmware "%s"\n", + rdev->rlc_fw->size, fw_name); + err = -EINVAL; + } + + /* No MC ucode on APUs */ + if (!(rdev->flags & RADEON_IS_IGP)) { + snprintf(fw_name, sizeof(fw_name), "radeon/%s_mc.bin", chip_name); + err = request_firmware(&rdev->mc_fw, fw_name, &pdev->dev); + if (err) + goto out; + if (rdev->mc_fw->size != mc_req_size) { + printk(KERN_ERR + "cik_mc: Bogus length %zu in firmware "%s"\n", + rdev->mc_fw->size, fw_name); + err = -EINVAL; + } + } + +out: + platform_device_unregister(pdev); + + if (err) { + if (err != -EINVAL) + printk(KERN_ERR + "cik_cp: Failed to load firmware "%s"\n", + fw_name); + release_firmware(rdev->pfp_fw); + rdev->pfp_fw = NULL; + release_firmware(rdev->me_fw); + rdev->me_fw = NULL; + release_firmware(rdev->ce_fw); + rdev->ce_fw = NULL; + release_firmware(rdev->rlc_fw); + rdev->rlc_fw = NULL; + release_firmware(rdev->mc_fw); + rdev->mc_fw = NULL; + } + return err; +} + /* * Core functions */ diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index b1a2230..1c06c47 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1714,6 +1714,7 @@ struct radeon_device { const struct firmware *mc_fw; /* NI MC firmware */ const struct firmware *ce_fw; /* SI CE firmware */ const struct firmware *uvd_fw; /* UVD firmware */ + const struct firmware *mec_fw; /* CIK MEC firmware */ struct r600_blit r600_blit; struct r600_vram_scratch vram_scratch; int msi_enabled; /* msi enabled */
From: Alex Deucher alexander.deucher@amd.com
Load the GDDR5 ucode and train the links.
v2: update ucode
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/cik.c | 116 +++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/cikd.h | 16 ++++++ 2 files changed, 132 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 36e0fc9..8eec582 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -65,6 +65,122 @@ extern void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_sa extern void evergreen_mc_resume(struct radeon_device *rdev, struct evergreen_mc_save *save); extern void si_vram_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc);
+#define BONAIRE_IO_MC_REGS_SIZE 36 + +static const u32 bonaire_io_mc_regs[BONAIRE_IO_MC_REGS_SIZE][2] = +{ + {0x00000070, 0x04400000}, + {0x00000071, 0x80c01803}, + {0x00000072, 0x00004004}, + {0x00000073, 0x00000100}, + {0x00000074, 0x00ff0000}, + {0x00000075, 0x34000000}, + {0x00000076, 0x08000014}, + {0x00000077, 0x00cc08ec}, + {0x00000078, 0x00000400}, + {0x00000079, 0x00000000}, + {0x0000007a, 0x04090000}, + {0x0000007c, 0x00000000}, + {0x0000007e, 0x4408a8e8}, + {0x0000007f, 0x00000304}, + {0x00000080, 0x00000000}, + {0x00000082, 0x00000001}, + {0x00000083, 0x00000002}, + {0x00000084, 0xf3e4f400}, + {0x00000085, 0x052024e3}, + {0x00000087, 0x00000000}, + {0x00000088, 0x01000000}, + {0x0000008a, 0x1c0a0000}, + {0x0000008b, 0xff010000}, + {0x0000008d, 0xffffefff}, + {0x0000008e, 0xfff3efff}, + {0x0000008f, 0xfff3efbf}, + {0x00000092, 0xf7ffffff}, + {0x00000093, 0xffffff7f}, + {0x00000095, 0x00101101}, + {0x00000096, 0x00000fff}, + {0x00000097, 0x00116fff}, + {0x00000098, 0x60010000}, + {0x00000099, 0x10010000}, + {0x0000009a, 0x00006000}, + {0x0000009b, 0x00001000}, + {0x0000009f, 0x00b48000} +}; + +/* ucode loading */ +/** + * ci_mc_load_microcode - load MC ucode into the hw + * + * @rdev: radeon_device pointer + * + * Load the GDDR MC ucode into the hw (CIK). + * Returns 0 on success, error on failure. + */ +static int ci_mc_load_microcode(struct radeon_device *rdev) +{ + const __be32 *fw_data; + u32 running, blackout = 0; + u32 *io_mc_regs; + int i, ucode_size, regs_size; + + if (!rdev->mc_fw) + return -EINVAL; + + switch (rdev->family) { + case CHIP_BONAIRE: + default: + io_mc_regs = (u32 *)&bonaire_io_mc_regs; + ucode_size = CIK_MC_UCODE_SIZE; + regs_size = BONAIRE_IO_MC_REGS_SIZE; + break; + } + + running = RREG32(MC_SEQ_SUP_CNTL) & RUN_MASK; + + if (running == 0) { + if (running) { + blackout = RREG32(MC_SHARED_BLACKOUT_CNTL); + WREG32(MC_SHARED_BLACKOUT_CNTL, blackout | 1); + } + + /* reset the engine and set to writable */ + WREG32(MC_SEQ_SUP_CNTL, 0x00000008); + WREG32(MC_SEQ_SUP_CNTL, 0x00000010); + + /* load mc io regs */ + for (i = 0; i < regs_size; i++) { + WREG32(MC_SEQ_IO_DEBUG_INDEX, io_mc_regs[(i << 1)]); + WREG32(MC_SEQ_IO_DEBUG_DATA, io_mc_regs[(i << 1) + 1]); + } + /* load the MC ucode */ + fw_data = (const __be32 *)rdev->mc_fw->data; + for (i = 0; i < ucode_size; i++) + WREG32(MC_SEQ_SUP_PGM, be32_to_cpup(fw_data++)); + + /* put the engine back into the active state */ + WREG32(MC_SEQ_SUP_CNTL, 0x00000008); + WREG32(MC_SEQ_SUP_CNTL, 0x00000004); + WREG32(MC_SEQ_SUP_CNTL, 0x00000001); + + /* wait for training to complete */ + for (i = 0; i < rdev->usec_timeout; i++) { + if (RREG32(MC_SEQ_TRAIN_WAKEUP_CNTL) & TRAIN_DONE_D0) + break; + udelay(1); + } + for (i = 0; i < rdev->usec_timeout; i++) { + if (RREG32(MC_SEQ_TRAIN_WAKEUP_CNTL) & TRAIN_DONE_D1) + break; + udelay(1); + } + + if (running) + WREG32(MC_SHARED_BLACKOUT_CNTL, blackout); + } + + return 0; +} + /** * cik_init_microcode - load ucode images from disk * diff --git a/drivers/gpu/drm/radeon/cikd.h b/drivers/gpu/drm/radeon/cikd.h index 0dab9c5..2300ae0 100644 --- a/drivers/gpu/drm/radeon/cikd.h +++ b/drivers/gpu/drm/radeon/cikd.h @@ -139,6 +139,8 @@ #define ENABLE_ADVANCED_DRIVER_MODEL (1 << 6) #define MC_VM_FB_OFFSET 0x2068
+#define MC_SHARED_BLACKOUT_CNTL 0x20ac + #define MC_ARB_RAMCFG 0x2760 #define NOOFBANK_SHIFT 0 #define NOOFBANK_MASK 0x00000003 @@ -153,6 +155,20 @@ #define NOOFGROUPS_SHIFT 12 #define NOOFGROUPS_MASK 0x00001000
+#define MC_SEQ_SUP_CNTL 0x28c8 +#define RUN_MASK (1 << 0) +#define MC_SEQ_SUP_PGM 0x28cc + +#define MC_SEQ_TRAIN_WAKEUP_CNTL 0x28e8 +#define TRAIN_DONE_D0 (1 << 30) +#define TRAIN_DONE_D1 (1 << 31) + +#define MC_IO_PAD_CNTL_D0 0x29d0 +#define MEM_FALL_OUT_CMD (1 << 8) + +#define MC_SEQ_IO_DEBUG_INDEX 0x2a44 +#define MC_SEQ_IO_DEBUG_DATA 0x2a48 + #define HDP_HOST_PATH_CNTL 0x2C00 #define HDP_NONSURFACE_BASE 0x2C04 #define HDP_NONSURFACE_INFO 0x2C08
From: Alex Deucher alexander.deucher@amd.com
Sets up the GFX ring and loads ucode for GFX and Compute.
Todo: - handle compute queue setup.
v2: add documentation v3: integrate with latest reset changes v4: additional init fixes v5: scratch reg write back no longer supported on CIK v6: properly set CP_RB0_BASE_HI v7: rebase
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/Makefile | 2 +- drivers/gpu/drm/radeon/cik.c | 395 +++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/cik_blit_shaders.c | 246 ++++++++++++++++++ drivers/gpu/drm/radeon/cik_blit_shaders.h | 32 +++ drivers/gpu/drm/radeon/cikd.h | 222 ++++++++++++++++ drivers/gpu/drm/radeon/radeon_cs.c | 4 +- 6 files changed, 899 insertions(+), 2 deletions(-) create mode 100644 drivers/gpu/drm/radeon/cik_blit_shaders.c create mode 100644 drivers/gpu/drm/radeon/cik_blit_shaders.h
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index 88d0601..292fd25 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile @@ -76,7 +76,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \ evergreen.o evergreen_cs.o evergreen_blit_shaders.o evergreen_blit_kms.o \ evergreen_hdmi.o radeon_trace_points.o ni.o cayman_blit_shaders.o \ atombios_encoders.o radeon_semaphore.o radeon_sa.o atombios_i2c.o si.o \ - si_blit_shaders.o radeon_prime.o radeon_uvd.o cik.o + si_blit_shaders.o radeon_prime.o radeon_uvd.o cik.o cik_blit_shaders.o
radeon-$(CONFIG_COMPAT) += radeon_ioc32.o radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 8eec582..5712526 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -30,6 +30,7 @@ #include "radeon_asic.h" #include "cikd.h" #include "atom.h" +#include "cik_blit_shaders.h"
/* GFX */ #define CIK_PFP_UCODE_SIZE 2144 @@ -1491,6 +1492,400 @@ static void cik_gpu_init(struct radeon_device *rdev) udelay(50); }
+/* + * CP. + * On CIK, gfx and compute now have independant command processors. + * + * GFX + * Gfx consists of a single ring and can process both gfx jobs and + * compute jobs. The gfx CP consists of three microengines (ME): + * PFP - Pre-Fetch Parser + * ME - Micro Engine + * CE - Constant Engine + * The PFP and ME make up what is considered the Drawing Engine (DE). + * The CE is an asynchronous engine used for updating buffer desciptors + * used by the DE so that they can be loaded into cache in parallel + * while the DE is processing state update packets. + * + * Compute + * The compute CP consists of two microengines (ME): + * MEC1 - Compute MicroEngine 1 + * MEC2 - Compute MicroEngine 2 + * Each MEC supports 4 compute pipes and each pipe supports 8 queues. + * The queues are exposed to userspace and are programmed directly + * by the compute runtime. + */ +/** + * cik_cp_gfx_enable - enable/disable the gfx CP MEs + * + * @rdev: radeon_device pointer + * @enable: enable or disable the MEs + * + * Halts or unhalts the gfx MEs. + */ +static void cik_cp_gfx_enable(struct radeon_device *rdev, bool enable) +{ + if (enable) + WREG32(CP_ME_CNTL, 0); + else { + WREG32(CP_ME_CNTL, (CP_ME_HALT | CP_PFP_HALT | CP_CE_HALT)); + rdev->ring[RADEON_RING_TYPE_GFX_INDEX].ready = false; + } + udelay(50); +} + +/** + * cik_cp_gfx_load_microcode - load the gfx CP ME ucode + * + * @rdev: radeon_device pointer + * + * Loads the gfx PFP, ME, and CE ucode. + * Returns 0 for success, -EINVAL if the ucode is not available. + */ +static int cik_cp_gfx_load_microcode(struct radeon_device *rdev) +{ + const __be32 *fw_data; + int i; + + if (!rdev->me_fw || !rdev->pfp_fw || !rdev->ce_fw) + return -EINVAL; + + cik_cp_gfx_enable(rdev, false); + + /* PFP */ + fw_data = (const __be32 *)rdev->pfp_fw->data; + WREG32(CP_PFP_UCODE_ADDR, 0); + for (i = 0; i < CIK_PFP_UCODE_SIZE; i++) + WREG32(CP_PFP_UCODE_DATA, be32_to_cpup(fw_data++)); + WREG32(CP_PFP_UCODE_ADDR, 0); + + /* CE */ + fw_data = (const __be32 *)rdev->ce_fw->data; + WREG32(CP_CE_UCODE_ADDR, 0); + for (i = 0; i < CIK_CE_UCODE_SIZE; i++) + WREG32(CP_CE_UCODE_DATA, be32_to_cpup(fw_data++)); + WREG32(CP_CE_UCODE_ADDR, 0); + + /* ME */ + fw_data = (const __be32 *)rdev->me_fw->data; + WREG32(CP_ME_RAM_WADDR, 0); + for (i = 0; i < CIK_ME_UCODE_SIZE; i++) + WREG32(CP_ME_RAM_DATA, be32_to_cpup(fw_data++)); + WREG32(CP_ME_RAM_WADDR, 0); + + WREG32(CP_PFP_UCODE_ADDR, 0); + WREG32(CP_CE_UCODE_ADDR, 0); + WREG32(CP_ME_RAM_WADDR, 0); + WREG32(CP_ME_RAM_RADDR, 0); + return 0; +} + +/** + * cik_cp_gfx_start - start the gfx ring + * + * @rdev: radeon_device pointer + * + * Enables the ring and loads the clear state context and other + * packets required to init the ring. + * Returns 0 for success, error for failure. + */ +static int cik_cp_gfx_start(struct radeon_device *rdev) +{ + struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + int r, i; + + /* init the CP */ + WREG32(CP_MAX_CONTEXT, rdev->config.cik.max_hw_contexts - 1); + WREG32(CP_ENDIAN_SWAP, 0); + WREG32(CP_DEVICE_ID, 1); + + cik_cp_gfx_enable(rdev, true); + + r = radeon_ring_lock(rdev, ring, cik_default_size + 17); + if (r) { + DRM_ERROR("radeon: cp failed to lock ring (%d).\n", r); + return r; + } + + /* init the CE partitions. CE only used for gfx on CIK */ + radeon_ring_write(ring, PACKET3(PACKET3_SET_BASE, 2)); + radeon_ring_write(ring, PACKET3_BASE_INDEX(CE_PARTITION_BASE)); + radeon_ring_write(ring, 0xc000); + radeon_ring_write(ring, 0xc000); + + /* setup clear context state */ + radeon_ring_write(ring, PACKET3(PACKET3_PREAMBLE_CNTL, 0)); + radeon_ring_write(ring, PACKET3_PREAMBLE_BEGIN_CLEAR_STATE); + + radeon_ring_write(ring, PACKET3(PACKET3_CONTEXT_CONTROL, 1)); + radeon_ring_write(ring, 0x80000000); + radeon_ring_write(ring, 0x80000000); + + for (i = 0; i < cik_default_size; i++) + radeon_ring_write(ring, cik_default_state[i]); + + radeon_ring_write(ring, PACKET3(PACKET3_PREAMBLE_CNTL, 0)); + radeon_ring_write(ring, PACKET3_PREAMBLE_END_CLEAR_STATE); + + /* set clear context state */ + radeon_ring_write(ring, PACKET3(PACKET3_CLEAR_STATE, 0)); + radeon_ring_write(ring, 0); + + radeon_ring_write(ring, PACKET3(PACKET3_SET_CONTEXT_REG, 2)); + radeon_ring_write(ring, 0x00000316); + radeon_ring_write(ring, 0x0000000e); /* VGT_VERTEX_REUSE_BLOCK_CNTL */ + radeon_ring_write(ring, 0x00000010); /* VGT_OUT_DEALLOC_CNTL */ + + radeon_ring_unlock_commit(rdev, ring); + + return 0; +} + +/** + * cik_cp_gfx_fini - stop the gfx ring + * + * @rdev: radeon_device pointer + * + * Stop the gfx ring and tear down the driver ring + * info. + */ +static void cik_cp_gfx_fini(struct radeon_device *rdev) +{ + cik_cp_gfx_enable(rdev, false); + radeon_ring_fini(rdev, &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]); +} + +/** + * cik_cp_gfx_resume - setup the gfx ring buffer registers + * + * @rdev: radeon_device pointer + * + * Program the location and size of the gfx ring buffer + * and test it to make sure it's working. + * Returns 0 for success, error for failure. + */ +static int cik_cp_gfx_resume(struct radeon_device *rdev) +{ + struct radeon_ring *ring; + u32 tmp; + u32 rb_bufsz; + u64 rb_addr; + int r; + + WREG32(CP_SEM_WAIT_TIMER, 0x0); + WREG32(CP_SEM_INCOMPLETE_TIMER_CNTL, 0x0); + + /* Set the write pointer delay */ + WREG32(CP_RB_WPTR_DELAY, 0); + + /* set the RB to use vmid 0 */ + WREG32(CP_RB_VMID, 0); + + WREG32(SCRATCH_ADDR, ((rdev->wb.gpu_addr + RADEON_WB_SCRATCH_OFFSET) >> 8) & 0xFFFFFFFF); + + /* ring 0 - compute and gfx */ + /* Set ring buffer size */ + ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + rb_bufsz = drm_order(ring->ring_size / 8); + tmp = (drm_order(RADEON_GPU_PAGE_SIZE/8) << 8) | rb_bufsz; +#ifdef __BIG_ENDIAN + tmp |= BUF_SWAP_32BIT; +#endif + WREG32(CP_RB0_CNTL, tmp); + + /* Initialize the ring buffer's read and write pointers */ + WREG32(CP_RB0_CNTL, tmp | RB_RPTR_WR_ENA); + ring->wptr = 0; + WREG32(CP_RB0_WPTR, ring->wptr); + + /* set the wb address wether it's enabled or not */ + WREG32(CP_RB0_RPTR_ADDR, (rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFFFFFFFC); + WREG32(CP_RB0_RPTR_ADDR_HI, upper_32_bits(rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFF); + + /* scratch register shadowing is no longer supported */ + WREG32(SCRATCH_UMSK, 0); + + if (!rdev->wb.enabled) + tmp |= RB_NO_UPDATE; + + mdelay(1); + WREG32(CP_RB0_CNTL, tmp); + + rb_addr = ring->gpu_addr >> 8; + WREG32(CP_RB0_BASE, rb_addr); + WREG32(CP_RB0_BASE_HI, upper_32_bits(rb_addr)); + + ring->rptr = RREG32(CP_RB0_RPTR); + + /* start the ring */ + cik_cp_gfx_start(rdev); + rdev->ring[RADEON_RING_TYPE_GFX_INDEX].ready = true; + r = radeon_ring_test(rdev, RADEON_RING_TYPE_GFX_INDEX, &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]); + if (r) { + rdev->ring[RADEON_RING_TYPE_GFX_INDEX].ready = false; + return r; + } + return 0; +} + +/** + * cik_cp_compute_enable - enable/disable the compute CP MEs + * + * @rdev: radeon_device pointer + * @enable: enable or disable the MEs + * + * Halts or unhalts the compute MEs. + */ +static void cik_cp_compute_enable(struct radeon_device *rdev, bool enable) +{ + if (enable) + WREG32(CP_MEC_CNTL, 0); + else + WREG32(CP_MEC_CNTL, (MEC_ME1_HALT | MEC_ME2_HALT)); + udelay(50); +} + +/** + * cik_cp_compute_load_microcode - load the compute CP ME ucode + * + * @rdev: radeon_device pointer + * + * Loads the compute MEC1&2 ucode. + * Returns 0 for success, -EINVAL if the ucode is not available. + */ +static int cik_cp_compute_load_microcode(struct radeon_device *rdev) +{ + const __be32 *fw_data; + int i; + + if (!rdev->mec_fw) + return -EINVAL; + + cik_cp_compute_enable(rdev, false); + + /* MEC1 */ + fw_data = (const __be32 *)rdev->mec_fw->data; + WREG32(CP_MEC_ME1_UCODE_ADDR, 0); + for (i = 0; i < CIK_MEC_UCODE_SIZE; i++) + WREG32(CP_MEC_ME1_UCODE_DATA, be32_to_cpup(fw_data++)); + WREG32(CP_MEC_ME1_UCODE_ADDR, 0); + + if (rdev->family == CHIP_KAVERI) { + /* MEC2 */ + fw_data = (const __be32 *)rdev->mec_fw->data; + WREG32(CP_MEC_ME2_UCODE_ADDR, 0); + for (i = 0; i < CIK_MEC_UCODE_SIZE; i++) + WREG32(CP_MEC_ME2_UCODE_DATA, be32_to_cpup(fw_data++)); + WREG32(CP_MEC_ME2_UCODE_ADDR, 0); + } + + return 0; +} + +/** + * cik_cp_compute_start - start the compute queues + * + * @rdev: radeon_device pointer + * + * Enable the compute queues. + * Returns 0 for success, error for failure. + */ +static int cik_cp_compute_start(struct radeon_device *rdev) +{ + //todo + return 0; +} + +/** + * cik_cp_compute_fini - stop the compute queues + * + * @rdev: radeon_device pointer + * + * Stop the compute queues and tear down the driver queue + * info. + */ +static void cik_cp_compute_fini(struct radeon_device *rdev) +{ + cik_cp_compute_enable(rdev, false); + //todo +} + +/** + * cik_cp_compute_resume - setup the compute queue registers + * + * @rdev: radeon_device pointer + * + * Program the compute queues and test them to make sure they + * are working. + * Returns 0 for success, error for failure. + */ +static int cik_cp_compute_resume(struct radeon_device *rdev) +{ + int r; + + //todo + r = cik_cp_compute_start(rdev); + if (r) + return r; + return 0; +} + +/* XXX temporary wrappers to handle both compute and gfx */ +/* XXX */ +static void cik_cp_enable(struct radeon_device *rdev, bool enable) +{ + cik_cp_gfx_enable(rdev, enable); + cik_cp_compute_enable(rdev, enable); +} + +/* XXX */ +static int cik_cp_load_microcode(struct radeon_device *rdev) +{ + int r; + + r = cik_cp_gfx_load_microcode(rdev); + if (r) + return r; + r = cik_cp_compute_load_microcode(rdev); + if (r) + return r; + + return 0; +} + +/* XXX */ +static void cik_cp_fini(struct radeon_device *rdev) +{ + cik_cp_gfx_fini(rdev); + cik_cp_compute_fini(rdev); +} + +/* XXX */ +static int cik_cp_resume(struct radeon_device *rdev) +{ + int r; + + /* Reset all cp blocks */ + WREG32(GRBM_SOFT_RESET, SOFT_RESET_CP); + RREG32(GRBM_SOFT_RESET); + mdelay(15); + WREG32(GRBM_SOFT_RESET, 0); + RREG32(GRBM_SOFT_RESET); + + r = cik_cp_load_microcode(rdev); + if (r) + return r; + + r = cik_cp_gfx_resume(rdev); + if (r) + return r; + r = cik_cp_compute_resume(rdev); + if (r) + return r; + + return 0; +} + /** * cik_gpu_is_lockup - check if the 3D engine is locked up * diff --git a/drivers/gpu/drm/radeon/cik_blit_shaders.c b/drivers/gpu/drm/radeon/cik_blit_shaders.c new file mode 100644 index 0000000..ff13118 --- /dev/null +++ b/drivers/gpu/drm/radeon/cik_blit_shaders.c @@ -0,0 +1,246 @@ +/* + * Copyright 2012 Advanced Micro Devices, 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 (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Alex Deucher alexander.deucher@amd.com + */ + +#include <linux/types.h> +#include <linux/bug.h> +#include <linux/kernel.h> + +const u32 cik_default_state[] = +{ + 0xc0066900, + 0x00000000, + 0x00000060, /* DB_RENDER_CONTROL */ + 0x00000000, /* DB_COUNT_CONTROL */ + 0x00000000, /* DB_DEPTH_VIEW */ + 0x0000002a, /* DB_RENDER_OVERRIDE */ + 0x00000000, /* DB_RENDER_OVERRIDE2 */ + 0x00000000, /* DB_HTILE_DATA_BASE */ + + 0xc0046900, + 0x00000008, + 0x00000000, /* DB_DEPTH_BOUNDS_MIN */ + 0x00000000, /* DB_DEPTH_BOUNDS_MAX */ + 0x00000000, /* DB_STENCIL_CLEAR */ + 0x00000000, /* DB_DEPTH_CLEAR */ + + 0xc0036900, + 0x0000000f, + 0x00000000, /* DB_DEPTH_INFO */ + 0x00000000, /* DB_Z_INFO */ + 0x00000000, /* DB_STENCIL_INFO */ + + 0xc0016900, + 0x00000080, + 0x00000000, /* PA_SC_WINDOW_OFFSET */ + + 0xc00d6900, + 0x00000083, + 0x0000ffff, /* PA_SC_CLIPRECT_RULE */ + 0x00000000, /* PA_SC_CLIPRECT_0_TL */ + 0x20002000, /* PA_SC_CLIPRECT_0_BR */ + 0x00000000, + 0x20002000, + 0x00000000, + 0x20002000, + 0x00000000, + 0x20002000, + 0xaaaaaaaa, /* PA_SC_EDGERULE */ + 0x00000000, /* PA_SU_HARDWARE_SCREEN_OFFSET */ + 0x0000000f, /* CB_TARGET_MASK */ + 0x0000000f, /* CB_SHADER_MASK */ + + 0xc0226900, + 0x00000094, + 0x80000000, /* PA_SC_VPORT_SCISSOR_0_TL */ + 0x20002000, /* PA_SC_VPORT_SCISSOR_0_BR */ + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x80000000, + 0x20002000, + 0x00000000, /* PA_SC_VPORT_ZMIN_0 */ + 0x3f800000, /* PA_SC_VPORT_ZMAX_0 */ + + 0xc0046900, + 0x00000100, + 0xffffffff, /* VGT_MAX_VTX_INDX */ + 0x00000000, /* VGT_MIN_VTX_INDX */ + 0x00000000, /* VGT_INDX_OFFSET */ + 0x00000000, /* VGT_MULTI_PRIM_IB_RESET_INDX */ + + 0xc0046900, + 0x00000105, + 0x00000000, /* CB_BLEND_RED */ + 0x00000000, /* CB_BLEND_GREEN */ + 0x00000000, /* CB_BLEND_BLUE */ + 0x00000000, /* CB_BLEND_ALPHA */ + + 0xc0016900, + 0x000001e0, + 0x00000000, /* CB_BLEND0_CONTROL */ + + 0xc00c6900, + 0x00000200, + 0x00000000, /* DB_DEPTH_CONTROL */ + 0x00000000, /* DB_EQAA */ + 0x00cc0010, /* CB_COLOR_CONTROL */ + 0x00000210, /* DB_SHADER_CONTROL */ + 0x00010000, /* PA_CL_CLIP_CNTL */ + 0x00000004, /* PA_SU_SC_MODE_CNTL */ + 0x00000100, /* PA_CL_VTE_CNTL */ + 0x00000000, /* PA_CL_VS_OUT_CNTL */ + 0x00000000, /* PA_CL_NANINF_CNTL */ + 0x00000000, /* PA_SU_LINE_STIPPLE_CNTL */ + 0x00000000, /* PA_SU_LINE_STIPPLE_SCALE */ + 0x00000000, /* PA_SU_PRIM_FILTER_CNTL */ + + 0xc0116900, + 0x00000280, + 0x00000000, /* PA_SU_POINT_SIZE */ + 0x00000000, /* PA_SU_POINT_MINMAX */ + 0x00000008, /* PA_SU_LINE_CNTL */ + 0x00000000, /* PA_SC_LINE_STIPPLE */ + 0x00000000, /* VGT_OUTPUT_PATH_CNTL */ + 0x00000000, /* VGT_HOS_CNTL */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, /* VGT_GS_MODE */ + + 0xc0026900, + 0x00000292, + 0x00000000, /* PA_SC_MODE_CNTL_0 */ + 0x00000000, /* PA_SC_MODE_CNTL_1 */ + + 0xc0016900, + 0x000002a1, + 0x00000000, /* VGT_PRIMITIVEID_EN */ + + 0xc0016900, + 0x000002a5, + 0x00000000, /* VGT_MULTI_PRIM_IB_RESET_EN */ + + 0xc0026900, + 0x000002a8, + 0x00000000, /* VGT_INSTANCE_STEP_RATE_0 */ + 0x00000000, + + 0xc0026900, + 0x000002ad, + 0x00000000, /* VGT_REUSE_OFF */ + 0x00000000, + + 0xc0016900, + 0x000002d5, + 0x00000000, /* VGT_SHADER_STAGES_EN */ + + 0xc0016900, + 0x000002dc, + 0x0000aa00, /* DB_ALPHA_TO_MASK */ + + 0xc0066900, + 0x000002de, + 0x00000000, /* PA_SU_POLY_OFFSET_DB_FMT_CNTL */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + + 0xc0026900, + 0x000002e5, + 0x00000000, /* VGT_STRMOUT_CONFIG */ + 0x00000000, + + 0xc01b6900, + 0x000002f5, + 0x76543210, /* PA_SC_CENTROID_PRIORITY_0 */ + 0xfedcba98, /* PA_SC_CENTROID_PRIORITY_1 */ + 0x00000000, /* PA_SC_LINE_CNTL */ + 0x00000000, /* PA_SC_AA_CONFIG */ + 0x00000005, /* PA_SU_VTX_CNTL */ + 0x3f800000, /* PA_CL_GB_VERT_CLIP_ADJ */ + 0x3f800000, /* PA_CL_GB_VERT_DISC_ADJ */ + 0x3f800000, /* PA_CL_GB_HORZ_CLIP_ADJ */ + 0x3f800000, /* PA_CL_GB_HORZ_DISC_ADJ */ + 0x00000000, /* PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y0_0 */ + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0xffffffff, /* PA_SC_AA_MASK_X0Y0_X1Y0 */ + 0xffffffff, + + 0xc0026900, + 0x00000316, + 0x0000000e, /* VGT_VERTEX_REUSE_BLOCK_CNTL */ + 0x00000010, /* */ +}; + +const u32 cik_default_size = ARRAY_SIZE(cik_default_state); diff --git a/drivers/gpu/drm/radeon/cik_blit_shaders.h b/drivers/gpu/drm/radeon/cik_blit_shaders.h new file mode 100644 index 0000000..dfe7314 --- /dev/null +++ b/drivers/gpu/drm/radeon/cik_blit_shaders.h @@ -0,0 +1,32 @@ +/* + * Copyright 2012 Advanced Micro Devices, 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 (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef CIK_BLIT_SHADERS_H +#define CIK_BLIT_SHADERS_H + +extern const u32 cik_default_state[]; + +extern const u32 cik_default_size; + +#endif diff --git a/drivers/gpu/drm/radeon/cikd.h b/drivers/gpu/drm/radeon/cikd.h index 2300ae0..0d1a298 100644 --- a/drivers/gpu/drm/radeon/cikd.h +++ b/drivers/gpu/drm/radeon/cikd.h @@ -263,11 +263,18 @@ #define MEC_ME2_HALT (1 << 28) #define MEC_ME1_HALT (1 << 30)
+#define CP_MEC_CNTL 0x8234 +#define MEC_ME2_HALT (1 << 28) +#define MEC_ME1_HALT (1 << 30) + #define CP_ME_CNTL 0x86D8 #define CP_CE_HALT (1 << 24) #define CP_PFP_HALT (1 << 26) #define CP_ME_HALT (1 << 28)
+#define CP_RB0_RPTR 0x8700 +#define CP_RB_WPTR_DELAY 0x8704 + #define CP_MEQ_THRESHOLDS 0x8764 #define MEQ1_START(x) ((x) << 0) #define MEQ2_START(x) ((x) << 8) @@ -445,12 +452,62 @@ #define TC_CFG_L1_VOLATILE 0xAC88 #define TC_CFG_L2_VOLATILE 0xAC8C
+#define CP_RB0_BASE 0xC100 +#define CP_RB0_CNTL 0xC104 +#define RB_BUFSZ(x) ((x) << 0) +#define RB_BLKSZ(x) ((x) << 8) +#define BUF_SWAP_32BIT (2 << 16) +#define RB_NO_UPDATE (1 << 27) +#define RB_RPTR_WR_ENA (1 << 31) + +#define CP_RB0_RPTR_ADDR 0xC10C +#define RB_RPTR_SWAP_32BIT (2 << 0) +#define CP_RB0_RPTR_ADDR_HI 0xC110 +#define CP_RB0_WPTR 0xC114 + +#define CP_DEVICE_ID 0xC12C +#define CP_ENDIAN_SWAP 0xC140 +#define CP_RB_VMID 0xC144 + +#define CP_PFP_UCODE_ADDR 0xC150 +#define CP_PFP_UCODE_DATA 0xC154 +#define CP_ME_RAM_RADDR 0xC158 +#define CP_ME_RAM_WADDR 0xC15C +#define CP_ME_RAM_DATA 0xC160 + +#define CP_CE_UCODE_ADDR 0xC168 +#define CP_CE_UCODE_DATA 0xC16C +#define CP_MEC_ME1_UCODE_ADDR 0xC170 +#define CP_MEC_ME1_UCODE_DATA 0xC174 +#define CP_MEC_ME2_UCODE_ADDR 0xC178 +#define CP_MEC_ME2_UCODE_DATA 0xC17C + +#define CP_MAX_CONTEXT 0xC2B8 + +#define CP_RB0_BASE_HI 0xC2C4 + #define PA_SC_RASTER_CONFIG 0x28350 # define RASTER_CONFIG_RB_MAP_0 0 # define RASTER_CONFIG_RB_MAP_1 1 # define RASTER_CONFIG_RB_MAP_2 2 # define RASTER_CONFIG_RB_MAP_3 3
+#define SCRATCH_REG0 0x30100 +#define SCRATCH_REG1 0x30104 +#define SCRATCH_REG2 0x30108 +#define SCRATCH_REG3 0x3010C +#define SCRATCH_REG4 0x30110 +#define SCRATCH_REG5 0x30114 +#define SCRATCH_REG6 0x30118 +#define SCRATCH_REG7 0x3011C + +#define SCRATCH_UMSK 0x30140 +#define SCRATCH_ADDR 0x30144 + +#define CP_SEM_WAIT_TIMER 0x301BC + +#define CP_SEM_INCOMPLETE_TIMER_CNTL 0x301C8 + #define GRBM_GFX_INDEX 0x30800 #define INSTANCE_INDEX(x) ((x) << 0) #define SH_INDEX(x) ((x) << 8) @@ -482,4 +539,169 @@ #define TCC_DISABLE_MASK 0xFFFF0000 #define TCC_DISABLE_SHIFT 16
+/* + * PM4 + */ +#define PACKET_TYPE0 0 +#define PACKET_TYPE1 1 +#define PACKET_TYPE2 2 +#define PACKET_TYPE3 3 + +#define CP_PACKET_GET_TYPE(h) (((h) >> 30) & 3) +#define CP_PACKET_GET_COUNT(h) (((h) >> 16) & 0x3FFF) +#define CP_PACKET0_GET_REG(h) (((h) & 0xFFFF) << 2) +#define CP_PACKET3_GET_OPCODE(h) (((h) >> 8) & 0xFF) +#define PACKET0(reg, n) ((PACKET_TYPE0 << 30) | \ + (((reg) >> 2) & 0xFFFF) | \ + ((n) & 0x3FFF) << 16) +#define CP_PACKET2 0x80000000 +#define PACKET2_PAD_SHIFT 0 +#define PACKET2_PAD_MASK (0x3fffffff << 0) + +#define PACKET2(v) (CP_PACKET2 | REG_SET(PACKET2_PAD, (v))) + +#define PACKET3(op, n) ((PACKET_TYPE3 << 30) | \ + (((op) & 0xFF) << 8) | \ + ((n) & 0x3FFF) << 16) + +#define PACKET3_COMPUTE(op, n) (PACKET3(op, n) | 1 << 1) + +/* Packet 3 types */ +#define PACKET3_NOP 0x10 +#define PACKET3_SET_BASE 0x11 +#define PACKET3_BASE_INDEX(x) ((x) << 0) +#define CE_PARTITION_BASE 3 +#define PACKET3_CLEAR_STATE 0x12 +#define PACKET3_INDEX_BUFFER_SIZE 0x13 +#define PACKET3_DISPATCH_DIRECT 0x15 +#define PACKET3_DISPATCH_INDIRECT 0x16 +#define PACKET3_ATOMIC_GDS 0x1D +#define PACKET3_ATOMIC_MEM 0x1E +#define PACKET3_OCCLUSION_QUERY 0x1F +#define PACKET3_SET_PREDICATION 0x20 +#define PACKET3_REG_RMW 0x21 +#define PACKET3_COND_EXEC 0x22 +#define PACKET3_PRED_EXEC 0x23 +#define PACKET3_DRAW_INDIRECT 0x24 +#define PACKET3_DRAW_INDEX_INDIRECT 0x25 +#define PACKET3_INDEX_BASE 0x26 +#define PACKET3_DRAW_INDEX_2 0x27 +#define PACKET3_CONTEXT_CONTROL 0x28 +#define PACKET3_INDEX_TYPE 0x2A +#define PACKET3_DRAW_INDIRECT_MULTI 0x2C +#define PACKET3_DRAW_INDEX_AUTO 0x2D +#define PACKET3_NUM_INSTANCES 0x2F +#define PACKET3_DRAW_INDEX_MULTI_AUTO 0x30 +#define PACKET3_INDIRECT_BUFFER_CONST 0x33 +#define PACKET3_STRMOUT_BUFFER_UPDATE 0x34 +#define PACKET3_DRAW_INDEX_OFFSET_2 0x35 +#define PACKET3_DRAW_PREAMBLE 0x36 +#define PACKET3_WRITE_DATA 0x37 +#define PACKET3_DRAW_INDEX_INDIRECT_MULTI 0x38 +#define PACKET3_MEM_SEMAPHORE 0x39 +#define PACKET3_COPY_DW 0x3B +#define PACKET3_WAIT_REG_MEM 0x3C +#define PACKET3_INDIRECT_BUFFER 0x3F +#define PACKET3_COPY_DATA 0x40 +#define PACKET3_PFP_SYNC_ME 0x42 +#define PACKET3_SURFACE_SYNC 0x43 +# define PACKET3_DEST_BASE_0_ENA (1 << 0) +# define PACKET3_DEST_BASE_1_ENA (1 << 1) +# define PACKET3_CB0_DEST_BASE_ENA (1 << 6) +# define PACKET3_CB1_DEST_BASE_ENA (1 << 7) +# define PACKET3_CB2_DEST_BASE_ENA (1 << 8) +# define PACKET3_CB3_DEST_BASE_ENA (1 << 9) +# define PACKET3_CB4_DEST_BASE_ENA (1 << 10) +# define PACKET3_CB5_DEST_BASE_ENA (1 << 11) +# define PACKET3_CB6_DEST_BASE_ENA (1 << 12) +# define PACKET3_CB7_DEST_BASE_ENA (1 << 13) +# define PACKET3_DB_DEST_BASE_ENA (1 << 14) +# define PACKET3_TCL1_VOL_ACTION_ENA (1 << 15) +# define PACKET3_TC_VOL_ACTION_ENA (1 << 16) /* L2 */ +# define PACKET3_TC_WB_ACTION_ENA (1 << 18) /* L2 */ +# define PACKET3_DEST_BASE_2_ENA (1 << 19) +# define PACKET3_DEST_BASE_3_ENA (1 << 21) +# define PACKET3_TCL1_ACTION_ENA (1 << 22) +# define PACKET3_TC_ACTION_ENA (1 << 23) /* L2 */ +# define PACKET3_CB_ACTION_ENA (1 << 25) +# define PACKET3_DB_ACTION_ENA (1 << 26) +# define PACKET3_SH_KCACHE_ACTION_ENA (1 << 27) +# define PACKET3_SH_KCACHE_VOL_ACTION_ENA (1 << 28) +# define PACKET3_SH_ICACHE_ACTION_ENA (1 << 29) +#define PACKET3_COND_WRITE 0x45 +#define PACKET3_EVENT_WRITE 0x46 +#define EVENT_TYPE(x) ((x) << 0) +#define EVENT_INDEX(x) ((x) << 8) + /* 0 - any non-TS event + * 1 - ZPASS_DONE, PIXEL_PIPE_STAT_* + * 2 - SAMPLE_PIPELINESTAT + * 3 - SAMPLE_STREAMOUTSTAT* + * 4 - *S_PARTIAL_FLUSH + * 5 - EOP events + * 6 - EOS events + */ +#define PACKET3_EVENT_WRITE_EOP 0x47 +#define EOP_TCL1_VOL_ACTION_EN (1 << 12) +#define EOP_TC_VOL_ACTION_EN (1 << 13) /* L2 */ +#define EOP_TC_WB_ACTION_EN (1 << 15) /* L2 */ +#define EOP_TCL1_ACTION_EN (1 << 16) +#define EOP_TC_ACTION_EN (1 << 17) /* L2 */ +#define CACHE_POLICY(x) ((x) << 25) + /* 0 - LRU + * 1 - Stream + * 2 - Bypass + */ +#define TCL2_VOLATILE (1 << 27) +#define DATA_SEL(x) ((x) << 29) + /* 0 - discard + * 1 - send low 32bit data + * 2 - send 64bit data + * 3 - send 64bit GPU counter value + * 4 - send 64bit sys counter value + */ +#define INT_SEL(x) ((x) << 24) + /* 0 - none + * 1 - interrupt only (DATA_SEL = 0) + * 2 - interrupt when data write is confirmed + */ +#define DST_SEL(x) ((x) << 16) + /* 0 - MC + * 1 - TC/L2 + */ +#define PACKET3_EVENT_WRITE_EOS 0x48 +#define PACKET3_RELEASE_MEM 0x49 +#define PACKET3_PREAMBLE_CNTL 0x4A +# define PACKET3_PREAMBLE_BEGIN_CLEAR_STATE (2 << 28) +# define PACKET3_PREAMBLE_END_CLEAR_STATE (3 << 28) +#define PACKET3_DMA_DATA 0x50 +#define PACKET3_AQUIRE_MEM 0x58 +#define PACKET3_REWIND 0x59 +#define PACKET3_LOAD_UCONFIG_REG 0x5E +#define PACKET3_LOAD_SH_REG 0x5F +#define PACKET3_LOAD_CONFIG_REG 0x60 +#define PACKET3_LOAD_CONTEXT_REG 0x61 +#define PACKET3_SET_CONFIG_REG 0x68 +#define PACKET3_SET_CONFIG_REG_START 0x00008000 +#define PACKET3_SET_CONFIG_REG_END 0x0000b000 +#define PACKET3_SET_CONTEXT_REG 0x69 +#define PACKET3_SET_CONTEXT_REG_START 0x00028000 +#define PACKET3_SET_CONTEXT_REG_END 0x00029000 +#define PACKET3_SET_CONTEXT_REG_INDIRECT 0x73 +#define PACKET3_SET_SH_REG 0x76 +#define PACKET3_SET_SH_REG_START 0x0000b000 +#define PACKET3_SET_SH_REG_END 0x0000c000 +#define PACKET3_SET_SH_REG_OFFSET 0x77 +#define PACKET3_SET_QUEUE_REG 0x78 +#define PACKET3_SET_UCONFIG_REG 0x79 +#define PACKET3_SCRATCH_RAM_WRITE 0x7D +#define PACKET3_SCRATCH_RAM_READ 0x7E +#define PACKET3_LOAD_CONST_RAM 0x80 +#define PACKET3_WRITE_CONST_RAM 0x81 +#define PACKET3_DUMP_CONST_RAM 0x83 +#define PACKET3_INCREMENT_CE_COUNTER 0x84 +#define PACKET3_INCREMENT_DE_COUNTER 0x85 +#define PACKET3_WAIT_ON_CE_COUNTER 0x86 +#define PACKET3_WAIT_ON_DE_COUNTER_DIFF 0x88 + + #endif diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c index 7e265a5..cf71734 100644 --- a/drivers/gpu/drm/radeon/radeon_cs.c +++ b/drivers/gpu/drm/radeon/radeon_cs.c @@ -121,7 +121,9 @@ static int radeon_cs_get_ring(struct radeon_cs_parser *p, u32 ring, s32 priority p->ring = RADEON_RING_TYPE_GFX_INDEX; break; case RADEON_CS_RING_COMPUTE: - if (p->rdev->family >= CHIP_TAHITI) { + if (p->rdev->family >= CHIP_BONAIRE) + p->ring = RADEON_RING_TYPE_GFX_INDEX; + else if (p->rdev->family >= CHIP_TAHITI) { if (p->priority > 0) p->ring = CAYMAN_RING_TYPE_CP1_INDEX; else
From: Alex Deucher alexander.deucher@amd.com
For gfx ring only. Compute is still todo.
v2: add documentation v3: update to latest reset changes, integrate emit update patch. v4: fix count on wait_reg_mem for HDP flush v5: use old hdp flush method for fence v6: set valid bit for IB v7: cleanup for release
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/cik.c | 134 +++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/cikd.h | 120 +++++++++++++++++++++++++++++++++++- 2 files changed, 251 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 5712526..0b9c3c9 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -1493,6 +1493,140 @@ static void cik_gpu_init(struct radeon_device *rdev) }
/* + * GPU scratch registers helpers function. + */ +/** + * cik_scratch_init - setup driver info for CP scratch regs + * + * @rdev: radeon_device pointer + * + * Set up the number and offset of the CP scratch registers. + * NOTE: use of CP scratch registers is a legacy inferface and + * is not used by default on newer asics (r6xx+). On newer asics, + * memory buffers are used for fences rather than scratch regs. + */ +static void cik_scratch_init(struct radeon_device *rdev) +{ + int i; + + rdev->scratch.num_reg = 7; + rdev->scratch.reg_base = SCRATCH_REG0; + for (i = 0; i < rdev->scratch.num_reg; i++) { + rdev->scratch.free[i] = true; + rdev->scratch.reg[i] = rdev->scratch.reg_base + (i * 4); + } +} + +/** + * cik_fence_ring_emit - emit a fence on the gfx ring + * + * @rdev: radeon_device pointer + * @fence: radeon fence object + * + * Emits a fence sequnce number on the gfx ring and flushes + * GPU caches. + */ +void cik_fence_ring_emit(struct radeon_device *rdev, + struct radeon_fence *fence) +{ + struct radeon_ring *ring = &rdev->ring[fence->ring]; + u64 addr = rdev->fence_drv[fence->ring].gpu_addr; + + /* EVENT_WRITE_EOP - flush caches, send int */ + radeon_ring_write(ring, PACKET3(PACKET3_EVENT_WRITE_EOP, 4)); + radeon_ring_write(ring, (EOP_TCL1_ACTION_EN | + EOP_TC_ACTION_EN | + EVENT_TYPE(CACHE_FLUSH_AND_INV_TS_EVENT) | + EVENT_INDEX(5))); + radeon_ring_write(ring, addr & 0xfffffffc); + radeon_ring_write(ring, (upper_32_bits(addr) & 0xffff) | DATA_SEL(1) | INT_SEL(2)); + radeon_ring_write(ring, fence->seq); + radeon_ring_write(ring, 0); + /* HDP flush */ + /* We should be using the new WAIT_REG_MEM special op packet here + * but it causes the CP to hang + */ + radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); + radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) | + WRITE_DATA_DST_SEL(0))); + radeon_ring_write(ring, HDP_MEM_COHERENCY_FLUSH_CNTL >> 2); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 0); +} + +void cik_semaphore_ring_emit(struct radeon_device *rdev, + struct radeon_ring *ring, + struct radeon_semaphore *semaphore, + bool emit_wait) +{ + uint64_t addr = semaphore->gpu_addr; + unsigned sel = emit_wait ? PACKET3_SEM_SEL_WAIT : PACKET3_SEM_SEL_SIGNAL; + + radeon_ring_write(ring, PACKET3(PACKET3_MEM_SEMAPHORE, 1)); + radeon_ring_write(ring, addr & 0xffffffff); + radeon_ring_write(ring, (upper_32_bits(addr) & 0xffff) | sel); +} + +/* + * IB stuff + */ +/** + * cik_ring_ib_execute - emit an IB (Indirect Buffer) on the gfx ring + * + * @rdev: radeon_device pointer + * @ib: radeon indirect buffer object + * + * Emits an DE (drawing engine) or CE (constant engine) IB + * on the gfx ring. IBs are usually generated by userspace + * acceleration drivers and submitted to the kernel for + * sheduling on the ring. This function schedules the IB + * on the gfx ring for execution by the GPU. + */ +void cik_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib) +{ + struct radeon_ring *ring = &rdev->ring[ib->ring]; + u32 header, control = INDIRECT_BUFFER_VALID; + + if (ib->is_const_ib) { + /* set switch buffer packet before const IB */ + radeon_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0)); + radeon_ring_write(ring, 0); + + header = PACKET3(PACKET3_INDIRECT_BUFFER_CONST, 2); + } else { + u32 next_rptr; + if (ring->rptr_save_reg) { + next_rptr = ring->wptr + 3 + 4; + radeon_ring_write(ring, PACKET3(PACKET3_SET_UCONFIG_REG, 1)); + radeon_ring_write(ring, ((ring->rptr_save_reg - + PACKET3_SET_UCONFIG_REG_START) >> 2)); + radeon_ring_write(ring, next_rptr); + } else if (rdev->wb.enabled) { + next_rptr = ring->wptr + 5 + 4; + radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); + radeon_ring_write(ring, WRITE_DATA_DST_SEL(1)); + radeon_ring_write(ring, ring->next_rptr_gpu_addr & 0xfffffffc); + radeon_ring_write(ring, upper_32_bits(ring->next_rptr_gpu_addr) & 0xffffffff); + radeon_ring_write(ring, next_rptr); + } + + header = PACKET3(PACKET3_INDIRECT_BUFFER, 2); + } + + control |= ib->length_dw | + (ib->vm ? (ib->vm->id << 24) : 0); + + radeon_ring_write(ring, header); + radeon_ring_write(ring, +#ifdef __BIG_ENDIAN + (2 << 0) | +#endif + (ib->gpu_addr & 0xFFFFFFFC)); + radeon_ring_write(ring, upper_32_bits(ib->gpu_addr) & 0xFFFF); + radeon_ring_write(ring, control); +} + +/* * CP. * On CIK, gfx and compute now have independant command processors. * diff --git a/drivers/gpu/drm/radeon/cikd.h b/drivers/gpu/drm/radeon/cikd.h index 0d1a298..783cf60 100644 --- a/drivers/gpu/drm/radeon/cikd.h +++ b/drivers/gpu/drm/radeon/cikd.h @@ -188,6 +188,21 @@
#define HDP_REG_COHERENCY_FLUSH_CNTL 0x54A0
+#define GPU_HDP_FLUSH_REQ 0x54DC +#define GPU_HDP_FLUSH_DONE 0x54E0 +#define CP0 (1 << 0) +#define CP1 (1 << 1) +#define CP2 (1 << 2) +#define CP3 (1 << 3) +#define CP4 (1 << 4) +#define CP5 (1 << 5) +#define CP6 (1 << 6) +#define CP7 (1 << 7) +#define CP8 (1 << 8) +#define CP9 (1 << 9) +#define SDMA0 (1 << 10) +#define SDMA1 (1 << 11) + #define GRBM_CNTL 0x8000 #define GRBM_READ_TIMEOUT(x) ((x) << 0)
@@ -492,6 +507,49 @@ # define RASTER_CONFIG_RB_MAP_2 2 # define RASTER_CONFIG_RB_MAP_3 3
+#define VGT_EVENT_INITIATOR 0x28a90 +# define SAMPLE_STREAMOUTSTATS1 (1 << 0) +# define SAMPLE_STREAMOUTSTATS2 (2 << 0) +# define SAMPLE_STREAMOUTSTATS3 (3 << 0) +# define CACHE_FLUSH_TS (4 << 0) +# define CACHE_FLUSH (6 << 0) +# define CS_PARTIAL_FLUSH (7 << 0) +# define VGT_STREAMOUT_RESET (10 << 0) +# define END_OF_PIPE_INCR_DE (11 << 0) +# define END_OF_PIPE_IB_END (12 << 0) +# define RST_PIX_CNT (13 << 0) +# define VS_PARTIAL_FLUSH (15 << 0) +# define PS_PARTIAL_FLUSH (16 << 0) +# define CACHE_FLUSH_AND_INV_TS_EVENT (20 << 0) +# define ZPASS_DONE (21 << 0) +# define CACHE_FLUSH_AND_INV_EVENT (22 << 0) +# define PERFCOUNTER_START (23 << 0) +# define PERFCOUNTER_STOP (24 << 0) +# define PIPELINESTAT_START (25 << 0) +# define PIPELINESTAT_STOP (26 << 0) +# define PERFCOUNTER_SAMPLE (27 << 0) +# define SAMPLE_PIPELINESTAT (30 << 0) +# define SO_VGT_STREAMOUT_FLUSH (31 << 0) +# define SAMPLE_STREAMOUTSTATS (32 << 0) +# define RESET_VTX_CNT (33 << 0) +# define VGT_FLUSH (36 << 0) +# define BOTTOM_OF_PIPE_TS (40 << 0) +# define DB_CACHE_FLUSH_AND_INV (42 << 0) +# define FLUSH_AND_INV_DB_DATA_TS (43 << 0) +# define FLUSH_AND_INV_DB_META (44 << 0) +# define FLUSH_AND_INV_CB_DATA_TS (45 << 0) +# define FLUSH_AND_INV_CB_META (46 << 0) +# define CS_DONE (47 << 0) +# define PS_DONE (48 << 0) +# define FLUSH_AND_INV_CB_PIXEL_DATA (49 << 0) +# define THREAD_TRACE_START (51 << 0) +# define THREAD_TRACE_STOP (52 << 0) +# define THREAD_TRACE_FLUSH (54 << 0) +# define THREAD_TRACE_FINISH (55 << 0) +# define PIXEL_PIPE_STAT_CONTROL (56 << 0) +# define PIXEL_PIPE_STAT_DUMP (57 << 0) +# define PIXEL_PIPE_STAT_RESET (58 << 0) + #define SCRATCH_REG0 0x30100 #define SCRATCH_REG1 0x30104 #define SCRATCH_REG2 0x30108 @@ -508,6 +566,8 @@
#define CP_SEM_INCOMPLETE_TIMER_CNTL 0x301C8
+#define CP_WAIT_REG_MEM_TIMEOUT 0x301D0 + #define GRBM_GFX_INDEX 0x30800 #define INSTANCE_INDEX(x) ((x) << 0) #define SH_INDEX(x) ((x) << 8) @@ -597,11 +657,63 @@ #define PACKET3_DRAW_INDEX_OFFSET_2 0x35 #define PACKET3_DRAW_PREAMBLE 0x36 #define PACKET3_WRITE_DATA 0x37 +#define WRITE_DATA_DST_SEL(x) ((x) << 8) + /* 0 - register + * 1 - memory (sync - via GRBM) + * 2 - gl2 + * 3 - gds + * 4 - reserved + * 5 - memory (async - direct) + */ +#define WR_ONE_ADDR (1 << 16) +#define WR_CONFIRM (1 << 20) +#define WRITE_DATA_CACHE_POLICY(x) ((x) << 25) + /* 0 - LRU + * 1 - Stream + */ +#define WRITE_DATA_ENGINE_SEL(x) ((x) << 30) + /* 0 - me + * 1 - pfp + * 2 - ce + */ #define PACKET3_DRAW_INDEX_INDIRECT_MULTI 0x38 #define PACKET3_MEM_SEMAPHORE 0x39 +# define PACKET3_SEM_USE_MAILBOX (0x1 << 16) +# define PACKET3_SEM_SEL_SIGNAL_TYPE (0x1 << 20) /* 0 = increment, 1 = write 1 */ +# define PACKET3_SEM_CLIENT_CODE ((x) << 24) /* 0 = CP, 1 = CB, 2 = DB */ +# define PACKET3_SEM_SEL_SIGNAL (0x6 << 29) +# define PACKET3_SEM_SEL_WAIT (0x7 << 29) #define PACKET3_COPY_DW 0x3B #define PACKET3_WAIT_REG_MEM 0x3C +#define WAIT_REG_MEM_FUNCTION(x) ((x) << 0) + /* 0 - always + * 1 - < + * 2 - <= + * 3 - == + * 4 - != + * 5 - >= + * 6 - > + */ +#define WAIT_REG_MEM_MEM_SPACE(x) ((x) << 4) + /* 0 - reg + * 1 - mem + */ +#define WAIT_REG_MEM_OPERATION(x) ((x) << 6) + /* 0 - wait_reg_mem + * 1 - wr_wait_wr_reg + */ +#define WAIT_REG_MEM_ENGINE(x) ((x) << 8) + /* 0 - me + * 1 - pfp + */ #define PACKET3_INDIRECT_BUFFER 0x3F +#define INDIRECT_BUFFER_TCL2_VOLATILE (1 << 22) +#define INDIRECT_BUFFER_VALID (1 << 23) +#define INDIRECT_BUFFER_CACHE_POLICY(x) ((x) << 28) + /* 0 - LRU + * 1 - Stream + * 2 - Bypass + */ #define PACKET3_COPY_DATA 0x40 #define PACKET3_PFP_SYNC_ME 0x42 #define PACKET3_SURFACE_SYNC 0x43 @@ -646,12 +758,12 @@ #define EOP_TC_WB_ACTION_EN (1 << 15) /* L2 */ #define EOP_TCL1_ACTION_EN (1 << 16) #define EOP_TC_ACTION_EN (1 << 17) /* L2 */ -#define CACHE_POLICY(x) ((x) << 25) +#define EOP_CACHE_POLICY(x) ((x) << 25) /* 0 - LRU * 1 - Stream * 2 - Bypass */ -#define TCL2_VOLATILE (1 << 27) +#define EOP_TCL2_VOLATILE (1 << 27) #define DATA_SEL(x) ((x) << 29) /* 0 - discard * 1 - send low 32bit data @@ -693,6 +805,8 @@ #define PACKET3_SET_SH_REG_OFFSET 0x77 #define PACKET3_SET_QUEUE_REG 0x78 #define PACKET3_SET_UCONFIG_REG 0x79 +#define PACKET3_SET_UCONFIG_REG_START 0x00030000 +#define PACKET3_SET_UCONFIG_REG_END 0x00031000 #define PACKET3_SCRATCH_RAM_WRITE 0x7D #define PACKET3_SCRATCH_RAM_READ 0x7E #define PACKET3_LOAD_CONST_RAM 0x80 @@ -702,6 +816,6 @@ #define PACKET3_INCREMENT_DE_COUNTER 0x85 #define PACKET3_WAIT_ON_CE_COUNTER 0x86 #define PACKET3_WAIT_ON_DE_COUNTER_DIFF 0x88 - +#define PACKET3_SWITCH_BUFFER 0x8B
#endif
From: Alex Deucher alexander.deucher@amd.com
v2: add documenation v3: update the latest ib changes
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/cik.c | 114 ++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 114 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 0b9c3c9..0cf04f3 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -1518,6 +1518,57 @@ static void cik_scratch_init(struct radeon_device *rdev) }
/** + * cik_ring_test - basic gfx ring test + * + * @rdev: radeon_device pointer + * @ring: radeon_ring structure holding ring information + * + * Allocate a scratch register and write to it using the gfx ring (CIK). + * Provides a basic gfx ring test to verify that the ring is working. + * Used by cik_cp_gfx_resume(); + * Returns 0 on success, error on failure. + */ +int cik_ring_test(struct radeon_device *rdev, struct radeon_ring *ring) +{ + uint32_t scratch; + uint32_t tmp = 0; + unsigned i; + int r; + + r = radeon_scratch_get(rdev, &scratch); + if (r) { + DRM_ERROR("radeon: cp failed to get scratch reg (%d).\n", r); + return r; + } + WREG32(scratch, 0xCAFEDEAD); + r = radeon_ring_lock(rdev, ring, 3); + if (r) { + DRM_ERROR("radeon: cp failed to lock ring %d (%d).\n", ring->idx, r); + radeon_scratch_free(rdev, scratch); + return r; + } + radeon_ring_write(ring, PACKET3(PACKET3_SET_UCONFIG_REG, 1)); + radeon_ring_write(ring, ((scratch - PACKET3_SET_UCONFIG_REG_START) >> 2)); + radeon_ring_write(ring, 0xDEADBEEF); + radeon_ring_unlock_commit(rdev, ring); + for (i = 0; i < rdev->usec_timeout; i++) { + tmp = RREG32(scratch); + if (tmp == 0xDEADBEEF) + break; + DRM_UDELAY(1); + } + if (i < rdev->usec_timeout) { + DRM_INFO("ring test on %d succeeded in %d usecs\n", ring->idx, i); + } else { + DRM_ERROR("radeon: ring %d test failed (scratch(0x%04X)=0x%08X)\n", + ring->idx, scratch, tmp); + r = -EINVAL; + } + radeon_scratch_free(rdev, scratch); + return r; +} + +/** * cik_fence_ring_emit - emit a fence on the gfx ring * * @rdev: radeon_device pointer @@ -1626,6 +1677,69 @@ void cik_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib) radeon_ring_write(ring, control); }
+/** + * cik_ib_test - basic gfx ring IB test + * + * @rdev: radeon_device pointer + * @ring: radeon_ring structure holding ring information + * + * Allocate an IB and execute it on the gfx ring (CIK). + * Provides a basic gfx ring test to verify that IBs are working. + * Returns 0 on success, error on failure. + */ +int cik_ib_test(struct radeon_device *rdev, struct radeon_ring *ring) +{ + struct radeon_ib ib; + uint32_t scratch; + uint32_t tmp = 0; + unsigned i; + int r; + + r = radeon_scratch_get(rdev, &scratch); + if (r) { + DRM_ERROR("radeon: failed to get scratch reg (%d).\n", r); + return r; + } + WREG32(scratch, 0xCAFEDEAD); + r = radeon_ib_get(rdev, ring->idx, &ib, NULL, 256); + if (r) { + DRM_ERROR("radeon: failed to get ib (%d).\n", r); + return r; + } + ib.ptr[0] = PACKET3(PACKET3_SET_UCONFIG_REG, 1); + ib.ptr[1] = ((scratch - PACKET3_SET_UCONFIG_REG_START) >> 2); + ib.ptr[2] = 0xDEADBEEF; + ib.length_dw = 3; + r = radeon_ib_schedule(rdev, &ib, NULL); + if (r) { + radeon_scratch_free(rdev, scratch); + radeon_ib_free(rdev, &ib); + DRM_ERROR("radeon: failed to schedule ib (%d).\n", r); + return r; + } + r = radeon_fence_wait(ib.fence, false); + if (r) { + DRM_ERROR("radeon: fence wait failed (%d).\n", r); + return r; + } + for (i = 0; i < rdev->usec_timeout; i++) { + tmp = RREG32(scratch); + if (tmp == 0xDEADBEEF) + break; + DRM_UDELAY(1); + } + if (i < rdev->usec_timeout) { + DRM_INFO("ib test on ring %d succeeded in %u usecs\n", ib.fence->ring, i); + } else { + DRM_ERROR("radeon: ib test failed (scratch(0x%04X)=0x%08X)\n", + scratch, tmp); + r = -EINVAL; + } + radeon_scratch_free(rdev, scratch); + radeon_ib_free(rdev, &ib); + return r; +} + /* * CP. * On CIK, gfx and compute now have independant command processors.
From: Alex Deucher alexander.deucher@amd.com
Update the page table base address and flush the VM TLB using the CP.
v2: update for 2 level PTs v3: use new packet for invalidate v4: update SH_MEM* regs when flushing the VM v5: add pfp sync, go back to old style vm TLB invalidate v6: fix hdp flush packet count v7: use old style HDP flush
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/cik.c | 79 ++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 79 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 0cf04f3..ba242eb 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -2698,3 +2698,82 @@ void cik_vm_fini(struct radeon_device *rdev) { }
+/** + * cik_vm_flush - cik vm flush using the CP + * + * @rdev: radeon_device pointer + * + * Update the page table base and flush the VM TLB + * using the CP (CIK). + */ +void cik_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm) +{ + struct radeon_ring *ring = &rdev->ring[ridx]; + + if (vm == NULL) + return; + + radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); + radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) | + WRITE_DATA_DST_SEL(0))); + if (vm->id < 8) { + radeon_ring_write(ring, + (VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + (vm->id << 2)) >> 2); + } else { + radeon_ring_write(ring, + (VM_CONTEXT8_PAGE_TABLE_BASE_ADDR + ((vm->id - 8) << 2)) >> 2); + } + radeon_ring_write(ring, 0); + radeon_ring_write(ring, vm->pd_gpu_addr >> 12); + + /* update SH_MEM_* regs */ + radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); + radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) | + WRITE_DATA_DST_SEL(0))); + radeon_ring_write(ring, SRBM_GFX_CNTL >> 2); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, VMID(vm->id)); + + radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 6)); + radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) | + WRITE_DATA_DST_SEL(0))); + radeon_ring_write(ring, SH_MEM_BASES >> 2); + radeon_ring_write(ring, 0); + + radeon_ring_write(ring, 0); /* SH_MEM_BASES */ + radeon_ring_write(ring, 0); /* SH_MEM_CONFIG */ + radeon_ring_write(ring, 1); /* SH_MEM_APE1_BASE */ + radeon_ring_write(ring, 0); /* SH_MEM_APE1_LIMIT */ + + radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); + radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) | + WRITE_DATA_DST_SEL(0))); + radeon_ring_write(ring, SRBM_GFX_CNTL >> 2); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, VMID(0)); + + /* HDP flush */ + /* We should be using the WAIT_REG_MEM packet here like in + * cik_fence_ring_emit(), but it causes the CP to hang in this + * context... + */ + radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); + radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) | + WRITE_DATA_DST_SEL(0))); + radeon_ring_write(ring, HDP_MEM_COHERENCY_FLUSH_CNTL >> 2); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 0); + + /* bits 0-15 are the VM contexts0-15 */ + radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); + radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) | + WRITE_DATA_DST_SEL(0))); + radeon_ring_write(ring, VM_INVALIDATE_REQUEST >> 2); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 1 << vm->id); + + /* sync PFP to ME, otherwise we might get invalid PFP reads */ + radeon_ring_write(ring, PACKET3(PACKET3_PFP_SYNC_ME, 0)); + radeon_ring_write(ring, 0x0); +} +
From: Alex Deucher alexander.deucher@amd.com
RLC handles the interrupt controller and other tasks on the GPU.
v2: add documentation v3: update programming sequence v4: additional setup
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/cik.c | 142 +++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/cikd.h | 47 ++++++++++++++ 2 files changed, 189 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index ba242eb..a44ede6 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -2777,3 +2777,145 @@ void cik_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm) radeon_ring_write(ring, 0x0); }
+/* + * RLC + * The RLC is a multi-purpose microengine that handles a + * variety of functions, the most important of which is + * the interrupt controller. + */ +/** + * cik_rlc_stop - stop the RLC ME + * + * @rdev: radeon_device pointer + * + * Halt the RLC ME (MicroEngine) (CIK). + */ +static void cik_rlc_stop(struct radeon_device *rdev) +{ + int i, j, k; + u32 mask, tmp; + + tmp = RREG32(CP_INT_CNTL_RING0); + tmp &= ~(CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE); + WREG32(CP_INT_CNTL_RING0, tmp); + + RREG32(CB_CGTT_SCLK_CTRL); + RREG32(CB_CGTT_SCLK_CTRL); + RREG32(CB_CGTT_SCLK_CTRL); + RREG32(CB_CGTT_SCLK_CTRL); + + tmp = RREG32(RLC_CGCG_CGLS_CTRL) & 0xfffffffc; + WREG32(RLC_CGCG_CGLS_CTRL, tmp); + + WREG32(RLC_CNTL, 0); + + for (i = 0; i < rdev->config.cik.max_shader_engines; i++) { + for (j = 0; j < rdev->config.cik.max_sh_per_se; j++) { + cik_select_se_sh(rdev, i, j); + for (k = 0; k < rdev->usec_timeout; k++) { + if (RREG32(RLC_SERDES_CU_MASTER_BUSY) == 0) + break; + udelay(1); + } + } + } + cik_select_se_sh(rdev, 0xffffffff, 0xffffffff); + + mask = SE_MASTER_BUSY_MASK | GC_MASTER_BUSY | TC0_MASTER_BUSY | TC1_MASTER_BUSY; + for (k = 0; k < rdev->usec_timeout; k++) { + if ((RREG32(RLC_SERDES_NONCU_MASTER_BUSY) & mask) == 0) + break; + udelay(1); + } +} + +/** + * cik_rlc_start - start the RLC ME + * + * @rdev: radeon_device pointer + * + * Unhalt the RLC ME (MicroEngine) (CIK). + */ +static void cik_rlc_start(struct radeon_device *rdev) +{ + u32 tmp; + + WREG32(RLC_CNTL, RLC_ENABLE); + + tmp = RREG32(CP_INT_CNTL_RING0); + tmp |= (CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE); + WREG32(CP_INT_CNTL_RING0, tmp); + + udelay(50); +} + +/** + * cik_rlc_resume - setup the RLC hw + * + * @rdev: radeon_device pointer + * + * Initialize the RLC registers, load the ucode, + * and start the RLC (CIK). + * Returns 0 for success, -EINVAL if the ucode is not available. + */ +static int cik_rlc_resume(struct radeon_device *rdev) +{ + u32 i, size; + u32 clear_state_info[3]; + const __be32 *fw_data; + + if (!rdev->rlc_fw) + return -EINVAL; + + switch (rdev->family) { + case CHIP_BONAIRE: + default: + size = BONAIRE_RLC_UCODE_SIZE; + break; + case CHIP_KAVERI: + size = KV_RLC_UCODE_SIZE; + break; + case CHIP_KABINI: + size = KB_RLC_UCODE_SIZE; + break; + } + + cik_rlc_stop(rdev); + + WREG32(GRBM_SOFT_RESET, SOFT_RESET_RLC); + RREG32(GRBM_SOFT_RESET); + udelay(50); + WREG32(GRBM_SOFT_RESET, 0); + RREG32(GRBM_SOFT_RESET); + udelay(50); + + WREG32(RLC_LB_CNTR_INIT, 0); + WREG32(RLC_LB_CNTR_MAX, 0x00008000); + + cik_select_se_sh(rdev, 0xffffffff, 0xffffffff); + WREG32(RLC_LB_INIT_CU_MASK, 0xffffffff); + WREG32(RLC_LB_PARAMS, 0x00600408); + WREG32(RLC_LB_CNTL, 0x80000004); + + WREG32(RLC_MC_CNTL, 0); + WREG32(RLC_UCODE_CNTL, 0); + + fw_data = (const __be32 *)rdev->rlc_fw->data; + WREG32(RLC_GPM_UCODE_ADDR, 0); + for (i = 0; i < size; i++) + WREG32(RLC_GPM_UCODE_DATA, be32_to_cpup(fw_data++)); + WREG32(RLC_GPM_UCODE_ADDR, 0); + + /* XXX */ + clear_state_info[0] = 0;//upper_32_bits(rdev->rlc.save_restore_gpu_addr); + clear_state_info[1] = 0;//rdev->rlc.save_restore_gpu_addr; + clear_state_info[2] = 0;//cik_default_size; + WREG32(RLC_GPM_SCRATCH_ADDR, 0x3d); + for (i = 0; i < 3; i++) + WREG32(RLC_GPM_SCRATCH_DATA, clear_state_info[i]); + WREG32(RLC_DRIVER_DMA_STATUS, 0); + + cik_rlc_start(rdev); + + return 0; +} diff --git a/drivers/gpu/drm/radeon/cikd.h b/drivers/gpu/drm/radeon/cikd.h index 783cf60..a116020 100644 --- a/drivers/gpu/drm/radeon/cikd.h +++ b/drivers/gpu/drm/radeon/cikd.h @@ -497,10 +497,55 @@ #define CP_MEC_ME2_UCODE_ADDR 0xC178 #define CP_MEC_ME2_UCODE_DATA 0xC17C
+#define CP_INT_CNTL_RING0 0xC1A8 +# define CNTX_BUSY_INT_ENABLE (1 << 19) +# define CNTX_EMPTY_INT_ENABLE (1 << 20) +# define PRIV_INSTR_INT_ENABLE (1 << 22) +# define PRIV_REG_INT_ENABLE (1 << 23) +# define TIME_STAMP_INT_ENABLE (1 << 26) +# define CP_RINGID2_INT_ENABLE (1 << 29) +# define CP_RINGID1_INT_ENABLE (1 << 30) +# define CP_RINGID0_INT_ENABLE (1 << 31) + #define CP_MAX_CONTEXT 0xC2B8
#define CP_RB0_BASE_HI 0xC2C4
+#define RLC_CNTL 0xC300 +# define RLC_ENABLE (1 << 0) + +#define RLC_MC_CNTL 0xC30C + +#define RLC_LB_CNTR_MAX 0xC348 + +#define RLC_LB_CNTL 0xC364 + +#define RLC_LB_CNTR_INIT 0xC36C + +#define RLC_SAVE_AND_RESTORE_BASE 0xC374 +#define RLC_DRIVER_DMA_STATUS 0xC378 + +#define RLC_GPM_UCODE_ADDR 0xC388 +#define RLC_GPM_UCODE_DATA 0xC38C + +#define RLC_UCODE_CNTL 0xC39C + +#define RLC_CGCG_CGLS_CTRL 0xC424 + +#define RLC_LB_INIT_CU_MASK 0xC43C + +#define RLC_LB_PARAMS 0xC444 + +#define RLC_SERDES_CU_MASTER_BUSY 0xC484 +#define RLC_SERDES_NONCU_MASTER_BUSY 0xC488 +# define SE_MASTER_BUSY_MASK 0x0000ffff +# define GC_MASTER_BUSY (1 << 16) +# define TC0_MASTER_BUSY (1 << 17) +# define TC1_MASTER_BUSY (1 << 18) + +#define RLC_GPM_SCRATCH_ADDR 0xC4B0 +#define RLC_GPM_SCRATCH_DATA 0xC4B4 + #define PA_SC_RASTER_CONFIG 0x28350 # define RASTER_CONFIG_RB_MAP_0 0 # define RASTER_CONFIG_RB_MAP_1 1 @@ -599,6 +644,8 @@ #define TCC_DISABLE_MASK 0xFFFF0000 #define TCC_DISABLE_SHIFT 16
+#define CB_CGTT_SCLK_CTRL 0x3c2a0 + /* * PM4 */
From: Alex Deucher alexander.deucher@amd.com
Todo: - handle interrupts for compute queues
v2: add documentation v3: update to latest reset code v4: update to latest illegal CP handling v5: fix missing break in interrupt handler switch statement
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/cik.c | 840 +++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/cikd.h | 170 ++++++++ drivers/gpu/drm/radeon/radeon.h | 11 + 3 files changed, 1021 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index a44ede6..72c7e83 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -62,6 +62,8 @@ MODULE_FIRMWARE("radeon/KABINI_ce.bin"); MODULE_FIRMWARE("radeon/KABINI_mec.bin"); MODULE_FIRMWARE("radeon/KABINI_rlc.bin");
+extern int r600_ih_ring_alloc(struct radeon_device *rdev); +extern void r600_ih_ring_fini(struct radeon_device *rdev); extern void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_save *save); extern void evergreen_mc_resume(struct radeon_device *rdev, struct evergreen_mc_save *save); extern void si_vram_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc); @@ -2919,3 +2921,841 @@ static int cik_rlc_resume(struct radeon_device *rdev)
return 0; } + +/* + * Interrupts + * Starting with r6xx, interrupts are handled via a ring buffer. + * Ring buffers are areas of GPU accessible memory that the GPU + * writes interrupt vectors into and the host reads vectors out of. + * There is a rptr (read pointer) that determines where the + * host is currently reading, and a wptr (write pointer) + * which determines where the GPU has written. When the + * pointers are equal, the ring is idle. When the GPU + * writes vectors to the ring buffer, it increments the + * wptr. When there is an interrupt, the host then starts + * fetching commands and processing them until the pointers are + * equal again at which point it updates the rptr. + */ + +/** + * cik_enable_interrupts - Enable the interrupt ring buffer + * + * @rdev: radeon_device pointer + * + * Enable the interrupt ring buffer (CIK). + */ +static void cik_enable_interrupts(struct radeon_device *rdev) +{ + u32 ih_cntl = RREG32(IH_CNTL); + u32 ih_rb_cntl = RREG32(IH_RB_CNTL); + + ih_cntl |= ENABLE_INTR; + ih_rb_cntl |= IH_RB_ENABLE; + WREG32(IH_CNTL, ih_cntl); + WREG32(IH_RB_CNTL, ih_rb_cntl); + rdev->ih.enabled = true; +} + +/** + * cik_disable_interrupts - Disable the interrupt ring buffer + * + * @rdev: radeon_device pointer + * + * Disable the interrupt ring buffer (CIK). + */ +static void cik_disable_interrupts(struct radeon_device *rdev) +{ + u32 ih_rb_cntl = RREG32(IH_RB_CNTL); + u32 ih_cntl = RREG32(IH_CNTL); + + ih_rb_cntl &= ~IH_RB_ENABLE; + ih_cntl &= ~ENABLE_INTR; + WREG32(IH_RB_CNTL, ih_rb_cntl); + WREG32(IH_CNTL, ih_cntl); + /* set rptr, wptr to 0 */ + WREG32(IH_RB_RPTR, 0); + WREG32(IH_RB_WPTR, 0); + rdev->ih.enabled = false; + rdev->ih.rptr = 0; +} + +/** + * cik_disable_interrupt_state - Disable all interrupt sources + * + * @rdev: radeon_device pointer + * + * Clear all interrupt enable bits used by the driver (CIK). + */ +static void cik_disable_interrupt_state(struct radeon_device *rdev) +{ + u32 tmp; + + /* gfx ring */ + WREG32(CP_INT_CNTL_RING0, CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE); + /* compute queues */ + WREG32(CP_ME1_PIPE0_INT_CNTL, 0); + WREG32(CP_ME1_PIPE1_INT_CNTL, 0); + WREG32(CP_ME1_PIPE2_INT_CNTL, 0); + WREG32(CP_ME1_PIPE3_INT_CNTL, 0); + WREG32(CP_ME2_PIPE0_INT_CNTL, 0); + WREG32(CP_ME2_PIPE1_INT_CNTL, 0); + WREG32(CP_ME2_PIPE2_INT_CNTL, 0); + WREG32(CP_ME2_PIPE3_INT_CNTL, 0); + /* grbm */ + WREG32(GRBM_INT_CNTL, 0); + /* vline/vblank, etc. */ + WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC0_REGISTER_OFFSET, 0); + WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC1_REGISTER_OFFSET, 0); + if (rdev->num_crtc >= 4) { + WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC2_REGISTER_OFFSET, 0); + WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC3_REGISTER_OFFSET, 0); + } + if (rdev->num_crtc >= 6) { + WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC4_REGISTER_OFFSET, 0); + WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC5_REGISTER_OFFSET, 0); + } + + /* dac hotplug */ + WREG32(DAC_AUTODETECT_INT_CONTROL, 0); + + /* digital hotplug */ + tmp = RREG32(DC_HPD1_INT_CONTROL) & DC_HPDx_INT_POLARITY; + WREG32(DC_HPD1_INT_CONTROL, tmp); + tmp = RREG32(DC_HPD2_INT_CONTROL) & DC_HPDx_INT_POLARITY; + WREG32(DC_HPD2_INT_CONTROL, tmp); + tmp = RREG32(DC_HPD3_INT_CONTROL) & DC_HPDx_INT_POLARITY; + WREG32(DC_HPD3_INT_CONTROL, tmp); + tmp = RREG32(DC_HPD4_INT_CONTROL) & DC_HPDx_INT_POLARITY; + WREG32(DC_HPD4_INT_CONTROL, tmp); + tmp = RREG32(DC_HPD5_INT_CONTROL) & DC_HPDx_INT_POLARITY; + WREG32(DC_HPD5_INT_CONTROL, tmp); + tmp = RREG32(DC_HPD6_INT_CONTROL) & DC_HPDx_INT_POLARITY; + WREG32(DC_HPD6_INT_CONTROL, tmp); + +} + +/** + * cik_irq_init - init and enable the interrupt ring + * + * @rdev: radeon_device pointer + * + * Allocate a ring buffer for the interrupt controller, + * enable the RLC, disable interrupts, enable the IH + * ring buffer and enable it (CIK). + * Called at device load and reume. + * Returns 0 for success, errors for failure. + */ +static int cik_irq_init(struct radeon_device *rdev) +{ + int ret = 0; + int rb_bufsz; + u32 interrupt_cntl, ih_cntl, ih_rb_cntl; + + /* allocate ring */ + ret = r600_ih_ring_alloc(rdev); + if (ret) + return ret; + + /* disable irqs */ + cik_disable_interrupts(rdev); + + /* init rlc */ + ret = cik_rlc_resume(rdev); + if (ret) { + r600_ih_ring_fini(rdev); + return ret; + } + + /* setup interrupt control */ + /* XXX this should actually be a bus address, not an MC address. same on older asics */ + WREG32(INTERRUPT_CNTL2, rdev->ih.gpu_addr >> 8); + interrupt_cntl = RREG32(INTERRUPT_CNTL); + /* IH_DUMMY_RD_OVERRIDE=0 - dummy read disabled with msi, enabled without msi + * IH_DUMMY_RD_OVERRIDE=1 - dummy read controlled by IH_DUMMY_RD_EN + */ + interrupt_cntl &= ~IH_DUMMY_RD_OVERRIDE; + /* IH_REQ_NONSNOOP_EN=1 if ring is in non-cacheable memory, e.g., vram */ + interrupt_cntl &= ~IH_REQ_NONSNOOP_EN; + WREG32(INTERRUPT_CNTL, interrupt_cntl); + + WREG32(IH_RB_BASE, rdev->ih.gpu_addr >> 8); + rb_bufsz = drm_order(rdev->ih.ring_size / 4); + + ih_rb_cntl = (IH_WPTR_OVERFLOW_ENABLE | + IH_WPTR_OVERFLOW_CLEAR | + (rb_bufsz << 1)); + + if (rdev->wb.enabled) + ih_rb_cntl |= IH_WPTR_WRITEBACK_ENABLE; + + /* set the writeback address whether it's enabled or not */ + WREG32(IH_RB_WPTR_ADDR_LO, (rdev->wb.gpu_addr + R600_WB_IH_WPTR_OFFSET) & 0xFFFFFFFC); + WREG32(IH_RB_WPTR_ADDR_HI, upper_32_bits(rdev->wb.gpu_addr + R600_WB_IH_WPTR_OFFSET) & 0xFF); + + WREG32(IH_RB_CNTL, ih_rb_cntl); + + /* set rptr, wptr to 0 */ + WREG32(IH_RB_RPTR, 0); + WREG32(IH_RB_WPTR, 0); + + /* Default settings for IH_CNTL (disabled at first) */ + ih_cntl = MC_WRREQ_CREDIT(0x10) | MC_WR_CLEAN_CNT(0x10) | MC_VMID(0); + /* RPTR_REARM only works if msi's are enabled */ + if (rdev->msi_enabled) + ih_cntl |= RPTR_REARM; + WREG32(IH_CNTL, ih_cntl); + + /* force the active interrupt state to all disabled */ + cik_disable_interrupt_state(rdev); + + pci_set_master(rdev->pdev); + + /* enable irqs */ + cik_enable_interrupts(rdev); + + return ret; +} + +/** + * cik_irq_set - enable/disable interrupt sources + * + * @rdev: radeon_device pointer + * + * Enable interrupt sources on the GPU (vblanks, hpd, + * etc.) (CIK). + * Returns 0 for success, errors for failure. + */ +int cik_irq_set(struct radeon_device *rdev) +{ + u32 cp_int_cntl = CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE | + PRIV_INSTR_INT_ENABLE | PRIV_REG_INT_ENABLE; + u32 crtc1 = 0, crtc2 = 0, crtc3 = 0, crtc4 = 0, crtc5 = 0, crtc6 = 0; + u32 hpd1, hpd2, hpd3, hpd4, hpd5, hpd6; + u32 grbm_int_cntl = 0; + + if (!rdev->irq.installed) { + WARN(1, "Can't enable IRQ/MSI because no handler is installed\n"); + return -EINVAL; + } + /* don't enable anything if the ih is disabled */ + if (!rdev->ih.enabled) { + cik_disable_interrupts(rdev); + /* force the active interrupt state to all disabled */ + cik_disable_interrupt_state(rdev); + return 0; + } + + hpd1 = RREG32(DC_HPD1_INT_CONTROL) & ~DC_HPDx_INT_EN; + hpd2 = RREG32(DC_HPD2_INT_CONTROL) & ~DC_HPDx_INT_EN; + hpd3 = RREG32(DC_HPD3_INT_CONTROL) & ~DC_HPDx_INT_EN; + hpd4 = RREG32(DC_HPD4_INT_CONTROL) & ~DC_HPDx_INT_EN; + hpd5 = RREG32(DC_HPD5_INT_CONTROL) & ~DC_HPDx_INT_EN; + hpd6 = RREG32(DC_HPD6_INT_CONTROL) & ~DC_HPDx_INT_EN; + + /* enable CP interrupts on all rings */ + if (atomic_read(&rdev->irq.ring_int[RADEON_RING_TYPE_GFX_INDEX])) { + DRM_DEBUG("cik_irq_set: sw int gfx\n"); + cp_int_cntl |= TIME_STAMP_INT_ENABLE; + } + /* TODO: compute queues! */ + /* CP_ME[1-2]_PIPE[0-3]_INT_CNTL */ + + if (rdev->irq.crtc_vblank_int[0] || + atomic_read(&rdev->irq.pflip[0])) { + DRM_DEBUG("cik_irq_set: vblank 0\n"); + crtc1 |= VBLANK_INTERRUPT_MASK; + } + if (rdev->irq.crtc_vblank_int[1] || + atomic_read(&rdev->irq.pflip[1])) { + DRM_DEBUG("cik_irq_set: vblank 1\n"); + crtc2 |= VBLANK_INTERRUPT_MASK; + } + if (rdev->irq.crtc_vblank_int[2] || + atomic_read(&rdev->irq.pflip[2])) { + DRM_DEBUG("cik_irq_set: vblank 2\n"); + crtc3 |= VBLANK_INTERRUPT_MASK; + } + if (rdev->irq.crtc_vblank_int[3] || + atomic_read(&rdev->irq.pflip[3])) { + DRM_DEBUG("cik_irq_set: vblank 3\n"); + crtc4 |= VBLANK_INTERRUPT_MASK; + } + if (rdev->irq.crtc_vblank_int[4] || + atomic_read(&rdev->irq.pflip[4])) { + DRM_DEBUG("cik_irq_set: vblank 4\n"); + crtc5 |= VBLANK_INTERRUPT_MASK; + } + if (rdev->irq.crtc_vblank_int[5] || + atomic_read(&rdev->irq.pflip[5])) { + DRM_DEBUG("cik_irq_set: vblank 5\n"); + crtc6 |= VBLANK_INTERRUPT_MASK; + } + if (rdev->irq.hpd[0]) { + DRM_DEBUG("cik_irq_set: hpd 1\n"); + hpd1 |= DC_HPDx_INT_EN; + } + if (rdev->irq.hpd[1]) { + DRM_DEBUG("cik_irq_set: hpd 2\n"); + hpd2 |= DC_HPDx_INT_EN; + } + if (rdev->irq.hpd[2]) { + DRM_DEBUG("cik_irq_set: hpd 3\n"); + hpd3 |= DC_HPDx_INT_EN; + } + if (rdev->irq.hpd[3]) { + DRM_DEBUG("cik_irq_set: hpd 4\n"); + hpd4 |= DC_HPDx_INT_EN; + } + if (rdev->irq.hpd[4]) { + DRM_DEBUG("cik_irq_set: hpd 5\n"); + hpd5 |= DC_HPDx_INT_EN; + } + if (rdev->irq.hpd[5]) { + DRM_DEBUG("cik_irq_set: hpd 6\n"); + hpd6 |= DC_HPDx_INT_EN; + } + + WREG32(CP_INT_CNTL_RING0, cp_int_cntl); + + WREG32(GRBM_INT_CNTL, grbm_int_cntl); + + WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC0_REGISTER_OFFSET, crtc1); + WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC1_REGISTER_OFFSET, crtc2); + if (rdev->num_crtc >= 4) { + WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC2_REGISTER_OFFSET, crtc3); + WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC3_REGISTER_OFFSET, crtc4); + } + if (rdev->num_crtc >= 6) { + WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC4_REGISTER_OFFSET, crtc5); + WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC5_REGISTER_OFFSET, crtc6); + } + + WREG32(DC_HPD1_INT_CONTROL, hpd1); + WREG32(DC_HPD2_INT_CONTROL, hpd2); + WREG32(DC_HPD3_INT_CONTROL, hpd3); + WREG32(DC_HPD4_INT_CONTROL, hpd4); + WREG32(DC_HPD5_INT_CONTROL, hpd5); + WREG32(DC_HPD6_INT_CONTROL, hpd6); + + return 0; +} + +/** + * cik_irq_ack - ack interrupt sources + * + * @rdev: radeon_device pointer + * + * Ack interrupt sources on the GPU (vblanks, hpd, + * etc.) (CIK). Certain interrupts sources are sw + * generated and do not require an explicit ack. + */ +static inline void cik_irq_ack(struct radeon_device *rdev) +{ + u32 tmp; + + rdev->irq.stat_regs.cik.disp_int = RREG32(DISP_INTERRUPT_STATUS); + rdev->irq.stat_regs.cik.disp_int_cont = RREG32(DISP_INTERRUPT_STATUS_CONTINUE); + rdev->irq.stat_regs.cik.disp_int_cont2 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE2); + rdev->irq.stat_regs.cik.disp_int_cont3 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE3); + rdev->irq.stat_regs.cik.disp_int_cont4 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE4); + rdev->irq.stat_regs.cik.disp_int_cont5 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE5); + rdev->irq.stat_regs.cik.disp_int_cont6 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE6); + + if (rdev->irq.stat_regs.cik.disp_int & LB_D1_VBLANK_INTERRUPT) + WREG32(LB_VBLANK_STATUS + EVERGREEN_CRTC0_REGISTER_OFFSET, VBLANK_ACK); + if (rdev->irq.stat_regs.cik.disp_int & LB_D1_VLINE_INTERRUPT) + WREG32(LB_VLINE_STATUS + EVERGREEN_CRTC0_REGISTER_OFFSET, VLINE_ACK); + if (rdev->irq.stat_regs.cik.disp_int_cont & LB_D2_VBLANK_INTERRUPT) + WREG32(LB_VBLANK_STATUS + EVERGREEN_CRTC1_REGISTER_OFFSET, VBLANK_ACK); + if (rdev->irq.stat_regs.cik.disp_int_cont & LB_D2_VLINE_INTERRUPT) + WREG32(LB_VLINE_STATUS + EVERGREEN_CRTC1_REGISTER_OFFSET, VLINE_ACK); + + if (rdev->num_crtc >= 4) { + if (rdev->irq.stat_regs.cik.disp_int_cont2 & LB_D3_VBLANK_INTERRUPT) + WREG32(LB_VBLANK_STATUS + EVERGREEN_CRTC2_REGISTER_OFFSET, VBLANK_ACK); + if (rdev->irq.stat_regs.cik.disp_int_cont2 & LB_D3_VLINE_INTERRUPT) + WREG32(LB_VLINE_STATUS + EVERGREEN_CRTC2_REGISTER_OFFSET, VLINE_ACK); + if (rdev->irq.stat_regs.cik.disp_int_cont3 & LB_D4_VBLANK_INTERRUPT) + WREG32(LB_VBLANK_STATUS + EVERGREEN_CRTC3_REGISTER_OFFSET, VBLANK_ACK); + if (rdev->irq.stat_regs.cik.disp_int_cont3 & LB_D4_VLINE_INTERRUPT) + WREG32(LB_VLINE_STATUS + EVERGREEN_CRTC3_REGISTER_OFFSET, VLINE_ACK); + } + + if (rdev->num_crtc >= 6) { + if (rdev->irq.stat_regs.cik.disp_int_cont4 & LB_D5_VBLANK_INTERRUPT) + WREG32(LB_VBLANK_STATUS + EVERGREEN_CRTC4_REGISTER_OFFSET, VBLANK_ACK); + if (rdev->irq.stat_regs.cik.disp_int_cont4 & LB_D5_VLINE_INTERRUPT) + WREG32(LB_VLINE_STATUS + EVERGREEN_CRTC4_REGISTER_OFFSET, VLINE_ACK); + if (rdev->irq.stat_regs.cik.disp_int_cont5 & LB_D6_VBLANK_INTERRUPT) + WREG32(LB_VBLANK_STATUS + EVERGREEN_CRTC5_REGISTER_OFFSET, VBLANK_ACK); + if (rdev->irq.stat_regs.cik.disp_int_cont5 & LB_D6_VLINE_INTERRUPT) + WREG32(LB_VLINE_STATUS + EVERGREEN_CRTC5_REGISTER_OFFSET, VLINE_ACK); + } + + if (rdev->irq.stat_regs.cik.disp_int & DC_HPD1_INTERRUPT) { + tmp = RREG32(DC_HPD1_INT_CONTROL); + tmp |= DC_HPDx_INT_ACK; + WREG32(DC_HPD1_INT_CONTROL, tmp); + } + if (rdev->irq.stat_regs.cik.disp_int_cont & DC_HPD2_INTERRUPT) { + tmp = RREG32(DC_HPD2_INT_CONTROL); + tmp |= DC_HPDx_INT_ACK; + WREG32(DC_HPD2_INT_CONTROL, tmp); + } + if (rdev->irq.stat_regs.cik.disp_int_cont2 & DC_HPD3_INTERRUPT) { + tmp = RREG32(DC_HPD3_INT_CONTROL); + tmp |= DC_HPDx_INT_ACK; + WREG32(DC_HPD3_INT_CONTROL, tmp); + } + if (rdev->irq.stat_regs.cik.disp_int_cont3 & DC_HPD4_INTERRUPT) { + tmp = RREG32(DC_HPD4_INT_CONTROL); + tmp |= DC_HPDx_INT_ACK; + WREG32(DC_HPD4_INT_CONTROL, tmp); + } + if (rdev->irq.stat_regs.cik.disp_int_cont4 & DC_HPD5_INTERRUPT) { + tmp = RREG32(DC_HPD5_INT_CONTROL); + tmp |= DC_HPDx_INT_ACK; + WREG32(DC_HPD5_INT_CONTROL, tmp); + } + if (rdev->irq.stat_regs.cik.disp_int_cont5 & DC_HPD6_INTERRUPT) { + tmp = RREG32(DC_HPD5_INT_CONTROL); + tmp |= DC_HPDx_INT_ACK; + WREG32(DC_HPD6_INT_CONTROL, tmp); + } +} + +/** + * cik_irq_disable - disable interrupts + * + * @rdev: radeon_device pointer + * + * Disable interrupts on the hw (CIK). + */ +static void cik_irq_disable(struct radeon_device *rdev) +{ + cik_disable_interrupts(rdev); + /* Wait and acknowledge irq */ + mdelay(1); + cik_irq_ack(rdev); + cik_disable_interrupt_state(rdev); +} + +/** + * cik_irq_disable - disable interrupts for suspend + * + * @rdev: radeon_device pointer + * + * Disable interrupts and stop the RLC (CIK). + * Used for suspend. + */ +static void cik_irq_suspend(struct radeon_device *rdev) +{ + cik_irq_disable(rdev); + cik_rlc_stop(rdev); +} + +/** + * cik_irq_fini - tear down interrupt support + * + * @rdev: radeon_device pointer + * + * Disable interrupts on the hw and free the IH ring + * buffer (CIK). + * Used for driver unload. + */ +static void cik_irq_fini(struct radeon_device *rdev) +{ + cik_irq_suspend(rdev); + r600_ih_ring_fini(rdev); +} + +/** + * cik_get_ih_wptr - get the IH ring buffer wptr + * + * @rdev: radeon_device pointer + * + * Get the IH ring buffer wptr from either the register + * or the writeback memory buffer (CIK). Also check for + * ring buffer overflow and deal with it. + * Used by cik_irq_process(). + * Returns the value of the wptr. + */ +static inline u32 cik_get_ih_wptr(struct radeon_device *rdev) +{ + u32 wptr, tmp; + + if (rdev->wb.enabled) + wptr = le32_to_cpu(rdev->wb.wb[R600_WB_IH_WPTR_OFFSET/4]); + else + wptr = RREG32(IH_RB_WPTR); + + if (wptr & RB_OVERFLOW) { + /* When a ring buffer overflow happen start parsing interrupt + * from the last not overwritten vector (wptr + 16). Hopefully + * this should allow us to catchup. + */ + dev_warn(rdev->dev, "IH ring buffer overflow (0x%08X, %d, %d)\n", + wptr, rdev->ih.rptr, (wptr + 16) + rdev->ih.ptr_mask); + rdev->ih.rptr = (wptr + 16) & rdev->ih.ptr_mask; + tmp = RREG32(IH_RB_CNTL); + tmp |= IH_WPTR_OVERFLOW_CLEAR; + WREG32(IH_RB_CNTL, tmp); + } + return (wptr & rdev->ih.ptr_mask); +} + +/* CIK IV Ring + * Each IV ring entry is 128 bits: + * [7:0] - interrupt source id + * [31:8] - reserved + * [59:32] - interrupt source data + * [63:60] - reserved + * [71:64] - RINGID: ME_ID [1:0], PIPE_ID[1:0], QUEUE_ID[2:0] + * QUEUE_ID - for compute, which of the 8 queues owned by the dispatcher + * - for gfx, hw shader state (0=PS...5=LS, 6=CS) + * ME_ID - 0 = gfx, 1 = first 4 CS pipes, 2 = second 4 CS pipes + * PIPE_ID - ME0 0=3D + * - ME1&2 compute dispatcher (4 pipes each) + * [79:72] - VMID + * [95:80] - PASID + * [127:96] - reserved + */ +/** + * cik_irq_process - interrupt handler + * + * @rdev: radeon_device pointer + * + * Interrupt hander (CIK). Walk the IH ring, + * ack interrupts and schedule work to handle + * interrupt events. + * Returns irq process return code. + */ +int cik_irq_process(struct radeon_device *rdev) +{ + u32 wptr; + u32 rptr; + u32 src_id, src_data, ring_id; + u8 me_id, pipe_id, queue_id; + u32 ring_index; + bool queue_hotplug = false; + bool queue_reset = false; + + if (!rdev->ih.enabled || rdev->shutdown) + return IRQ_NONE; + + wptr = cik_get_ih_wptr(rdev); + +restart_ih: + /* is somebody else already processing irqs? */ + if (atomic_xchg(&rdev->ih.lock, 1)) + return IRQ_NONE; + + rptr = rdev->ih.rptr; + DRM_DEBUG("cik_irq_process start: rptr %d, wptr %d\n", rptr, wptr); + + /* Order reading of wptr vs. reading of IH ring data */ + rmb(); + + /* display interrupts */ + cik_irq_ack(rdev); + + while (rptr != wptr) { + /* wptr/rptr are in bytes! */ + ring_index = rptr / 4; + src_id = le32_to_cpu(rdev->ih.ring[ring_index]) & 0xff; + src_data = le32_to_cpu(rdev->ih.ring[ring_index + 1]) & 0xfffffff; + ring_id = le32_to_cpu(rdev->ih.ring[ring_index + 2]) & 0xff; + /* XXX check the bitfield order! */ + me_id = (ring_id & 0x60) >> 5; + pipe_id = (ring_id & 0x18) >> 3; + queue_id = (ring_id & 0x7) >> 0; + + switch (src_id) { + case 1: /* D1 vblank/vline */ + switch (src_data) { + case 0: /* D1 vblank */ + if (rdev->irq.stat_regs.cik.disp_int & LB_D1_VBLANK_INTERRUPT) { + if (rdev->irq.crtc_vblank_int[0]) { + drm_handle_vblank(rdev->ddev, 0); + rdev->pm.vblank_sync = true; + wake_up(&rdev->irq.vblank_queue); + } + if (atomic_read(&rdev->irq.pflip[0])) + radeon_crtc_handle_flip(rdev, 0); + rdev->irq.stat_regs.cik.disp_int &= ~LB_D1_VBLANK_INTERRUPT; + DRM_DEBUG("IH: D1 vblank\n"); + } + break; + case 1: /* D1 vline */ + if (rdev->irq.stat_regs.cik.disp_int & LB_D1_VLINE_INTERRUPT) { + rdev->irq.stat_regs.cik.disp_int &= ~LB_D1_VLINE_INTERRUPT; + DRM_DEBUG("IH: D1 vline\n"); + } + break; + default: + DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data); + break; + } + break; + case 2: /* D2 vblank/vline */ + switch (src_data) { + case 0: /* D2 vblank */ + if (rdev->irq.stat_regs.cik.disp_int_cont & LB_D2_VBLANK_INTERRUPT) { + if (rdev->irq.crtc_vblank_int[1]) { + drm_handle_vblank(rdev->ddev, 1); + rdev->pm.vblank_sync = true; + wake_up(&rdev->irq.vblank_queue); + } + if (atomic_read(&rdev->irq.pflip[1])) + radeon_crtc_handle_flip(rdev, 1); + rdev->irq.stat_regs.cik.disp_int_cont &= ~LB_D2_VBLANK_INTERRUPT; + DRM_DEBUG("IH: D2 vblank\n"); + } + break; + case 1: /* D2 vline */ + if (rdev->irq.stat_regs.cik.disp_int_cont & LB_D2_VLINE_INTERRUPT) { + rdev->irq.stat_regs.cik.disp_int_cont &= ~LB_D2_VLINE_INTERRUPT; + DRM_DEBUG("IH: D2 vline\n"); + } + break; + default: + DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data); + break; + } + break; + case 3: /* D3 vblank/vline */ + switch (src_data) { + case 0: /* D3 vblank */ + if (rdev->irq.stat_regs.cik.disp_int_cont2 & LB_D3_VBLANK_INTERRUPT) { + if (rdev->irq.crtc_vblank_int[2]) { + drm_handle_vblank(rdev->ddev, 2); + rdev->pm.vblank_sync = true; + wake_up(&rdev->irq.vblank_queue); + } + if (atomic_read(&rdev->irq.pflip[2])) + radeon_crtc_handle_flip(rdev, 2); + rdev->irq.stat_regs.cik.disp_int_cont2 &= ~LB_D3_VBLANK_INTERRUPT; + DRM_DEBUG("IH: D3 vblank\n"); + } + break; + case 1: /* D3 vline */ + if (rdev->irq.stat_regs.cik.disp_int_cont2 & LB_D3_VLINE_INTERRUPT) { + rdev->irq.stat_regs.cik.disp_int_cont2 &= ~LB_D3_VLINE_INTERRUPT; + DRM_DEBUG("IH: D3 vline\n"); + } + break; + default: + DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data); + break; + } + break; + case 4: /* D4 vblank/vline */ + switch (src_data) { + case 0: /* D4 vblank */ + if (rdev->irq.stat_regs.cik.disp_int_cont3 & LB_D4_VBLANK_INTERRUPT) { + if (rdev->irq.crtc_vblank_int[3]) { + drm_handle_vblank(rdev->ddev, 3); + rdev->pm.vblank_sync = true; + wake_up(&rdev->irq.vblank_queue); + } + if (atomic_read(&rdev->irq.pflip[3])) + radeon_crtc_handle_flip(rdev, 3); + rdev->irq.stat_regs.cik.disp_int_cont3 &= ~LB_D4_VBLANK_INTERRUPT; + DRM_DEBUG("IH: D4 vblank\n"); + } + break; + case 1: /* D4 vline */ + if (rdev->irq.stat_regs.cik.disp_int_cont3 & LB_D4_VLINE_INTERRUPT) { + rdev->irq.stat_regs.cik.disp_int_cont3 &= ~LB_D4_VLINE_INTERRUPT; + DRM_DEBUG("IH: D4 vline\n"); + } + break; + default: + DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data); + break; + } + break; + case 5: /* D5 vblank/vline */ + switch (src_data) { + case 0: /* D5 vblank */ + if (rdev->irq.stat_regs.cik.disp_int_cont4 & LB_D5_VBLANK_INTERRUPT) { + if (rdev->irq.crtc_vblank_int[4]) { + drm_handle_vblank(rdev->ddev, 4); + rdev->pm.vblank_sync = true; + wake_up(&rdev->irq.vblank_queue); + } + if (atomic_read(&rdev->irq.pflip[4])) + radeon_crtc_handle_flip(rdev, 4); + rdev->irq.stat_regs.cik.disp_int_cont4 &= ~LB_D5_VBLANK_INTERRUPT; + DRM_DEBUG("IH: D5 vblank\n"); + } + break; + case 1: /* D5 vline */ + if (rdev->irq.stat_regs.cik.disp_int_cont4 & LB_D5_VLINE_INTERRUPT) { + rdev->irq.stat_regs.cik.disp_int_cont4 &= ~LB_D5_VLINE_INTERRUPT; + DRM_DEBUG("IH: D5 vline\n"); + } + break; + default: + DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data); + break; + } + break; + case 6: /* D6 vblank/vline */ + switch (src_data) { + case 0: /* D6 vblank */ + if (rdev->irq.stat_regs.cik.disp_int_cont5 & LB_D6_VBLANK_INTERRUPT) { + if (rdev->irq.crtc_vblank_int[5]) { + drm_handle_vblank(rdev->ddev, 5); + rdev->pm.vblank_sync = true; + wake_up(&rdev->irq.vblank_queue); + } + if (atomic_read(&rdev->irq.pflip[5])) + radeon_crtc_handle_flip(rdev, 5); + rdev->irq.stat_regs.cik.disp_int_cont5 &= ~LB_D6_VBLANK_INTERRUPT; + DRM_DEBUG("IH: D6 vblank\n"); + } + break; + case 1: /* D6 vline */ + if (rdev->irq.stat_regs.cik.disp_int_cont5 & LB_D6_VLINE_INTERRUPT) { + rdev->irq.stat_regs.cik.disp_int_cont5 &= ~LB_D6_VLINE_INTERRUPT; + DRM_DEBUG("IH: D6 vline\n"); + } + break; + default: + DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data); + break; + } + break; + case 42: /* HPD hotplug */ + switch (src_data) { + case 0: + if (rdev->irq.stat_regs.cik.disp_int & DC_HPD1_INTERRUPT) { + rdev->irq.stat_regs.cik.disp_int &= ~DC_HPD1_INTERRUPT; + queue_hotplug = true; + DRM_DEBUG("IH: HPD1\n"); + } + break; + case 1: + if (rdev->irq.stat_regs.cik.disp_int_cont & DC_HPD2_INTERRUPT) { + rdev->irq.stat_regs.cik.disp_int_cont &= ~DC_HPD2_INTERRUPT; + queue_hotplug = true; + DRM_DEBUG("IH: HPD2\n"); + } + break; + case 2: + if (rdev->irq.stat_regs.cik.disp_int_cont2 & DC_HPD3_INTERRUPT) { + rdev->irq.stat_regs.cik.disp_int_cont2 &= ~DC_HPD3_INTERRUPT; + queue_hotplug = true; + DRM_DEBUG("IH: HPD3\n"); + } + break; + case 3: + if (rdev->irq.stat_regs.cik.disp_int_cont3 & DC_HPD4_INTERRUPT) { + rdev->irq.stat_regs.cik.disp_int_cont3 &= ~DC_HPD4_INTERRUPT; + queue_hotplug = true; + DRM_DEBUG("IH: HPD4\n"); + } + break; + case 4: + if (rdev->irq.stat_regs.cik.disp_int_cont4 & DC_HPD5_INTERRUPT) { + rdev->irq.stat_regs.cik.disp_int_cont4 &= ~DC_HPD5_INTERRUPT; + queue_hotplug = true; + DRM_DEBUG("IH: HPD5\n"); + } + break; + case 5: + if (rdev->irq.stat_regs.cik.disp_int_cont5 & DC_HPD6_INTERRUPT) { + rdev->irq.stat_regs.cik.disp_int_cont5 &= ~DC_HPD6_INTERRUPT; + queue_hotplug = true; + DRM_DEBUG("IH: HPD6\n"); + } + break; + default: + DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data); + break; + } + break; + case 176: /* GFX RB CP_INT */ + case 177: /* GFX IB CP_INT */ + radeon_fence_process(rdev, RADEON_RING_TYPE_GFX_INDEX); + break; + case 181: /* CP EOP event */ + DRM_DEBUG("IH: CP EOP\n"); + switch (me_id) { + case 0: + radeon_fence_process(rdev, RADEON_RING_TYPE_GFX_INDEX); + break; + case 1: + /* XXX compute */ + break; + case 2: + /* XXX compute */ + break; + } + break; + case 184: /* CP Privileged reg access */ + DRM_ERROR("Illegal register access in command stream\n"); + /* XXX check the bitfield order! */ + me_id = (ring_id & 0x60) >> 5; + pipe_id = (ring_id & 0x18) >> 3; + queue_id = (ring_id & 0x7) >> 0; + switch (me_id) { + case 0: + /* This results in a full GPU reset, but all we need to do is soft + * reset the CP for gfx + */ + queue_reset = true; + break; + case 1: + /* XXX compute */ + break; + case 2: + /* XXX compute */ + break; + } + break; + case 185: /* CP Privileged inst */ + DRM_ERROR("Illegal instruction in command stream\n"); + switch (me_id) { + case 0: + /* This results in a full GPU reset, but all we need to do is soft + * reset the CP for gfx + */ + queue_reset = true; + break; + case 1: + /* XXX compute */ + break; + case 2: + /* XXX compute */ + break; + } + break; + case 233: /* GUI IDLE */ + DRM_DEBUG("IH: GUI idle\n"); + break; + default: + DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data); + break; + } + + /* wptr/rptr are in bytes! */ + rptr += 16; + rptr &= rdev->ih.ptr_mask; + } + if (queue_hotplug) + schedule_work(&rdev->hotplug_work); + if (queue_reset) + schedule_work(&rdev->reset_work); + rdev->ih.rptr = rptr; + WREG32(IH_RB_RPTR, rdev->ih.rptr); + atomic_set(&rdev->ih.lock, 0); + + /* make sure wptr hasn't changed while processing */ + wptr = cik_get_ih_wptr(rdev); + if (wptr != rptr) + goto restart_ih; + + return IRQ_HANDLED; +} diff --git a/drivers/gpu/drm/radeon/cikd.h b/drivers/gpu/drm/radeon/cikd.h index a116020..a282168 100644 --- a/drivers/gpu/drm/radeon/cikd.h +++ b/drivers/gpu/drm/radeon/cikd.h @@ -178,8 +178,42 @@ #define HDP_MISC_CNTL 0x2F4C #define HDP_FLUSH_INVALIDATE_CACHE (1 << 0)
+#define IH_RB_CNTL 0x3e00 +# define IH_RB_ENABLE (1 << 0) +# define IH_RB_SIZE(x) ((x) << 1) /* log2 */ +# define IH_RB_FULL_DRAIN_ENABLE (1 << 6) +# define IH_WPTR_WRITEBACK_ENABLE (1 << 8) +# define IH_WPTR_WRITEBACK_TIMER(x) ((x) << 9) /* log2 */ +# define IH_WPTR_OVERFLOW_ENABLE (1 << 16) +# define IH_WPTR_OVERFLOW_CLEAR (1 << 31) +#define IH_RB_BASE 0x3e04 +#define IH_RB_RPTR 0x3e08 +#define IH_RB_WPTR 0x3e0c +# define RB_OVERFLOW (1 << 0) +# define WPTR_OFFSET_MASK 0x3fffc +#define IH_RB_WPTR_ADDR_HI 0x3e10 +#define IH_RB_WPTR_ADDR_LO 0x3e14 +#define IH_CNTL 0x3e18 +# define ENABLE_INTR (1 << 0) +# define IH_MC_SWAP(x) ((x) << 1) +# define IH_MC_SWAP_NONE 0 +# define IH_MC_SWAP_16BIT 1 +# define IH_MC_SWAP_32BIT 2 +# define IH_MC_SWAP_64BIT 3 +# define RPTR_REARM (1 << 4) +# define MC_WRREQ_CREDIT(x) ((x) << 15) +# define MC_WR_CLEAN_CNT(x) ((x) << 20) +# define MC_VMID(x) ((x) << 25) + #define CONFIG_MEMSIZE 0x5428
+#define INTERRUPT_CNTL 0x5468 +# define IH_DUMMY_RD_OVERRIDE (1 << 0) +# define IH_DUMMY_RD_EN (1 << 1) +# define IH_REQ_NONSNOOP_EN (1 << 3) +# define GEN_IH_INT_EN (1 << 8) +#define INTERRUPT_CNTL2 0x546c + #define HDP_MEM_COHERENCY_FLUSH_CNTL 0x5480
#define BIF_FB_EN 0x5490 @@ -203,6 +237,99 @@ #define SDMA0 (1 << 10) #define SDMA1 (1 << 11)
+/* 0x6b24, 0x7724, 0x10324, 0x10f24, 0x11b24, 0x12724 */ +#define LB_VLINE_STATUS 0x6b24 +# define VLINE_OCCURRED (1 << 0) +# define VLINE_ACK (1 << 4) +# define VLINE_STAT (1 << 12) +# define VLINE_INTERRUPT (1 << 16) +# define VLINE_INTERRUPT_TYPE (1 << 17) +/* 0x6b2c, 0x772c, 0x1032c, 0x10f2c, 0x11b2c, 0x1272c */ +#define LB_VBLANK_STATUS 0x6b2c +# define VBLANK_OCCURRED (1 << 0) +# define VBLANK_ACK (1 << 4) +# define VBLANK_STAT (1 << 12) +# define VBLANK_INTERRUPT (1 << 16) +# define VBLANK_INTERRUPT_TYPE (1 << 17) + +/* 0x6b20, 0x7720, 0x10320, 0x10f20, 0x11b20, 0x12720 */ +#define LB_INTERRUPT_MASK 0x6b20 +# define VBLANK_INTERRUPT_MASK (1 << 0) +# define VLINE_INTERRUPT_MASK (1 << 4) +# define VLINE2_INTERRUPT_MASK (1 << 8) + +#define DISP_INTERRUPT_STATUS 0x60f4 +# define LB_D1_VLINE_INTERRUPT (1 << 2) +# define LB_D1_VBLANK_INTERRUPT (1 << 3) +# define DC_HPD1_INTERRUPT (1 << 17) +# define DC_HPD1_RX_INTERRUPT (1 << 18) +# define DACA_AUTODETECT_INTERRUPT (1 << 22) +# define DACB_AUTODETECT_INTERRUPT (1 << 23) +# define DC_I2C_SW_DONE_INTERRUPT (1 << 24) +# define DC_I2C_HW_DONE_INTERRUPT (1 << 25) +#define DISP_INTERRUPT_STATUS_CONTINUE 0x60f8 +# define LB_D2_VLINE_INTERRUPT (1 << 2) +# define LB_D2_VBLANK_INTERRUPT (1 << 3) +# define DC_HPD2_INTERRUPT (1 << 17) +# define DC_HPD2_RX_INTERRUPT (1 << 18) +# define DISP_TIMER_INTERRUPT (1 << 24) +#define DISP_INTERRUPT_STATUS_CONTINUE2 0x60fc +# define LB_D3_VLINE_INTERRUPT (1 << 2) +# define LB_D3_VBLANK_INTERRUPT (1 << 3) +# define DC_HPD3_INTERRUPT (1 << 17) +# define DC_HPD3_RX_INTERRUPT (1 << 18) +#define DISP_INTERRUPT_STATUS_CONTINUE3 0x6100 +# define LB_D4_VLINE_INTERRUPT (1 << 2) +# define LB_D4_VBLANK_INTERRUPT (1 << 3) +# define DC_HPD4_INTERRUPT (1 << 17) +# define DC_HPD4_RX_INTERRUPT (1 << 18) +#define DISP_INTERRUPT_STATUS_CONTINUE4 0x614c +# define LB_D5_VLINE_INTERRUPT (1 << 2) +# define LB_D5_VBLANK_INTERRUPT (1 << 3) +# define DC_HPD5_INTERRUPT (1 << 17) +# define DC_HPD5_RX_INTERRUPT (1 << 18) +#define DISP_INTERRUPT_STATUS_CONTINUE5 0x6150 +# define LB_D6_VLINE_INTERRUPT (1 << 2) +# define LB_D6_VBLANK_INTERRUPT (1 << 3) +# define DC_HPD6_INTERRUPT (1 << 17) +# define DC_HPD6_RX_INTERRUPT (1 << 18) +#define DISP_INTERRUPT_STATUS_CONTINUE6 0x6780 + +#define DAC_AUTODETECT_INT_CONTROL 0x67c8 + +#define DC_HPD1_INT_STATUS 0x601c +#define DC_HPD2_INT_STATUS 0x6028 +#define DC_HPD3_INT_STATUS 0x6034 +#define DC_HPD4_INT_STATUS 0x6040 +#define DC_HPD5_INT_STATUS 0x604c +#define DC_HPD6_INT_STATUS 0x6058 +# define DC_HPDx_INT_STATUS (1 << 0) +# define DC_HPDx_SENSE (1 << 1) +# define DC_HPDx_SENSE_DELAYED (1 << 4) +# define DC_HPDx_RX_INT_STATUS (1 << 8) + +#define DC_HPD1_INT_CONTROL 0x6020 +#define DC_HPD2_INT_CONTROL 0x602c +#define DC_HPD3_INT_CONTROL 0x6038 +#define DC_HPD4_INT_CONTROL 0x6044 +#define DC_HPD5_INT_CONTROL 0x6050 +#define DC_HPD6_INT_CONTROL 0x605c +# define DC_HPDx_INT_ACK (1 << 0) +# define DC_HPDx_INT_POLARITY (1 << 8) +# define DC_HPDx_INT_EN (1 << 16) +# define DC_HPDx_RX_INT_ACK (1 << 20) +# define DC_HPDx_RX_INT_EN (1 << 24) + +#define DC_HPD1_CONTROL 0x6024 +#define DC_HPD2_CONTROL 0x6030 +#define DC_HPD3_CONTROL 0x603c +#define DC_HPD4_CONTROL 0x6048 +#define DC_HPD5_CONTROL 0x6054 +#define DC_HPD6_CONTROL 0x6060 +# define DC_HPDx_CONNECTION_TIMER(x) ((x) << 0) +# define DC_HPDx_RX_INT_TIMER(x) ((x) << 16) +# define DC_HPDx_EN (1 << 28) + #define GRBM_CNTL 0x8000 #define GRBM_READ_TIMEOUT(x) ((x) << 0)
@@ -274,6 +401,10 @@ #define SOFT_RESET_CPC (1 << 18) /* CP Compute (MEC1/2) */ #define SOFT_RESET_CPG (1 << 19) /* CP GFX (PFP, ME, CE) */
+#define GRBM_INT_CNTL 0x8060 +# define RDERR_INT_ENABLE (1 << 0) +# define GUI_IDLE_INT_ENABLE (1 << 19) + #define CP_MEC_CNTL 0x8234 #define MEC_ME2_HALT (1 << 28) #define MEC_ME1_HALT (1 << 30) @@ -507,6 +638,45 @@ # define CP_RINGID1_INT_ENABLE (1 << 30) # define CP_RINGID0_INT_ENABLE (1 << 31)
+#define CP_INT_STATUS_RING0 0xC1B4 +# define PRIV_INSTR_INT_STAT (1 << 22) +# define PRIV_REG_INT_STAT (1 << 23) +# define TIME_STAMP_INT_STAT (1 << 26) +# define CP_RINGID2_INT_STAT (1 << 29) +# define CP_RINGID1_INT_STAT (1 << 30) +# define CP_RINGID0_INT_STAT (1 << 31) + +#define CP_ME1_PIPE0_INT_CNTL 0xC214 +#define CP_ME1_PIPE1_INT_CNTL 0xC218 +#define CP_ME1_PIPE2_INT_CNTL 0xC21C +#define CP_ME1_PIPE3_INT_CNTL 0xC220 +#define CP_ME2_PIPE0_INT_CNTL 0xC224 +#define CP_ME2_PIPE1_INT_CNTL 0xC228 +#define CP_ME2_PIPE2_INT_CNTL 0xC22C +#define CP_ME2_PIPE3_INT_CNTL 0xC230 +# define DEQUEUE_REQUEST_INT_ENABLE (1 << 13) +# define WRM_POLL_TIMEOUT_INT_ENABLE (1 << 17) +# define PRIV_REG_INT_ENABLE (1 << 23) +# define TIME_STAMP_INT_ENABLE (1 << 26) +# define GENERIC2_INT_ENABLE (1 << 29) +# define GENERIC1_INT_ENABLE (1 << 30) +# define GENERIC0_INT_ENABLE (1 << 31) +#define CP_ME1_PIPE0_INT_STATUS 0xC214 +#define CP_ME1_PIPE1_INT_STATUS 0xC218 +#define CP_ME1_PIPE2_INT_STATUS 0xC21C +#define CP_ME1_PIPE3_INT_STATUS 0xC220 +#define CP_ME2_PIPE0_INT_STATUS 0xC224 +#define CP_ME2_PIPE1_INT_STATUS 0xC228 +#define CP_ME2_PIPE2_INT_STATUS 0xC22C +#define CP_ME2_PIPE3_INT_STATUS 0xC230 +# define DEQUEUE_REQUEST_INT_STATUS (1 << 13) +# define WRM_POLL_TIMEOUT_INT_STATUS (1 << 17) +# define PRIV_REG_INT_STATUS (1 << 23) +# define TIME_STAMP_INT_STATUS (1 << 26) +# define GENERIC2_INT_STATUS (1 << 29) +# define GENERIC1_INT_STATUS (1 << 30) +# define GENERIC0_INT_STATUS (1 << 31) + #define CP_MAX_CONTEXT 0xC2B8
#define CP_RB0_BASE_HI 0xC2C4 diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 1c06c47..e09157b 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -600,10 +600,21 @@ struct evergreen_irq_stat_regs { u32 afmt_status6; };
+struct cik_irq_stat_regs { + u32 disp_int; + u32 disp_int_cont; + u32 disp_int_cont2; + u32 disp_int_cont3; + u32 disp_int_cont4; + u32 disp_int_cont5; + u32 disp_int_cont6; +}; + union radeon_irq_stat_regs { struct r500_irq_stat_regs r500; struct r600_irq_stat_regs r600; struct evergreen_irq_stat_regs evergreen; + struct cik_irq_stat_regs cik; };
#define RADEON_MAX_HPD_PINS 6
From: Alex Deucher alexander.deucher@amd.com
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/cik.c | 10 ++++++++++ drivers/gpu/drm/radeon/cikd.h | 4 ++++ 2 files changed, 14 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 72c7e83..b70f017 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -3676,6 +3676,16 @@ restart_ih: break; } break; + case 146: + case 147: + dev_err(rdev->dev, "GPU fault detected: %d 0x%08x\n", src_id, src_data); + dev_err(rdev->dev, " VM_CONTEXT1_PROTECTION_FAULT_ADDR 0x%08X\n", + RREG32(VM_CONTEXT1_PROTECTION_FAULT_ADDR)); + dev_err(rdev->dev, " VM_CONTEXT1_PROTECTION_FAULT_STATUS 0x%08X\n", + RREG32(VM_CONTEXT1_PROTECTION_FAULT_STATUS)); + /* reset addr and status */ + WREG32_P(VM_CONTEXT1_CNTL2, 1, ~1); + break; case 176: /* GFX RB CP_INT */ case 177: /* GFX IB CP_INT */ radeon_fence_process(rdev, RADEON_RING_TYPE_GFX_INDEX); diff --git a/drivers/gpu/drm/radeon/cikd.h b/drivers/gpu/drm/radeon/cikd.h index a282168..cc4f28e 100644 --- a/drivers/gpu/drm/radeon/cikd.h +++ b/drivers/gpu/drm/radeon/cikd.h @@ -95,6 +95,10 @@ #define VM_INVALIDATE_REQUEST 0x1478 #define VM_INVALIDATE_RESPONSE 0x147c
+#define VM_CONTEXT1_PROTECTION_FAULT_STATUS 0x14DC + +#define VM_CONTEXT1_PROTECTION_FAULT_ADDR 0x14FC + #define VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR 0x1518 #define VM_CONTEXT1_PROTECTION_FAULT_DEFAULT_ADDR 0x151c
From: Alex Deucher alexander.deucher@amd.com
CIK has new asynchronous DMA engines called sDMA (system DMA). Each engine supports 1 ring buffer for kernel and gfx and 2 userspace queues for compute.
TODO: fill in the compute setup.
v2: update to the latest reset code v3: remove ib_parse v4: fix copy_dma() v5: drop WIP compute sDMA queues v6: rebase v7: endian fixes for IB v8: cleanup for release
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/cik.c | 745 ++++++++++++++++++++++++++++++++++++++- drivers/gpu/drm/radeon/cikd.h | 130 +++++++ drivers/gpu/drm/radeon/radeon.h | 1 + 3 files changed, 870 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index b70f017..931169e 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -44,6 +44,9 @@ #define KV_RLC_UCODE_SIZE 2560 /* gddr controller */ #define CIK_MC_UCODE_SIZE 7866 +/* sdma */ +#define CIK_SDMA_UCODE_SIZE 1050 +#define CIK_SDMA_UCODE_VERSION 64
MODULE_FIRMWARE("radeon/BONAIRE_pfp.bin"); MODULE_FIRMWARE("radeon/BONAIRE_me.bin"); @@ -51,16 +54,19 @@ MODULE_FIRMWARE("radeon/BONAIRE_ce.bin"); MODULE_FIRMWARE("radeon/BONAIRE_mec.bin"); MODULE_FIRMWARE("radeon/BONAIRE_mc.bin"); MODULE_FIRMWARE("radeon/BONAIRE_rlc.bin"); +MODULE_FIRMWARE("radeon/BONAIRE_sdma.bin"); MODULE_FIRMWARE("radeon/KAVERI_pfp.bin"); MODULE_FIRMWARE("radeon/KAVERI_me.bin"); MODULE_FIRMWARE("radeon/KAVERI_ce.bin"); MODULE_FIRMWARE("radeon/KAVERI_mec.bin"); MODULE_FIRMWARE("radeon/KAVERI_rlc.bin"); +MODULE_FIRMWARE("radeon/KAVERI_sdma.bin"); MODULE_FIRMWARE("radeon/KABINI_pfp.bin"); MODULE_FIRMWARE("radeon/KABINI_me.bin"); MODULE_FIRMWARE("radeon/KABINI_ce.bin"); MODULE_FIRMWARE("radeon/KABINI_mec.bin"); MODULE_FIRMWARE("radeon/KABINI_rlc.bin"); +MODULE_FIRMWARE("radeon/KABINI_sdma.bin");
extern int r600_ih_ring_alloc(struct radeon_device *rdev); extern void r600_ih_ring_fini(struct radeon_device *rdev); @@ -198,7 +204,8 @@ static int cik_init_microcode(struct radeon_device *rdev) struct platform_device *pdev; const char *chip_name; size_t pfp_req_size, me_req_size, ce_req_size, - mec_req_size, rlc_req_size, mc_req_size; + mec_req_size, rlc_req_size, mc_req_size, + sdma_req_size; char fw_name[30]; int err;
@@ -220,6 +227,7 @@ static int cik_init_microcode(struct radeon_device *rdev) mec_req_size = CIK_MEC_UCODE_SIZE * 4; rlc_req_size = BONAIRE_RLC_UCODE_SIZE * 4; mc_req_size = CIK_MC_UCODE_SIZE * 4; + sdma_req_size = CIK_SDMA_UCODE_SIZE * 4; break; case CHIP_KAVERI: chip_name = "KAVERI"; @@ -228,6 +236,7 @@ static int cik_init_microcode(struct radeon_device *rdev) ce_req_size = CIK_CE_UCODE_SIZE * 4; mec_req_size = CIK_MEC_UCODE_SIZE * 4; rlc_req_size = KV_RLC_UCODE_SIZE * 4; + sdma_req_size = CIK_SDMA_UCODE_SIZE * 4; break; case CHIP_KABINI: chip_name = "KABINI"; @@ -236,6 +245,7 @@ static int cik_init_microcode(struct radeon_device *rdev) ce_req_size = CIK_CE_UCODE_SIZE * 4; mec_req_size = CIK_MEC_UCODE_SIZE * 4; rlc_req_size = KB_RLC_UCODE_SIZE * 4; + sdma_req_size = CIK_SDMA_UCODE_SIZE * 4; break; default: BUG(); } @@ -298,6 +308,17 @@ static int cik_init_microcode(struct radeon_device *rdev) err = -EINVAL; }
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_sdma.bin", chip_name); + err = request_firmware(&rdev->sdma_fw, fw_name, &pdev->dev); + if (err) + goto out; + if (rdev->sdma_fw->size != sdma_req_size) { + printk(KERN_ERR + "cik_sdma: Bogus length %zu in firmware "%s"\n", + rdev->sdma_fw->size, fw_name); + err = -EINVAL; + } + /* No MC ucode on APUs */ if (!(rdev->flags & RADEON_IS_IGP)) { snprintf(fw_name, sizeof(fw_name), "radeon/%s_mc.bin", chip_name); @@ -1425,6 +1446,8 @@ static void cik_gpu_init(struct radeon_device *rdev) WREG32(GB_ADDR_CONFIG, gb_addr_config); WREG32(HDP_ADDR_CONFIG, gb_addr_config); WREG32(DMIF_ADDR_CALC, gb_addr_config); + WREG32(SDMA0_TILING_CONFIG + SDMA0_REGISTER_OFFSET, gb_addr_config & 0x70); + WREG32(SDMA0_TILING_CONFIG + SDMA1_REGISTER_OFFSET, gb_addr_config & 0x70);
cik_tiling_mode_table_init(rdev);
@@ -2136,6 +2159,578 @@ static int cik_cp_resume(struct radeon_device *rdev) return 0; }
+/* + * sDMA - System DMA + * Starting with CIK, the GPU has new asynchronous + * DMA engines. These engines are used for compute + * and gfx. There are two DMA engines (SDMA0, SDMA1) + * and each one supports 1 ring buffer used for gfx + * and 2 queues used for compute. + * + * The programming model is very similar to the CP + * (ring buffer, IBs, etc.), but sDMA has it's own + * packet format that is different from the PM4 format + * used by the CP. sDMA supports copying data, writing + * embedded data, solid fills, and a number of other + * things. It also has support for tiling/detiling of + * buffers. + */ +/** + * cik_sdma_ring_ib_execute - Schedule an IB on the DMA engine + * + * @rdev: radeon_device pointer + * @ib: IB object to schedule + * + * Schedule an IB in the DMA ring (CIK). + */ +void cik_sdma_ring_ib_execute(struct radeon_device *rdev, + struct radeon_ib *ib) +{ + struct radeon_ring *ring = &rdev->ring[ib->ring]; + u32 extra_bits = (ib->vm ? ib->vm->id : 0) & 0xf; + + if (rdev->wb.enabled) { + u32 next_rptr = ring->wptr + 5; + while ((next_rptr & 7) != 4) + next_rptr++; + next_rptr += 4; + radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_WRITE, SDMA_WRITE_SUB_OPCODE_LINEAR, 0)); + radeon_ring_write(ring, ring->next_rptr_gpu_addr & 0xfffffffc); + radeon_ring_write(ring, upper_32_bits(ring->next_rptr_gpu_addr) & 0xffffffff); + radeon_ring_write(ring, 1); /* number of DWs to follow */ + radeon_ring_write(ring, next_rptr); + } + + /* IB packet must end on a 8 DW boundary */ + while ((ring->wptr & 7) != 4) + radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_NOP, 0, 0)); + radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_INDIRECT_BUFFER, 0, extra_bits)); + radeon_ring_write(ring, ib->gpu_addr & 0xffffffe0); /* base must be 32 byte aligned */ + radeon_ring_write(ring, upper_32_bits(ib->gpu_addr) & 0xffffffff); + radeon_ring_write(ring, ib->length_dw); + +} + +/** + * cik_sdma_fence_ring_emit - emit a fence on the DMA ring + * + * @rdev: radeon_device pointer + * @fence: radeon fence object + * + * Add a DMA fence packet to the ring to write + * the fence seq number and DMA trap packet to generate + * an interrupt if needed (CIK). + */ +void cik_sdma_fence_ring_emit(struct radeon_device *rdev, + struct radeon_fence *fence) +{ + struct radeon_ring *ring = &rdev->ring[fence->ring]; + u64 addr = rdev->fence_drv[fence->ring].gpu_addr; + u32 extra_bits = (SDMA_POLL_REG_MEM_EXTRA_OP(1) | + SDMA_POLL_REG_MEM_EXTRA_FUNC(3)); /* == */ + u32 ref_and_mask; + + if (fence->ring == R600_RING_TYPE_DMA_INDEX) + ref_and_mask = SDMA0; + else + ref_and_mask = SDMA1; + + /* write the fence */ + radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_FENCE, 0, 0)); + radeon_ring_write(ring, addr & 0xffffffff); + radeon_ring_write(ring, upper_32_bits(addr) & 0xffffffff); + radeon_ring_write(ring, fence->seq); + /* generate an interrupt */ + radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_TRAP, 0, 0)); + /* flush HDP */ + radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_POLL_REG_MEM, 0, extra_bits)); + radeon_ring_write(ring, GPU_HDP_FLUSH_DONE); + radeon_ring_write(ring, GPU_HDP_FLUSH_REQ); + radeon_ring_write(ring, ref_and_mask); /* REFERENCE */ + radeon_ring_write(ring, ref_and_mask); /* MASK */ + radeon_ring_write(ring, (4 << 16) | 10); /* RETRY_COUNT, POLL_INTERVAL */ +} + +/** + * cik_sdma_semaphore_ring_emit - emit a semaphore on the dma ring + * + * @rdev: radeon_device pointer + * @ring: radeon_ring structure holding ring information + * @semaphore: radeon semaphore object + * @emit_wait: wait or signal semaphore + * + * Add a DMA semaphore packet to the ring wait on or signal + * other rings (CIK). + */ +void cik_sdma_semaphore_ring_emit(struct radeon_device *rdev, + struct radeon_ring *ring, + struct radeon_semaphore *semaphore, + bool emit_wait) +{ + u64 addr = semaphore->gpu_addr; + u32 extra_bits = emit_wait ? 0 : SDMA_SEMAPHORE_EXTRA_S; + + radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_SEMAPHORE, 0, extra_bits)); + radeon_ring_write(ring, addr & 0xfffffff8); + radeon_ring_write(ring, upper_32_bits(addr) & 0xffffffff); +} + +/** + * cik_sdma_gfx_stop - stop the gfx async dma engines + * + * @rdev: radeon_device pointer + * + * Stop the gfx async dma ring buffers (CIK). + */ +static void cik_sdma_gfx_stop(struct radeon_device *rdev) +{ + u32 rb_cntl, reg_offset; + int i; + + radeon_ttm_set_active_vram_size(rdev, rdev->mc.visible_vram_size); + + for (i = 0; i < 2; i++) { + if (i == 0) + reg_offset = SDMA0_REGISTER_OFFSET; + else + reg_offset = SDMA1_REGISTER_OFFSET; + rb_cntl = RREG32(SDMA0_GFX_RB_CNTL + reg_offset); + rb_cntl &= ~SDMA_RB_ENABLE; + WREG32(SDMA0_GFX_RB_CNTL + reg_offset, rb_cntl); + WREG32(SDMA0_GFX_IB_CNTL + reg_offset, 0); + } +} + +/** + * cik_sdma_rlc_stop - stop the compute async dma engines + * + * @rdev: radeon_device pointer + * + * Stop the compute async dma queues (CIK). + */ +static void cik_sdma_rlc_stop(struct radeon_device *rdev) +{ + /* XXX todo */ +} + +/** + * cik_sdma_enable - stop the async dma engines + * + * @rdev: radeon_device pointer + * @enable: enable/disable the DMA MEs. + * + * Halt or unhalt the async dma engines (CIK). + */ +static void cik_sdma_enable(struct radeon_device *rdev, bool enable) +{ + u32 me_cntl, reg_offset; + int i; + + for (i = 0; i < 2; i++) { + if (i == 0) + reg_offset = SDMA0_REGISTER_OFFSET; + else + reg_offset = SDMA1_REGISTER_OFFSET; + me_cntl = RREG32(SDMA0_ME_CNTL + reg_offset); + if (enable) + me_cntl &= ~SDMA_HALT; + else + me_cntl |= SDMA_HALT; + WREG32(SDMA0_ME_CNTL + reg_offset, me_cntl); + } +} + +/** + * cik_sdma_gfx_resume - setup and start the async dma engines + * + * @rdev: radeon_device pointer + * + * Set up the gfx DMA ring buffers and enable them (CIK). + * Returns 0 for success, error for failure. + */ +static int cik_sdma_gfx_resume(struct radeon_device *rdev) +{ + struct radeon_ring *ring; + u32 rb_cntl, ib_cntl; + u32 rb_bufsz; + u32 reg_offset, wb_offset; + int i, r; + + for (i = 0; i < 2; i++) { + if (i == 0) { + ring = &rdev->ring[R600_RING_TYPE_DMA_INDEX]; + reg_offset = SDMA0_REGISTER_OFFSET; + wb_offset = R600_WB_DMA_RPTR_OFFSET; + } else { + ring = &rdev->ring[CAYMAN_RING_TYPE_DMA1_INDEX]; + reg_offset = SDMA1_REGISTER_OFFSET; + wb_offset = CAYMAN_WB_DMA1_RPTR_OFFSET; + } + + WREG32(SDMA0_SEM_INCOMPLETE_TIMER_CNTL + reg_offset, 0); + WREG32(SDMA0_SEM_WAIT_FAIL_TIMER_CNTL + reg_offset, 0); + + /* Set ring buffer size in dwords */ + rb_bufsz = drm_order(ring->ring_size / 4); + rb_cntl = rb_bufsz << 1; +#ifdef __BIG_ENDIAN + rb_cntl |= SDMA_RB_SWAP_ENABLE | SDMA_RPTR_WRITEBACK_SWAP_ENABLE; +#endif + WREG32(SDMA0_GFX_RB_CNTL + reg_offset, rb_cntl); + + /* Initialize the ring buffer's read and write pointers */ + WREG32(SDMA0_GFX_RB_RPTR + reg_offset, 0); + WREG32(SDMA0_GFX_RB_WPTR + reg_offset, 0); + + /* set the wb address whether it's enabled or not */ + WREG32(SDMA0_GFX_RB_RPTR_ADDR_HI + reg_offset, + upper_32_bits(rdev->wb.gpu_addr + wb_offset) & 0xFFFFFFFF); + WREG32(SDMA0_GFX_RB_RPTR_ADDR_LO + reg_offset, + ((rdev->wb.gpu_addr + wb_offset) & 0xFFFFFFFC)); + + if (rdev->wb.enabled) + rb_cntl |= SDMA_RPTR_WRITEBACK_ENABLE; + + WREG32(SDMA0_GFX_RB_BASE + reg_offset, ring->gpu_addr >> 8); + WREG32(SDMA0_GFX_RB_BASE_HI + reg_offset, ring->gpu_addr >> 40); + + ring->wptr = 0; + WREG32(SDMA0_GFX_RB_WPTR + reg_offset, ring->wptr << 2); + + ring->rptr = RREG32(SDMA0_GFX_RB_RPTR + reg_offset) >> 2; + + /* enable DMA RB */ + WREG32(SDMA0_GFX_RB_CNTL + reg_offset, rb_cntl | SDMA_RB_ENABLE); + + ib_cntl = SDMA_IB_ENABLE; +#ifdef __BIG_ENDIAN + ib_cntl |= SDMA_IB_SWAP_ENABLE; +#endif + /* enable DMA IBs */ + WREG32(SDMA0_GFX_IB_CNTL + reg_offset, ib_cntl); + + ring->ready = true; + + r = radeon_ring_test(rdev, ring->idx, ring); + if (r) { + ring->ready = false; + return r; + } + } + + radeon_ttm_set_active_vram_size(rdev, rdev->mc.real_vram_size); + + return 0; +} + +/** + * cik_sdma_rlc_resume - setup and start the async dma engines + * + * @rdev: radeon_device pointer + * + * Set up the compute DMA queues and enable them (CIK). + * Returns 0 for success, error for failure. + */ +static int cik_sdma_rlc_resume(struct radeon_device *rdev) +{ + /* XXX todo */ + return 0; +} + +/** + * cik_sdma_load_microcode - load the sDMA ME ucode + * + * @rdev: radeon_device pointer + * + * Loads the sDMA0/1 ucode. + * Returns 0 for success, -EINVAL if the ucode is not available. + */ +static int cik_sdma_load_microcode(struct radeon_device *rdev) +{ + const __be32 *fw_data; + int i; + + if (!rdev->sdma_fw) + return -EINVAL; + + /* stop the gfx rings and rlc compute queues */ + cik_sdma_gfx_stop(rdev); + cik_sdma_rlc_stop(rdev); + + /* halt the MEs */ + cik_sdma_enable(rdev, false); + + /* sdma0 */ + fw_data = (const __be32 *)rdev->sdma_fw->data; + WREG32(SDMA0_UCODE_ADDR + SDMA0_REGISTER_OFFSET, 0); + for (i = 0; i < CIK_SDMA_UCODE_SIZE; i++) + WREG32(SDMA0_UCODE_DATA + SDMA0_REGISTER_OFFSET, be32_to_cpup(fw_data++)); + WREG32(SDMA0_UCODE_DATA + SDMA0_REGISTER_OFFSET, CIK_SDMA_UCODE_VERSION); + + /* sdma1 */ + fw_data = (const __be32 *)rdev->sdma_fw->data; + WREG32(SDMA0_UCODE_ADDR + SDMA1_REGISTER_OFFSET, 0); + for (i = 0; i < CIK_SDMA_UCODE_SIZE; i++) + WREG32(SDMA0_UCODE_DATA + SDMA1_REGISTER_OFFSET, be32_to_cpup(fw_data++)); + WREG32(SDMA0_UCODE_DATA + SDMA1_REGISTER_OFFSET, CIK_SDMA_UCODE_VERSION); + + WREG32(SDMA0_UCODE_ADDR + SDMA0_REGISTER_OFFSET, 0); + WREG32(SDMA0_UCODE_ADDR + SDMA1_REGISTER_OFFSET, 0); + return 0; +} + +/** + * cik_sdma_resume - setup and start the async dma engines + * + * @rdev: radeon_device pointer + * + * Set up the DMA engines and enable them (CIK). + * Returns 0 for success, error for failure. + */ +static int cik_sdma_resume(struct radeon_device *rdev) +{ + int r; + + /* Reset dma */ + WREG32(SRBM_SOFT_RESET, SOFT_RESET_SDMA | SOFT_RESET_SDMA1); + RREG32(SRBM_SOFT_RESET); + udelay(50); + WREG32(SRBM_SOFT_RESET, 0); + RREG32(SRBM_SOFT_RESET); + + r = cik_sdma_load_microcode(rdev); + if (r) + return r; + + /* unhalt the MEs */ + cik_sdma_enable(rdev, true); + + /* start the gfx rings and rlc compute queues */ + r = cik_sdma_gfx_resume(rdev); + if (r) + return r; + r = cik_sdma_rlc_resume(rdev); + if (r) + return r; + + return 0; +} + +/** + * cik_sdma_fini - tear down the async dma engines + * + * @rdev: radeon_device pointer + * + * Stop the async dma engines and free the rings (CIK). + */ +static void cik_sdma_fini(struct radeon_device *rdev) +{ + /* stop the gfx rings and rlc compute queues */ + cik_sdma_gfx_stop(rdev); + cik_sdma_rlc_stop(rdev); + /* halt the MEs */ + cik_sdma_enable(rdev, false); + radeon_ring_fini(rdev, &rdev->ring[R600_RING_TYPE_DMA_INDEX]); + radeon_ring_fini(rdev, &rdev->ring[CAYMAN_RING_TYPE_DMA1_INDEX]); + /* XXX - compute dma queue tear down */ +} + +/** + * cik_copy_dma - copy pages using the DMA engine + * + * @rdev: radeon_device pointer + * @src_offset: src GPU address + * @dst_offset: dst GPU address + * @num_gpu_pages: number of GPU pages to xfer + * @fence: radeon fence object + * + * Copy GPU paging using the DMA engine (CIK). + * Used by the radeon ttm implementation to move pages if + * registered as the asic copy callback. + */ +int cik_copy_dma(struct radeon_device *rdev, + uint64_t src_offset, uint64_t dst_offset, + unsigned num_gpu_pages, + struct radeon_fence **fence) +{ + struct radeon_semaphore *sem = NULL; + int ring_index = rdev->asic->copy.dma_ring_index; + struct radeon_ring *ring = &rdev->ring[ring_index]; + u32 size_in_bytes, cur_size_in_bytes; + int i, num_loops; + int r = 0; + + r = radeon_semaphore_create(rdev, &sem); + if (r) { + DRM_ERROR("radeon: moving bo (%d).\n", r); + return r; + } + + size_in_bytes = (num_gpu_pages << RADEON_GPU_PAGE_SHIFT); + num_loops = DIV_ROUND_UP(size_in_bytes, 0x1fffff); + r = radeon_ring_lock(rdev, ring, num_loops * 7 + 14); + if (r) { + DRM_ERROR("radeon: moving bo (%d).\n", r); + radeon_semaphore_free(rdev, &sem, NULL); + return r; + } + + if (radeon_fence_need_sync(*fence, ring->idx)) { + radeon_semaphore_sync_rings(rdev, sem, (*fence)->ring, + ring->idx); + radeon_fence_note_sync(*fence, ring->idx); + } else { + radeon_semaphore_free(rdev, &sem, NULL); + } + + for (i = 0; i < num_loops; i++) { + cur_size_in_bytes = size_in_bytes; + if (cur_size_in_bytes > 0x1fffff) + cur_size_in_bytes = 0x1fffff; + size_in_bytes -= cur_size_in_bytes; + radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_COPY, SDMA_COPY_SUB_OPCODE_LINEAR, 0)); + radeon_ring_write(ring, cur_size_in_bytes); + radeon_ring_write(ring, 0); /* src/dst endian swap */ + radeon_ring_write(ring, src_offset & 0xffffffff); + radeon_ring_write(ring, upper_32_bits(src_offset) & 0xffffffff); + radeon_ring_write(ring, dst_offset & 0xfffffffc); + radeon_ring_write(ring, upper_32_bits(dst_offset) & 0xffffffff); + src_offset += cur_size_in_bytes; + dst_offset += cur_size_in_bytes; + } + + r = radeon_fence_emit(rdev, fence, ring->idx); + if (r) { + radeon_ring_unlock_undo(rdev, ring); + return r; + } + + radeon_ring_unlock_commit(rdev, ring); + radeon_semaphore_free(rdev, &sem, *fence); + + return r; +} + +/** + * cik_sdma_ring_test - simple async dma engine test + * + * @rdev: radeon_device pointer + * @ring: radeon_ring structure holding ring information + * + * Test the DMA engine by writing using it to write an + * value to memory. (CIK). + * Returns 0 for success, error for failure. + */ +int cik_sdma_ring_test(struct radeon_device *rdev, + struct radeon_ring *ring) +{ + unsigned i; + int r; + void __iomem *ptr = (void *)rdev->vram_scratch.ptr; + u32 tmp; + + if (!ptr) { + DRM_ERROR("invalid vram scratch pointer\n"); + return -EINVAL; + } + + tmp = 0xCAFEDEAD; + writel(tmp, ptr); + + r = radeon_ring_lock(rdev, ring, 4); + if (r) { + DRM_ERROR("radeon: dma failed to lock ring %d (%d).\n", ring->idx, r); + return r; + } + radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_WRITE, SDMA_WRITE_SUB_OPCODE_LINEAR, 0)); + radeon_ring_write(ring, rdev->vram_scratch.gpu_addr & 0xfffffffc); + radeon_ring_write(ring, upper_32_bits(rdev->vram_scratch.gpu_addr) & 0xffffffff); + radeon_ring_write(ring, 1); /* number of DWs to follow */ + radeon_ring_write(ring, 0xDEADBEEF); + radeon_ring_unlock_commit(rdev, ring); + + for (i = 0; i < rdev->usec_timeout; i++) { + tmp = readl(ptr); + if (tmp == 0xDEADBEEF) + break; + DRM_UDELAY(1); + } + + if (i < rdev->usec_timeout) { + DRM_INFO("ring test on %d succeeded in %d usecs\n", ring->idx, i); + } else { + DRM_ERROR("radeon: ring %d test failed (0x%08X)\n", + ring->idx, tmp); + r = -EINVAL; + } + return r; +} + +/** + * cik_sdma_ib_test - test an IB on the DMA engine + * + * @rdev: radeon_device pointer + * @ring: radeon_ring structure holding ring information + * + * Test a simple IB in the DMA ring (CIK). + * Returns 0 on success, error on failure. + */ +int cik_sdma_ib_test(struct radeon_device *rdev, struct radeon_ring *ring) +{ + struct radeon_ib ib; + unsigned i; + int r; + void __iomem *ptr = (void *)rdev->vram_scratch.ptr; + u32 tmp = 0; + + if (!ptr) { + DRM_ERROR("invalid vram scratch pointer\n"); + return -EINVAL; + } + + tmp = 0xCAFEDEAD; + writel(tmp, ptr); + + r = radeon_ib_get(rdev, ring->idx, &ib, NULL, 256); + if (r) { + DRM_ERROR("radeon: failed to get ib (%d).\n", r); + return r; + } + + ib.ptr[0] = SDMA_PACKET(SDMA_OPCODE_WRITE, SDMA_WRITE_SUB_OPCODE_LINEAR, 0); + ib.ptr[1] = rdev->vram_scratch.gpu_addr & 0xfffffffc; + ib.ptr[2] = upper_32_bits(rdev->vram_scratch.gpu_addr) & 0xffffffff; + ib.ptr[3] = 1; + ib.ptr[4] = 0xDEADBEEF; + ib.length_dw = 5; + + r = radeon_ib_schedule(rdev, &ib, NULL); + if (r) { + radeon_ib_free(rdev, &ib); + DRM_ERROR("radeon: failed to schedule ib (%d).\n", r); + return r; + } + r = radeon_fence_wait(ib.fence, false); + if (r) { + DRM_ERROR("radeon: fence wait failed (%d).\n", r); + return r; + } + for (i = 0; i < rdev->usec_timeout; i++) { + tmp = readl(ptr); + if (tmp == 0xDEADBEEF) + break; + DRM_UDELAY(1); + } + if (i < rdev->usec_timeout) { + DRM_INFO("ib test on ring %d succeeded in %u usecs\n", ib.fence->ring, i); + } else { + DRM_ERROR("radeon: ib test failed (0x%08X)\n", tmp); + r = -EINVAL; + } + radeon_ib_free(rdev, &ib); + return r; +} + /** * cik_gpu_is_lockup - check if the 3D engine is locked up * @@ -2330,6 +2925,32 @@ int cik_asic_reset(struct radeon_device *rdev) return cik_gfx_gpu_soft_reset(rdev); }
+/** + * cik_sdma_is_lockup - Check if the DMA engine is locked up + * + * @rdev: radeon_device pointer + * @ring: radeon_ring structure holding ring information + * + * Check if the async DMA engine is locked up (CIK). + * Returns true if the engine appears to be locked up, false if not. + */ +bool cik_sdma_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring) +{ + u32 dma_status_reg; + + if (ring->idx == R600_RING_TYPE_DMA_INDEX) + dma_status_reg = RREG32(SDMA0_STATUS_REG + SDMA0_REGISTER_OFFSET); + else + dma_status_reg = RREG32(SDMA0_STATUS_REG + SDMA1_REGISTER_OFFSET); + if (dma_status_reg & SDMA_IDLE) { + radeon_ring_lockup_update(ring); + return false; + } + /* force ring activities */ + radeon_ring_force_activity(rdev, ring); + return radeon_ring_test_lockup(rdev, ring); +} + /* MC */ /** * cik_mc_program - program the GPU memory controller @@ -2588,10 +3209,17 @@ static int cik_pcie_gart_enable(struct radeon_device *rdev) /* where to put LDS, scratch, GPUVM in FSA64 space */ for (i = 0; i < 16; i++) { WREG32(SRBM_GFX_CNTL, VMID(i)); + /* CP and shaders */ WREG32(SH_MEM_CONFIG, 0); WREG32(SH_MEM_APE1_BASE, 1); WREG32(SH_MEM_APE1_LIMIT, 0); WREG32(SH_MEM_BASES, 0); + /* SDMA GFX */ + WREG32(SDMA0_GFX_VIRTUAL_ADDR + SDMA0_REGISTER_OFFSET, 0); + WREG32(SDMA0_GFX_APE1_CNTL + SDMA0_REGISTER_OFFSET, 0); + WREG32(SDMA0_GFX_VIRTUAL_ADDR + SDMA1_REGISTER_OFFSET, 0); + WREG32(SDMA0_GFX_APE1_CNTL + SDMA1_REGISTER_OFFSET, 0); + /* XXX SDMA RLC - todo */ } WREG32(SRBM_GFX_CNTL, 0);
@@ -2992,6 +3620,11 @@ static void cik_disable_interrupt_state(struct radeon_device *rdev)
/* gfx ring */ WREG32(CP_INT_CNTL_RING0, CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE); + /* sdma */ + tmp = RREG32(SDMA0_CNTL + SDMA0_REGISTER_OFFSET) & ~TRAP_ENABLE; + WREG32(SDMA0_CNTL + SDMA0_REGISTER_OFFSET, tmp); + tmp = RREG32(SDMA0_CNTL + SDMA1_REGISTER_OFFSET) & ~TRAP_ENABLE; + WREG32(SDMA0_CNTL + SDMA1_REGISTER_OFFSET, tmp); /* compute queues */ WREG32(CP_ME1_PIPE0_INT_CNTL, 0); WREG32(CP_ME1_PIPE1_INT_CNTL, 0); @@ -3132,6 +3765,7 @@ int cik_irq_set(struct radeon_device *rdev) u32 crtc1 = 0, crtc2 = 0, crtc3 = 0, crtc4 = 0, crtc5 = 0, crtc6 = 0; u32 hpd1, hpd2, hpd3, hpd4, hpd5, hpd6; u32 grbm_int_cntl = 0; + u32 dma_cntl, dma_cntl1;
if (!rdev->irq.installed) { WARN(1, "Can't enable IRQ/MSI because no handler is installed\n"); @@ -3152,6 +3786,9 @@ int cik_irq_set(struct radeon_device *rdev) hpd5 = RREG32(DC_HPD5_INT_CONTROL) & ~DC_HPDx_INT_EN; hpd6 = RREG32(DC_HPD6_INT_CONTROL) & ~DC_HPDx_INT_EN;
+ dma_cntl = RREG32(SDMA0_CNTL + SDMA0_REGISTER_OFFSET) & ~TRAP_ENABLE; + dma_cntl1 = RREG32(SDMA0_CNTL + SDMA1_REGISTER_OFFSET) & ~TRAP_ENABLE; + /* enable CP interrupts on all rings */ if (atomic_read(&rdev->irq.ring_int[RADEON_RING_TYPE_GFX_INDEX])) { DRM_DEBUG("cik_irq_set: sw int gfx\n"); @@ -3160,6 +3797,16 @@ int cik_irq_set(struct radeon_device *rdev) /* TODO: compute queues! */ /* CP_ME[1-2]_PIPE[0-3]_INT_CNTL */
+ if (atomic_read(&rdev->irq.ring_int[R600_RING_TYPE_DMA_INDEX])) { + DRM_DEBUG("cik_irq_set: sw int dma\n"); + dma_cntl |= TRAP_ENABLE; + } + + if (atomic_read(&rdev->irq.ring_int[CAYMAN_RING_TYPE_DMA1_INDEX])) { + DRM_DEBUG("cik_irq_set: sw int dma1\n"); + dma_cntl1 |= TRAP_ENABLE; + } + if (rdev->irq.crtc_vblank_int[0] || atomic_read(&rdev->irq.pflip[0])) { DRM_DEBUG("cik_irq_set: vblank 0\n"); @@ -3217,6 +3864,9 @@ int cik_irq_set(struct radeon_device *rdev)
WREG32(CP_INT_CNTL_RING0, cp_int_cntl);
+ WREG32(SDMA0_CNTL + SDMA0_REGISTER_OFFSET, dma_cntl); + WREG32(SDMA0_CNTL + SDMA1_REGISTER_OFFSET, dma_cntl1); + WREG32(GRBM_INT_CNTL, grbm_int_cntl);
WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC0_REGISTER_OFFSET, crtc1); @@ -3410,12 +4060,18 @@ static inline u32 cik_get_ih_wptr(struct radeon_device *rdev) * [31:8] - reserved * [59:32] - interrupt source data * [63:60] - reserved - * [71:64] - RINGID: ME_ID [1:0], PIPE_ID[1:0], QUEUE_ID[2:0] + * [71:64] - RINGID + * CP: + * ME_ID [1:0], PIPE_ID[1:0], QUEUE_ID[2:0] * QUEUE_ID - for compute, which of the 8 queues owned by the dispatcher * - for gfx, hw shader state (0=PS...5=LS, 6=CS) * ME_ID - 0 = gfx, 1 = first 4 CS pipes, 2 = second 4 CS pipes * PIPE_ID - ME0 0=3D * - ME1&2 compute dispatcher (4 pipes each) + * SDMA: + * INSTANCE_ID [1:0], QUEUE_ID[1:0] + * INSTANCE_ID - 0 = sdma0, 1 = sdma1 + * QUEUE_ID - 0 = gfx, 1 = rlc0, 2 = rlc1 * [79:72] - VMID * [95:80] - PASID * [127:96] - reserved @@ -3465,10 +4121,6 @@ restart_ih: src_id = le32_to_cpu(rdev->ih.ring[ring_index]) & 0xff; src_data = le32_to_cpu(rdev->ih.ring[ring_index + 1]) & 0xfffffff; ring_id = le32_to_cpu(rdev->ih.ring[ring_index + 2]) & 0xff; - /* XXX check the bitfield order! */ - me_id = (ring_id & 0x60) >> 5; - pipe_id = (ring_id & 0x18) >> 3; - queue_id = (ring_id & 0x7) >> 0;
switch (src_id) { case 1: /* D1 vblank/vline */ @@ -3692,6 +4344,10 @@ restart_ih: break; case 181: /* CP EOP event */ DRM_DEBUG("IH: CP EOP\n"); + /* XXX check the bitfield order! */ + me_id = (ring_id & 0x60) >> 5; + pipe_id = (ring_id & 0x18) >> 3; + queue_id = (ring_id & 0x7) >> 0; switch (me_id) { case 0: radeon_fence_process(rdev, RADEON_RING_TYPE_GFX_INDEX); @@ -3727,6 +4383,10 @@ restart_ih: break; case 185: /* CP Privileged inst */ DRM_ERROR("Illegal instruction in command stream\n"); + /* XXX check the bitfield order! */ + me_id = (ring_id & 0x60) >> 5; + pipe_id = (ring_id & 0x18) >> 3; + queue_id = (ring_id & 0x7) >> 0; switch (me_id) { case 0: /* This results in a full GPU reset, but all we need to do is soft @@ -3742,6 +4402,79 @@ restart_ih: break; } break; + case 224: /* SDMA trap event */ + /* XXX check the bitfield order! */ + me_id = (ring_id & 0x3) >> 0; + queue_id = (ring_id & 0xc) >> 2; + DRM_DEBUG("IH: SDMA trap\n"); + switch (me_id) { + case 0: + switch (queue_id) { + case 0: + radeon_fence_process(rdev, R600_RING_TYPE_DMA_INDEX); + break; + case 1: + /* XXX compute */ + break; + case 2: + /* XXX compute */ + break; + } + break; + case 1: + switch (queue_id) { + case 0: + radeon_fence_process(rdev, CAYMAN_RING_TYPE_DMA1_INDEX); + break; + case 1: + /* XXX compute */ + break; + case 2: + /* XXX compute */ + break; + } + break; + } + break; + case 241: /* SDMA Privileged inst */ + case 247: /* SDMA Privileged inst */ + DRM_ERROR("Illegal instruction in SDMA command stream\n"); + /* XXX check the bitfield order! */ + me_id = (ring_id & 0x3) >> 0; + queue_id = (ring_id & 0xc) >> 2; + switch (me_id) { + case 0: + switch (queue_id) { + case 0: + queue_reset = true; + break; + case 1: + /* XXX compute */ + queue_reset = true; + break; + case 2: + /* XXX compute */ + queue_reset = true; + break; + } + break; + case 1: + switch (queue_id) { + case 0: + queue_reset = true; + break; + case 1: + /* XXX compute */ + queue_reset = true; + break; + case 2: + /* XXX compute */ + queue_reset = true; + break; + } + break; + } + break; case 233: /* GUI IDLE */ DRM_DEBUG("IH: GUI idle\n"); break; diff --git a/drivers/gpu/drm/radeon/cikd.h b/drivers/gpu/drm/radeon/cikd.h index cc4f28e..39ed517 100644 --- a/drivers/gpu/drm/radeon/cikd.h +++ b/drivers/gpu/drm/radeon/cikd.h @@ -42,6 +42,24 @@ #define SRBM_STATUS2 0xE4C #define SRBM_STATUS 0xE50
+#define SRBM_SOFT_RESET 0xE60 +#define SOFT_RESET_BIF (1 << 1) +#define SOFT_RESET_R0PLL (1 << 4) +#define SOFT_RESET_DC (1 << 5) +#define SOFT_RESET_SDMA1 (1 << 6) +#define SOFT_RESET_GRBM (1 << 8) +#define SOFT_RESET_HDP (1 << 9) +#define SOFT_RESET_IH (1 << 10) +#define SOFT_RESET_MC (1 << 11) +#define SOFT_RESET_ROM (1 << 14) +#define SOFT_RESET_SEM (1 << 15) +#define SOFT_RESET_VMC (1 << 17) +#define SOFT_RESET_SDMA (1 << 20) +#define SOFT_RESET_TST (1 << 21) +#define SOFT_RESET_REGBB (1 << 22) +#define SOFT_RESET_ORB (1 << 23) +#define SOFT_RESET_VCE (1 << 24) + #define VM_L2_CNTL 0x1400 #define ENABLE_L2_CACHE (1 << 0) #define ENABLE_L2_FRAGMENT_PROCESSING (1 << 1) @@ -1039,4 +1057,116 @@ #define PACKET3_WAIT_ON_DE_COUNTER_DIFF 0x88 #define PACKET3_SWITCH_BUFFER 0x8B
+/* SDMA - first instance at 0xd000, second at 0xd800 */ +#define SDMA0_REGISTER_OFFSET 0x0 /* not a register */ +#define SDMA1_REGISTER_OFFSET 0x800 /* not a register */ + +#define SDMA0_UCODE_ADDR 0xD000 +#define SDMA0_UCODE_DATA 0xD004 + +#define SDMA0_CNTL 0xD010 +# define TRAP_ENABLE (1 << 0) +# define SEM_INCOMPLETE_INT_ENABLE (1 << 1) +# define SEM_WAIT_INT_ENABLE (1 << 2) +# define DATA_SWAP_ENABLE (1 << 3) +# define FENCE_SWAP_ENABLE (1 << 4) +# define AUTO_CTXSW_ENABLE (1 << 18) +# define CTXEMPTY_INT_ENABLE (1 << 28) + +#define SDMA0_TILING_CONFIG 0xD018 + +#define SDMA0_SEM_INCOMPLETE_TIMER_CNTL 0xD020 +#define SDMA0_SEM_WAIT_FAIL_TIMER_CNTL 0xD024 + +#define SDMA0_STATUS_REG 0xd034 +# define SDMA_IDLE (1 << 0) + +#define SDMA0_ME_CNTL 0xD048 +# define SDMA_HALT (1 << 0) + +#define SDMA0_GFX_RB_CNTL 0xD200 +# define SDMA_RB_ENABLE (1 << 0) +# define SDMA_RB_SIZE(x) ((x) << 1) /* log2 */ +# define SDMA_RB_SWAP_ENABLE (1 << 9) /* 8IN32 */ +# define SDMA_RPTR_WRITEBACK_ENABLE (1 << 12) +# define SDMA_RPTR_WRITEBACK_SWAP_ENABLE (1 << 13) /* 8IN32 */ +# define SDMA_RPTR_WRITEBACK_TIMER(x) ((x) << 16) /* log2 */ +#define SDMA0_GFX_RB_BASE 0xD204 +#define SDMA0_GFX_RB_BASE_HI 0xD208 +#define SDMA0_GFX_RB_RPTR 0xD20C +#define SDMA0_GFX_RB_WPTR 0xD210 + +#define SDMA0_GFX_RB_RPTR_ADDR_HI 0xD220 +#define SDMA0_GFX_RB_RPTR_ADDR_LO 0xD224 +#define SDMA0_GFX_IB_CNTL 0xD228 +# define SDMA_IB_ENABLE (1 << 0) +# define SDMA_IB_SWAP_ENABLE (1 << 4) +# define SDMA_SWITCH_INSIDE_IB (1 << 8) +# define SDMA_CMD_VMID(x) ((x) << 16) + +#define SDMA0_GFX_VIRTUAL_ADDR 0xD29C +#define SDMA0_GFX_APE1_CNTL 0xD2A0 + +#define SDMA_PACKET(op, sub_op, e) ((((e) & 0xFFFF) << 16) | \ + (((sub_op) & 0xFF) << 8) | \ + (((op) & 0xFF) << 0)) +/* sDMA opcodes */ +#define SDMA_OPCODE_NOP 0 +#define SDMA_OPCODE_COPY 1 +# define SDMA_COPY_SUB_OPCODE_LINEAR 0 +# define SDMA_COPY_SUB_OPCODE_TILED 1 +# define SDMA_COPY_SUB_OPCODE_SOA 3 +# define SDMA_COPY_SUB_OPCODE_LINEAR_SUB_WINDOW 4 +# define SDMA_COPY_SUB_OPCODE_TILED_SUB_WINDOW 5 +# define SDMA_COPY_SUB_OPCODE_T2T_SUB_WINDOW 6 +#define SDMA_OPCODE_WRITE 2 +# define SDMA_WRITE_SUB_OPCODE_LINEAR 0 +# define SDMA_WRTIE_SUB_OPCODE_TILED 1 +#define SDMA_OPCODE_INDIRECT_BUFFER 4 +#define SDMA_OPCODE_FENCE 5 +#define SDMA_OPCODE_TRAP 6 +#define SDMA_OPCODE_SEMAPHORE 7 +# define SDMA_SEMAPHORE_EXTRA_O (1 << 13) + /* 0 - increment + * 1 - write 1 + */ +# define SDMA_SEMAPHORE_EXTRA_S (1 << 14) + /* 0 - wait + * 1 - signal + */ +# define SDMA_SEMAPHORE_EXTRA_M (1 << 15) + /* mailbox */ +#define SDMA_OPCODE_POLL_REG_MEM 8 +# define SDMA_POLL_REG_MEM_EXTRA_OP(x) ((x) << 10) + /* 0 - wait_reg_mem + * 1 - wr_wait_wr_reg + */ +# define SDMA_POLL_REG_MEM_EXTRA_FUNC(x) ((x) << 12) + /* 0 - always + * 1 - < + * 2 - <= + * 3 - == + * 4 - != + * 5 - >= + * 6 - > + */ +# define SDMA_POLL_REG_MEM_EXTRA_M (1 << 15) + /* 0 = register + * 1 = memory + */ +#define SDMA_OPCODE_COND_EXEC 9 +#define SDMA_OPCODE_CONSTANT_FILL 11 +# define SDMA_CONSTANT_FILL_EXTRA_SIZE(x) ((x) << 14) + /* 0 = byte fill + * 2 = DW fill + */ +#define SDMA_OPCODE_GENERATE_PTE_PDE 12 +#define SDMA_OPCODE_TIMESTAMP 13 +# define SDMA_TIMESTAMP_SUB_OPCODE_SET_LOCAL 0 +# define SDMA_TIMESTAMP_SUB_OPCODE_GET_LOCAL 1 +# define SDMA_TIMESTAMP_SUB_OPCODE_GET_GLOBAL 2 +#define SDMA_OPCODE_SRBM_WRITE 14 +# define SDMA_SRBM_WRITE_EXTRA_BYTE_ENABLE(x) ((x) << 12) + /* byte mask */ + #endif diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index e09157b..919c4d8 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1726,6 +1726,7 @@ struct radeon_device { const struct firmware *ce_fw; /* SI CE firmware */ const struct firmware *uvd_fw; /* UVD firmware */ const struct firmware *mec_fw; /* CIK MEC firmware */ + const struct firmware *sdma_fw; /* CIK SDMA firmware */ struct r600_blit r600_blit; struct r600_vram_scratch vram_scratch; int msi_enabled; /* msi enabled */
From: Alex Deucher alexander.deucher@amd.com
Update the page table base address and flush the VM TLB using the sDMA.
V2: update for 2 level PTs V3: update vm flush V4: update SH_MEM* regs V5: switch back to old style VM TLB invalidate V6: fix packet formatting
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/cik.c | 70 ++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 70 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 931169e..3c18a63 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -3407,6 +3407,76 @@ void cik_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm) radeon_ring_write(ring, 0x0); }
+/** + * cik_dma_vm_flush - cik vm flush using sDMA + * + * @rdev: radeon_device pointer + * + * Update the page table base and flush the VM TLB + * using sDMA (CIK). + */ +void cik_dma_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm) +{ + struct radeon_ring *ring = &rdev->ring[ridx]; + u32 extra_bits = (SDMA_POLL_REG_MEM_EXTRA_OP(1) | + SDMA_POLL_REG_MEM_EXTRA_FUNC(3)); /* == */ + u32 ref_and_mask; + + if (vm == NULL) + return; + + if (ridx == R600_RING_TYPE_DMA_INDEX) + ref_and_mask = SDMA0; + else + ref_and_mask = SDMA1; + + radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_SRBM_WRITE, 0, 0xf000)); + if (vm->id < 8) { + radeon_ring_write(ring, (VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + (vm->id << 2)) >> 2); + } else { + radeon_ring_write(ring, (VM_CONTEXT8_PAGE_TABLE_BASE_ADDR + ((vm->id - 8) << 2)) >> 2); + } + radeon_ring_write(ring, vm->pd_gpu_addr >> 12); + + /* update SH_MEM_* regs */ + radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_SRBM_WRITE, 0, 0xf000)); + radeon_ring_write(ring, SRBM_GFX_CNTL >> 2); + radeon_ring_write(ring, VMID(vm->id)); + + radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_SRBM_WRITE, 0, 0xf000)); + radeon_ring_write(ring, SH_MEM_BASES >> 2); + radeon_ring_write(ring, 0); + + radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_SRBM_WRITE, 0, 0xf000)); + radeon_ring_write(ring, SH_MEM_CONFIG >> 2); + radeon_ring_write(ring, 0); + + radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_SRBM_WRITE, 0, 0xf000)); + radeon_ring_write(ring, SH_MEM_APE1_BASE >> 2); + radeon_ring_write(ring, 1); + + radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_SRBM_WRITE, 0, 0xf000)); + radeon_ring_write(ring, SH_MEM_APE1_LIMIT >> 2); + radeon_ring_write(ring, 0); + + radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_SRBM_WRITE, 0, 0xf000)); + radeon_ring_write(ring, SRBM_GFX_CNTL >> 2); + radeon_ring_write(ring, VMID(0)); + + /* flush HDP */ + radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_POLL_REG_MEM, 0, extra_bits)); + radeon_ring_write(ring, GPU_HDP_FLUSH_DONE); + radeon_ring_write(ring, GPU_HDP_FLUSH_REQ); + radeon_ring_write(ring, ref_and_mask); /* REFERENCE */ + radeon_ring_write(ring, ref_and_mask); /* MASK */ + radeon_ring_write(ring, (4 << 16) | 10); /* RETRY_COUNT, POLL_INTERVAL */ + + /* flush TLB */ + radeon_ring_write(ring, SDMA_PACKET(SDMA_OPCODE_SRBM_WRITE, 0, 0xf000)); + radeon_ring_write(ring, VM_INVALIDATE_REQUEST >> 2); + radeon_ring_write(ring, 1 << vm->id); +} + /* * RLC * The RLC is a multi-purpose microengine that handles a
From: Alex Deucher alexander.deucher@amd.com
Async page table updates using the sDMA engine. sDMA has a special packet for updating entries for contiguous pages that reduces overhead.
v2: add support for and use the CP for now. v3: update for 2 level PTs v4: rebase, fix DMA packet v5: switch to using an IB
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/cik.c | 109 ++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 109 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 3c18a63..cf1e0b1 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -3408,6 +3408,115 @@ void cik_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm) }
/** + * cik_vm_set_page - update the page tables using sDMA + * + * @rdev: radeon_device pointer + * @ib: indirect buffer to fill with commands + * @pe: addr of the page entry + * @addr: dst addr to write into pe + * @count: number of page entries to update + * @incr: increase next addr by incr bytes + * @flags: access flags + * + * Update the page tables using CP or sDMA (CIK). + */ +void cik_vm_set_page(struct radeon_device *rdev, + struct radeon_ib *ib, + uint64_t pe, + uint64_t addr, unsigned count, + uint32_t incr, uint32_t flags) +{ + uint32_t r600_flags = cayman_vm_page_flags(rdev, flags); + uint64_t value; + unsigned ndw; + + if (rdev->asic->vm.pt_ring_index == RADEON_RING_TYPE_GFX_INDEX) { + /* CP */ + while (count) { + ndw = 2 + count * 2; + if (ndw > 0x3FFE) + ndw = 0x3FFE; + + ib->ptr[ib->length_dw++] = PACKET3(PACKET3_WRITE_DATA, ndw); + ib->ptr[ib->length_dw++] = (WRITE_DATA_ENGINE_SEL(0) | + WRITE_DATA_DST_SEL(1)); + ib->ptr[ib->length_dw++] = pe; + ib->ptr[ib->length_dw++] = upper_32_bits(pe); + for (; ndw > 2; ndw -= 2, --count, pe += 8) { + if (flags & RADEON_VM_PAGE_SYSTEM) { + value = radeon_vm_map_gart(rdev, addr); + value &= 0xFFFFFFFFFFFFF000ULL; + } else if (flags & RADEON_VM_PAGE_VALID) { + value = addr; + } else { + value = 0; + } + addr += incr; + value |= r600_flags; + ib->ptr[ib->length_dw++] = value; + ib->ptr[ib->length_dw++] = upper_32_bits(value); + } + } + } else { + /* DMA */ + if (flags & RADEON_VM_PAGE_SYSTEM) { + while (count) { + ndw = count * 2; + if (ndw > 0xFFFFE) + ndw = 0xFFFFE; + + /* for non-physically contiguous pages (system) */ + ib->ptr[ib->length_dw++] = SDMA_PACKET(SDMA_OPCODE_WRITE, SDMA_WRITE_SUB_OPCODE_LINEAR, 0); + ib->ptr[ib->length_dw++] = pe; + ib->ptr[ib->length_dw++] = upper_32_bits(pe); + ib->ptr[ib->length_dw++] = ndw; + for (; ndw > 0; ndw -= 2, --count, pe += 8) { + if (flags & RADEON_VM_PAGE_SYSTEM) { + value = radeon_vm_map_gart(rdev, addr); + value &= 0xFFFFFFFFFFFFF000ULL; + } else if (flags & RADEON_VM_PAGE_VALID) { + value = addr; + } else { + value = 0; + } + addr += incr; + value |= r600_flags; + ib->ptr[ib->length_dw++] = value; + ib->ptr[ib->length_dw++] = upper_32_bits(value); + } + } + } else { + while (count) { + ndw = count; + if (ndw > 0x7FFFF) + ndw = 0x7FFFF; + + if (flags & RADEON_VM_PAGE_VALID) + value = addr; + else + value = 0; + /* for physically contiguous pages (vram) */ + ib->ptr[ib->length_dw++] = SDMA_PACKET(SDMA_OPCODE_GENERATE_PTE_PDE, 0, 0); + ib->ptr[ib->length_dw++] = pe; /* dst addr */ + ib->ptr[ib->length_dw++] = upper_32_bits(pe); + ib->ptr[ib->length_dw++] = r600_flags; /* mask */ + ib->ptr[ib->length_dw++] = 0; + ib->ptr[ib->length_dw++] = value; /* value */ + ib->ptr[ib->length_dw++] = upper_32_bits(value); + ib->ptr[ib->length_dw++] = incr; /* increment size */ + ib->ptr[ib->length_dw++] = 0; + ib->ptr[ib->length_dw++] = ndw; /* number of entries */ + pe += ndw * 8; + addr += ndw * incr; + count -= ndw; + } + } + while (ib->length_dw & 0x7) + ib->ptr[ib->length_dw++] = SDMA_PACKET(SDMA_OPCODE_NOP, 0, 0); + } +} + +/** * cik_dma_vm_flush - cik vm flush using sDMA * * @rdev: radeon_device pointer
From: Alex Deucher alexander.deucher@amd.com
v2: update to latest driver changes v3: properly tear down vm on suspend v4: fix up irq init ordering
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/cik.c | 340 ++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 340 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index cf1e0b1..cbc64a2 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -73,6 +73,8 @@ extern void r600_ih_ring_fini(struct radeon_device *rdev); extern void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_save *save); extern void evergreen_mc_resume(struct radeon_device *rdev, struct evergreen_mc_save *save); extern void si_vram_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc); +extern void si_rlc_fini(struct radeon_device *rdev); +extern int si_rlc_init(struct radeon_device *rdev);
#define BONAIRE_IO_MC_REGS_SIZE 36
@@ -4681,3 +4683,341 @@ restart_ih:
return IRQ_HANDLED; } + +/* + * startup/shutdown callbacks + */ +/** + * cik_startup - program the asic to a functional state + * + * @rdev: radeon_device pointer + * + * Programs the asic to a functional state (CIK). + * Called by cik_init() and cik_resume(). + * Returns 0 for success, error for failure. + */ +static int cik_startup(struct radeon_device *rdev) +{ + struct radeon_ring *ring; + int r; + + if (rdev->flags & RADEON_IS_IGP) { + if (!rdev->me_fw || !rdev->pfp_fw || !rdev->ce_fw || + !rdev->mec_fw || !rdev->sdma_fw || !rdev->rlc_fw) { + r = cik_init_microcode(rdev); + if (r) { + DRM_ERROR("Failed to load firmware!\n"); + return r; + } + } + } else { + if (!rdev->me_fw || !rdev->pfp_fw || !rdev->ce_fw || + !rdev->mec_fw || !rdev->sdma_fw || !rdev->rlc_fw || + !rdev->mc_fw) { + r = cik_init_microcode(rdev); + if (r) { + DRM_ERROR("Failed to load firmware!\n"); + return r; + } + } + + r = ci_mc_load_microcode(rdev); + if (r) { + DRM_ERROR("Failed to load MC firmware!\n"); + return r; + } + } + + r = r600_vram_scratch_init(rdev); + if (r) + return r; + + cik_mc_program(rdev); + r = cik_pcie_gart_enable(rdev); + if (r) + return r; + cik_gpu_init(rdev); + + /* allocate rlc buffers */ + r = si_rlc_init(rdev); + if (r) { + DRM_ERROR("Failed to init rlc BOs!\n"); + return r; + } + + /* allocate wb buffer */ + r = radeon_wb_init(rdev); + if (r) + return r; + + r = radeon_fence_driver_start_ring(rdev, RADEON_RING_TYPE_GFX_INDEX); + if (r) { + dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r); + return r; + } + + r = radeon_fence_driver_start_ring(rdev, R600_RING_TYPE_DMA_INDEX); + if (r) { + dev_err(rdev->dev, "failed initializing DMA fences (%d).\n", r); + return r; + } + + r = radeon_fence_driver_start_ring(rdev, CAYMAN_RING_TYPE_DMA1_INDEX); + if (r) { + dev_err(rdev->dev, "failed initializing DMA fences (%d).\n", r); + return r; + } + + /* Enable IRQ */ + if (!rdev->irq.installed) { + r = radeon_irq_kms_init(rdev); + if (r) + return r; + } + + r = cik_irq_init(rdev); + if (r) { + DRM_ERROR("radeon: IH init failed (%d).\n", r); + radeon_irq_kms_fini(rdev); + return r; + } + cik_irq_set(rdev); + + ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + r = radeon_ring_init(rdev, ring, ring->ring_size, RADEON_WB_CP_RPTR_OFFSET, + CP_RB0_RPTR, CP_RB0_WPTR, + 0, 0xfffff, RADEON_CP_PACKET2); + if (r) + return r; + + ring = &rdev->ring[R600_RING_TYPE_DMA_INDEX]; + r = radeon_ring_init(rdev, ring, ring->ring_size, R600_WB_DMA_RPTR_OFFSET, + SDMA0_GFX_RB_RPTR + SDMA0_REGISTER_OFFSET, + SDMA0_GFX_RB_WPTR + SDMA0_REGISTER_OFFSET, + 2, 0xfffffffc, SDMA_PACKET(SDMA_OPCODE_NOP, 0, 0)); + if (r) + return r; + + ring = &rdev->ring[CAYMAN_RING_TYPE_DMA1_INDEX]; + r = radeon_ring_init(rdev, ring, ring->ring_size, CAYMAN_WB_DMA1_RPTR_OFFSET, + SDMA0_GFX_RB_RPTR + SDMA1_REGISTER_OFFSET, + SDMA0_GFX_RB_WPTR + SDMA1_REGISTER_OFFSET, + 2, 0xfffffffc, SDMA_PACKET(SDMA_OPCODE_NOP, 0, 0)); + if (r) + return r; + + r = cik_cp_resume(rdev); + if (r) + return r; + + r = cik_sdma_resume(rdev); + if (r) + return r; + + r = radeon_ib_pool_init(rdev); + if (r) { + dev_err(rdev->dev, "IB initialization failed (%d).\n", r); + return r; + } + + r = radeon_vm_manager_init(rdev); + if (r) { + dev_err(rdev->dev, "vm manager initialization failed (%d).\n", r); + return r; + } + + return 0; +} + +/** + * cik_resume - resume the asic to a functional state + * + * @rdev: radeon_device pointer + * + * Programs the asic to a functional state (CIK). + * Called at resume. + * Returns 0 for success, error for failure. + */ +int cik_resume(struct radeon_device *rdev) +{ + int r; + + /* Do not reset GPU before posting, on rv770 hw unlike on r500 hw, + * posting will perform necessary task to bring back GPU into good + * shape. + */ + /* post card */ + atom_asic_init(rdev->mode_info.atom_context); + + rdev->accel_working = true; + r = cik_startup(rdev); + if (r) { + DRM_ERROR("cik startup failed on resume\n"); + rdev->accel_working = false; + return r; + } + + return r; + +} + +/** + * cik_suspend - suspend the asic + * + * @rdev: radeon_device pointer + * + * Bring the chip into a state suitable for suspend (CIK). + * Called at suspend. + * Returns 0 for success. + */ +int cik_suspend(struct radeon_device *rdev) +{ + radeon_vm_manager_fini(rdev); + cik_cp_enable(rdev, false); + cik_sdma_enable(rdev, false); + cik_irq_suspend(rdev); + radeon_wb_disable(rdev); + cik_pcie_gart_disable(rdev); + return 0; +} + +/* Plan is to move initialization in that function and use + * helper function so that radeon_device_init pretty much + * do nothing more than calling asic specific function. This + * should also allow to remove a bunch of callback function + * like vram_info. + */ +/** + * cik_init - asic specific driver and hw init + * + * @rdev: radeon_device pointer + * + * Setup asic specific driver variables and program the hw + * to a functional state (CIK). + * Called at driver startup. + * Returns 0 for success, errors for failure. + */ +int cik_init(struct radeon_device *rdev) +{ + struct radeon_ring *ring; + int r; + + /* Read BIOS */ + if (!radeon_get_bios(rdev)) { + if (ASIC_IS_AVIVO(rdev)) + return -EINVAL; + } + /* Must be an ATOMBIOS */ + if (!rdev->is_atom_bios) { + dev_err(rdev->dev, "Expecting atombios for cayman GPU\n"); + return -EINVAL; + } + r = radeon_atombios_init(rdev); + if (r) + return r; + + /* Post card if necessary */ + if (!radeon_card_posted(rdev)) { + if (!rdev->bios) { + dev_err(rdev->dev, "Card not posted and no BIOS - ignoring\n"); + return -EINVAL; + } + DRM_INFO("GPU not posted. posting now...\n"); + atom_asic_init(rdev->mode_info.atom_context); + } + /* Initialize scratch registers */ + cik_scratch_init(rdev); + /* Initialize surface registers */ + radeon_surface_init(rdev); + /* Initialize clocks */ + radeon_get_clock_info(rdev->ddev); + + /* Fence driver */ + r = radeon_fence_driver_init(rdev); + if (r) + return r; + + /* initialize memory controller */ + r = cik_mc_init(rdev); + if (r) + return r; + /* Memory manager */ + r = radeon_bo_init(rdev); + if (r) + return r; + + ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]; + ring->ring_obj = NULL; + r600_ring_init(rdev, ring, 1024 * 1024); + + ring = &rdev->ring[R600_RING_TYPE_DMA_INDEX]; + ring->ring_obj = NULL; + r600_ring_init(rdev, ring, 256 * 1024); + + ring = &rdev->ring[CAYMAN_RING_TYPE_DMA1_INDEX]; + ring->ring_obj = NULL; + r600_ring_init(rdev, ring, 256 * 1024); + + rdev->ih.ring_obj = NULL; + r600_ih_ring_init(rdev, 64 * 1024); + + r = r600_pcie_gart_init(rdev); + if (r) + return r; + + rdev->accel_working = true; + r = cik_startup(rdev); + if (r) { + dev_err(rdev->dev, "disabling GPU acceleration\n"); + cik_cp_fini(rdev); + cik_sdma_fini(rdev); + cik_irq_fini(rdev); + si_rlc_fini(rdev); + radeon_wb_fini(rdev); + radeon_ib_pool_fini(rdev); + radeon_vm_manager_fini(rdev); + radeon_irq_kms_fini(rdev); + cik_pcie_gart_fini(rdev); + rdev->accel_working = false; + } + + /* Don't start up if the MC ucode is missing. + * The default clocks and voltages before the MC ucode + * is loaded are not suffient for advanced operations. + */ + if (!rdev->mc_fw && !(rdev->flags & RADEON_IS_IGP)) { + DRM_ERROR("radeon: MC ucode required for NI+.\n"); + return -EINVAL; + } + + return 0; +} + +/** + * cik_fini - asic specific driver and hw fini + * + * @rdev: radeon_device pointer + * + * Tear down the asic specific driver variables and program the hw + * to an idle state (CIK). + * Called at driver unload. + */ +void cik_fini(struct radeon_device *rdev) +{ + cik_cp_fini(rdev); + cik_sdma_fini(rdev); + cik_irq_fini(rdev); + si_rlc_fini(rdev); + radeon_wb_fini(rdev); + radeon_vm_manager_fini(rdev); + radeon_ib_pool_fini(rdev); + radeon_irq_kms_fini(rdev); + cik_pcie_gart_fini(rdev); + r600_vram_scratch_fini(rdev); + radeon_gem_fini(rdev); + radeon_fence_driver_fini(rdev); + radeon_bo_fini(rdev); + radeon_atombios_fini(rdev); + kfree(rdev->bios); + rdev->bios = NULL; +}
Am 26.06.2013 15:21, schrieb alexdeucher@gmail.com:
From: Alex Deucher alexander.deucher@amd.com
v2: update to latest driver changes v3: properly tear down vm on suspend v4: fix up irq init ordering
Signed-off-by: Alex Deucher alexander.deucher@amd.com
Just a copy&paste error, but some comments are referring to cayman and r500/r700 generations.
With that fixed the patch is:
Reviewed-by: Christian König christian.koenig@amd.com
drivers/gpu/drm/radeon/cik.c | 340 ++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 340 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index cf1e0b1..cbc64a2 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -73,6 +73,8 @@ extern void r600_ih_ring_fini(struct radeon_device *rdev); extern void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_save *save); extern void evergreen_mc_resume(struct radeon_device *rdev, struct evergreen_mc_save *save); extern void si_vram_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc); +extern void si_rlc_fini(struct radeon_device *rdev); +extern int si_rlc_init(struct radeon_device *rdev);
#define BONAIRE_IO_MC_REGS_SIZE 36
@@ -4681,3 +4683,341 @@ restart_ih:
return IRQ_HANDLED; }
+/*
- startup/shutdown callbacks
- */
+/**
- cik_startup - program the asic to a functional state
- @rdev: radeon_device pointer
- Programs the asic to a functional state (CIK).
- Called by cik_init() and cik_resume().
- Returns 0 for success, error for failure.
- */
+static int cik_startup(struct radeon_device *rdev) +{
- struct radeon_ring *ring;
- int r;
- if (rdev->flags & RADEON_IS_IGP) {
if (!rdev->me_fw || !rdev->pfp_fw || !rdev->ce_fw ||
!rdev->mec_fw || !rdev->sdma_fw || !rdev->rlc_fw) {
r = cik_init_microcode(rdev);
if (r) {
DRM_ERROR("Failed to load firmware!\n");
return r;
}
}
- } else {
if (!rdev->me_fw || !rdev->pfp_fw || !rdev->ce_fw ||
!rdev->mec_fw || !rdev->sdma_fw || !rdev->rlc_fw ||
!rdev->mc_fw) {
r = cik_init_microcode(rdev);
if (r) {
DRM_ERROR("Failed to load firmware!\n");
return r;
}
}
r = ci_mc_load_microcode(rdev);
if (r) {
DRM_ERROR("Failed to load MC firmware!\n");
return r;
}
- }
- r = r600_vram_scratch_init(rdev);
- if (r)
return r;
- cik_mc_program(rdev);
- r = cik_pcie_gart_enable(rdev);
- if (r)
return r;
- cik_gpu_init(rdev);
- /* allocate rlc buffers */
- r = si_rlc_init(rdev);
- if (r) {
DRM_ERROR("Failed to init rlc BOs!\n");
return r;
- }
- /* allocate wb buffer */
- r = radeon_wb_init(rdev);
- if (r)
return r;
- r = radeon_fence_driver_start_ring(rdev, RADEON_RING_TYPE_GFX_INDEX);
- if (r) {
dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r);
return r;
- }
- r = radeon_fence_driver_start_ring(rdev, R600_RING_TYPE_DMA_INDEX);
- if (r) {
dev_err(rdev->dev, "failed initializing DMA fences (%d).\n", r);
return r;
- }
- r = radeon_fence_driver_start_ring(rdev, CAYMAN_RING_TYPE_DMA1_INDEX);
- if (r) {
dev_err(rdev->dev, "failed initializing DMA fences (%d).\n", r);
return r;
- }
- /* Enable IRQ */
- if (!rdev->irq.installed) {
r = radeon_irq_kms_init(rdev);
if (r)
return r;
- }
- r = cik_irq_init(rdev);
- if (r) {
DRM_ERROR("radeon: IH init failed (%d).\n", r);
radeon_irq_kms_fini(rdev);
return r;
- }
- cik_irq_set(rdev);
- ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX];
- r = radeon_ring_init(rdev, ring, ring->ring_size, RADEON_WB_CP_RPTR_OFFSET,
CP_RB0_RPTR, CP_RB0_WPTR,
0, 0xfffff, RADEON_CP_PACKET2);
- if (r)
return r;
- ring = &rdev->ring[R600_RING_TYPE_DMA_INDEX];
- r = radeon_ring_init(rdev, ring, ring->ring_size, R600_WB_DMA_RPTR_OFFSET,
SDMA0_GFX_RB_RPTR + SDMA0_REGISTER_OFFSET,
SDMA0_GFX_RB_WPTR + SDMA0_REGISTER_OFFSET,
2, 0xfffffffc, SDMA_PACKET(SDMA_OPCODE_NOP, 0, 0));
- if (r)
return r;
- ring = &rdev->ring[CAYMAN_RING_TYPE_DMA1_INDEX];
- r = radeon_ring_init(rdev, ring, ring->ring_size, CAYMAN_WB_DMA1_RPTR_OFFSET,
SDMA0_GFX_RB_RPTR + SDMA1_REGISTER_OFFSET,
SDMA0_GFX_RB_WPTR + SDMA1_REGISTER_OFFSET,
2, 0xfffffffc, SDMA_PACKET(SDMA_OPCODE_NOP, 0, 0));
- if (r)
return r;
- r = cik_cp_resume(rdev);
- if (r)
return r;
- r = cik_sdma_resume(rdev);
- if (r)
return r;
- r = radeon_ib_pool_init(rdev);
- if (r) {
dev_err(rdev->dev, "IB initialization failed (%d).\n", r);
return r;
- }
- r = radeon_vm_manager_init(rdev);
- if (r) {
dev_err(rdev->dev, "vm manager initialization failed (%d).\n", r);
return r;
- }
- return 0;
+}
+/**
- cik_resume - resume the asic to a functional state
- @rdev: radeon_device pointer
- Programs the asic to a functional state (CIK).
- Called at resume.
- Returns 0 for success, error for failure.
- */
+int cik_resume(struct radeon_device *rdev) +{
- int r;
- /* Do not reset GPU before posting, on rv770 hw unlike on r500 hw,
* posting will perform necessary task to bring back GPU into good
* shape.
*/
- /* post card */
- atom_asic_init(rdev->mode_info.atom_context);
- rdev->accel_working = true;
- r = cik_startup(rdev);
- if (r) {
DRM_ERROR("cik startup failed on resume\n");
rdev->accel_working = false;
return r;
- }
- return r;
+}
+/**
- cik_suspend - suspend the asic
- @rdev: radeon_device pointer
- Bring the chip into a state suitable for suspend (CIK).
- Called at suspend.
- Returns 0 for success.
- */
+int cik_suspend(struct radeon_device *rdev) +{
- radeon_vm_manager_fini(rdev);
- cik_cp_enable(rdev, false);
- cik_sdma_enable(rdev, false);
- cik_irq_suspend(rdev);
- radeon_wb_disable(rdev);
- cik_pcie_gart_disable(rdev);
- return 0;
+}
+/* Plan is to move initialization in that function and use
- helper function so that radeon_device_init pretty much
- do nothing more than calling asic specific function. This
- should also allow to remove a bunch of callback function
- like vram_info.
- */
+/**
- cik_init - asic specific driver and hw init
- @rdev: radeon_device pointer
- Setup asic specific driver variables and program the hw
- to a functional state (CIK).
- Called at driver startup.
- Returns 0 for success, errors for failure.
- */
+int cik_init(struct radeon_device *rdev) +{
- struct radeon_ring *ring;
- int r;
- /* Read BIOS */
- if (!radeon_get_bios(rdev)) {
if (ASIC_IS_AVIVO(rdev))
return -EINVAL;
- }
- /* Must be an ATOMBIOS */
- if (!rdev->is_atom_bios) {
dev_err(rdev->dev, "Expecting atombios for cayman GPU\n");
return -EINVAL;
- }
- r = radeon_atombios_init(rdev);
- if (r)
return r;
- /* Post card if necessary */
- if (!radeon_card_posted(rdev)) {
if (!rdev->bios) {
dev_err(rdev->dev, "Card not posted and no BIOS - ignoring\n");
return -EINVAL;
}
DRM_INFO("GPU not posted. posting now...\n");
atom_asic_init(rdev->mode_info.atom_context);
- }
- /* Initialize scratch registers */
- cik_scratch_init(rdev);
- /* Initialize surface registers */
- radeon_surface_init(rdev);
- /* Initialize clocks */
- radeon_get_clock_info(rdev->ddev);
- /* Fence driver */
- r = radeon_fence_driver_init(rdev);
- if (r)
return r;
- /* initialize memory controller */
- r = cik_mc_init(rdev);
- if (r)
return r;
- /* Memory manager */
- r = radeon_bo_init(rdev);
- if (r)
return r;
- ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX];
- ring->ring_obj = NULL;
- r600_ring_init(rdev, ring, 1024 * 1024);
- ring = &rdev->ring[R600_RING_TYPE_DMA_INDEX];
- ring->ring_obj = NULL;
- r600_ring_init(rdev, ring, 256 * 1024);
- ring = &rdev->ring[CAYMAN_RING_TYPE_DMA1_INDEX];
- ring->ring_obj = NULL;
- r600_ring_init(rdev, ring, 256 * 1024);
- rdev->ih.ring_obj = NULL;
- r600_ih_ring_init(rdev, 64 * 1024);
- r = r600_pcie_gart_init(rdev);
- if (r)
return r;
- rdev->accel_working = true;
- r = cik_startup(rdev);
- if (r) {
dev_err(rdev->dev, "disabling GPU acceleration\n");
cik_cp_fini(rdev);
cik_sdma_fini(rdev);
cik_irq_fini(rdev);
si_rlc_fini(rdev);
radeon_wb_fini(rdev);
radeon_ib_pool_fini(rdev);
radeon_vm_manager_fini(rdev);
radeon_irq_kms_fini(rdev);
cik_pcie_gart_fini(rdev);
rdev->accel_working = false;
- }
- /* Don't start up if the MC ucode is missing.
* The default clocks and voltages before the MC ucode
* is loaded are not suffient for advanced operations.
*/
- if (!rdev->mc_fw && !(rdev->flags & RADEON_IS_IGP)) {
DRM_ERROR("radeon: MC ucode required for NI+.\n");
return -EINVAL;
- }
- return 0;
+}
+/**
- cik_fini - asic specific driver and hw fini
- @rdev: radeon_device pointer
- Tear down the asic specific driver variables and program the hw
- to an idle state (CIK).
- Called at driver unload.
- */
+void cik_fini(struct radeon_device *rdev) +{
- cik_cp_fini(rdev);
- cik_sdma_fini(rdev);
- cik_irq_fini(rdev);
- si_rlc_fini(rdev);
- radeon_wb_fini(rdev);
- radeon_vm_manager_fini(rdev);
- radeon_ib_pool_fini(rdev);
- radeon_irq_kms_fini(rdev);
- cik_pcie_gart_fini(rdev);
- r600_vram_scratch_fini(rdev);
- radeon_gem_fini(rdev);
- radeon_fence_driver_fini(rdev);
- radeon_bo_fini(rdev);
- radeon_atombios_fini(rdev);
- kfree(rdev->bios);
- rdev->bios = NULL;
+}
From: Alex Deucher alexander.deucher@amd.com
v2: further updates
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/ObjectID.h | 40 +++++++++++++++++++++++++++++++++++++ 1 files changed, 40 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/ObjectID.h b/drivers/gpu/drm/radeon/ObjectID.h index ca4b038..0619269 100644 --- a/drivers/gpu/drm/radeon/ObjectID.h +++ b/drivers/gpu/drm/radeon/ObjectID.h @@ -69,6 +69,8 @@ #define ENCODER_OBJECT_ID_ALMOND 0x22 #define ENCODER_OBJECT_ID_TRAVIS 0x23 #define ENCODER_OBJECT_ID_NUTMEG 0x22 +#define ENCODER_OBJECT_ID_HDMI_ANX9805 0x26 + /* Kaleidoscope (KLDSCP) Class Display Hardware (internal) */ #define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1 0x13 #define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1 0x14 @@ -86,6 +88,8 @@ #define ENCODER_OBJECT_ID_INTERNAL_UNIPHY1 0x20 #define ENCODER_OBJECT_ID_INTERNAL_UNIPHY2 0x21 #define ENCODER_OBJECT_ID_INTERNAL_VCE 0x24 +#define ENCODER_OBJECT_ID_INTERNAL_UNIPHY3 0x25 +#define ENCODER_OBJECT_ID_INTERNAL_AMCLK 0x27
#define ENCODER_OBJECT_ID_GENERAL_EXTERNAL_DVO 0xFF
@@ -364,6 +368,14 @@ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ ENCODER_OBJECT_ID_INTERNAL_UNIPHY2 << OBJECT_ID_SHIFT)
+#define ENCODER_INTERNAL_UNIPHY3_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_UNIPHY3 << OBJECT_ID_SHIFT) + +#define ENCODER_INTERNAL_UNIPHY3_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_INTERNAL_UNIPHY3 << OBJECT_ID_SHIFT) + #define ENCODER_GENERAL_EXTERNAL_DVO_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ ENCODER_OBJECT_ID_GENERAL_EXTERNAL_DVO << OBJECT_ID_SHIFT) @@ -392,6 +404,10 @@ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ ENCODER_OBJECT_ID_INTERNAL_VCE << OBJECT_ID_SHIFT)
+#define ENCODER_HDMI_ANX9805_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ + ENCODER_OBJECT_ID_HDMI_ANX9805 << OBJECT_ID_SHIFT) + /****************************************************/ /* Connector Object ID definition - Shared with BIOS */ /****************************************************/ @@ -461,6 +477,14 @@ GRAPH_OBJECT_ENUM_ID4 << ENUM_ID_SHIFT |\ CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D << OBJECT_ID_SHIFT)
+#define CONNECTOR_SINGLE_LINK_DVI_D_ENUM_ID5 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID5 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D << OBJECT_ID_SHIFT) + +#define CONNECTOR_SINGLE_LINK_DVI_D_ENUM_ID6 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID6 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D << OBJECT_ID_SHIFT) + #define CONNECTOR_DUAL_LINK_DVI_D_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D << OBJECT_ID_SHIFT) @@ -473,6 +497,10 @@ GRAPH_OBJECT_ENUM_ID3 << ENUM_ID_SHIFT |\ CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D << OBJECT_ID_SHIFT)
+#define CONNECTOR_DUAL_LINK_DVI_D_ENUM_ID4 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID4 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D << OBJECT_ID_SHIFT) + #define CONNECTOR_VGA_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ CONNECTOR_OBJECT_ID_VGA << OBJECT_ID_SHIFT) @@ -541,6 +569,18 @@ GRAPH_OBJECT_ENUM_ID3 << ENUM_ID_SHIFT |\ CONNECTOR_OBJECT_ID_HDMI_TYPE_A << OBJECT_ID_SHIFT)
+#define CONNECTOR_HDMI_TYPE_A_ENUM_ID4 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID4 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_HDMI_TYPE_A << OBJECT_ID_SHIFT) + +#define CONNECTOR_HDMI_TYPE_A_ENUM_ID5 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID5 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_HDMI_TYPE_A << OBJECT_ID_SHIFT) + +#define CONNECTOR_HDMI_TYPE_A_ENUM_ID6 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ + GRAPH_OBJECT_ENUM_ID6 << ENUM_ID_SHIFT |\ + CONNECTOR_OBJECT_ID_HDMI_TYPE_A << OBJECT_ID_SHIFT) + #define CONNECTOR_HDMI_TYPE_B_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\ CONNECTOR_OBJECT_ID_HDMI_TYPE_B << OBJECT_ID_SHIFT)
From: Alex Deucher alexander.deucher@amd.com
v2: further updates
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/atombios.h | 486 ++++++++++++++++++++++++++++++++++--- 1 files changed, 454 insertions(+), 32 deletions(-)
diff --git a/drivers/gpu/drm/radeon/atombios.h b/drivers/gpu/drm/radeon/atombios.h index 0ee5737..f19d9a6 100644 --- a/drivers/gpu/drm/radeon/atombios.h +++ b/drivers/gpu/drm/radeon/atombios.h @@ -74,6 +74,8 @@ #define ATOM_PPLL2 1 #define ATOM_DCPLL 2 #define ATOM_PPLL0 2 +#define ATOM_PPLL3 3 + #define ATOM_EXT_PLL1 8 #define ATOM_EXT_PLL2 9 #define ATOM_EXT_CLOCK 10 @@ -259,7 +261,7 @@ typedef struct _ATOM_MASTER_LIST_OF_COMMAND_TABLES{ USHORT AdjustDisplayPll; //Atomic Table, used by various SW componentes. USHORT AdjustMemoryController; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock USHORT EnableASIC_StaticPwrMgt; //Atomic Table, only used by Bios - USHORT ASIC_StaticPwrMgtStatusChange; //Obsolete , only used by Bios + USHORT SetUniphyInstance; //Atomic Table, only used by Bios USHORT DAC_LoadDetection; //Atomic Table, directly used by various SW components,latest version 1.2 USHORT LVTMAEncoderControl; //Atomic Table,directly used by various SW components,latest version 1.3 USHORT HW_Misc_Operation; //Atomic Table, directly used by various SW components,latest version 1.1 @@ -271,7 +273,7 @@ typedef struct _ATOM_MASTER_LIST_OF_COMMAND_TABLES{ USHORT TVEncoderControl; //Function Table,directly used by various SW components,latest version 1.1 USHORT PatchMCSetting; //only used by BIOS USHORT MC_SEQ_Control; //only used by BIOS - USHORT TV1OutputControl; //Atomic Table, Obsolete from Ry6xx, use DAC2 Output instead + USHORT Gfx_Harvesting; //Atomic Table, Obsolete from Ry6xx, Now only used by BIOS for GFX harvesting USHORT EnableScaler; //Atomic Table, used only by Bios USHORT BlankCRTC; //Atomic Table, directly used by various SW components,latest version 1.1 USHORT EnableCRTC; //Atomic Table, directly used by various SW components,latest version 1.1 @@ -328,7 +330,7 @@ typedef struct _ATOM_MASTER_LIST_OF_COMMAND_TABLES{ #define UNIPHYTransmitterControl DIG1TransmitterControl #define LVTMATransmitterControl DIG2TransmitterControl #define SetCRTC_DPM_State GetConditionalGoldenSetting -#define SetUniphyInstance ASIC_StaticPwrMgtStatusChange +#define ASIC_StaticPwrMgtStatusChange SetUniphyInstance #define HPDInterruptService ReadHWAssistedI2CStatus #define EnableVGA_Access GetSCLKOverMCLKRatio #define EnableYUV GetDispObjectInfo @@ -338,7 +340,7 @@ typedef struct _ATOM_MASTER_LIST_OF_COMMAND_TABLES{ #define TMDSAEncoderControl PatchMCSetting #define LVDSEncoderControl MC_SEQ_Control #define LCD1OutputControl HW_Misc_Operation - +#define TV1OutputControl Gfx_Harvesting
typedef struct _ATOM_MASTER_COMMAND_TABLE { @@ -478,11 +480,11 @@ typedef struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V3 typedef struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V4 { #if ATOM_BIG_ENDIAN - ULONG ucPostDiv; //return parameter: post divider which is used to program to register directly + ULONG ucPostDiv:8; //return parameter: post divider which is used to program to register directly ULONG ulClock:24; //Input= target clock, output = actual clock #else ULONG ulClock:24; //Input= target clock, output = actual clock - ULONG ucPostDiv; //return parameter: post divider which is used to program to register directly + ULONG ucPostDiv:8; //return parameter: post divider which is used to program to register directly #endif }COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V4;
@@ -504,6 +506,32 @@ typedef struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V5 UCHAR ucReserved; }COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V5;
+ +typedef struct _COMPUTE_GPU_CLOCK_INPUT_PARAMETERS_V1_6 +{ + ATOM_COMPUTE_CLOCK_FREQ ulClock; //Input Parameter + ULONG ulReserved[2]; +}COMPUTE_GPU_CLOCK_INPUT_PARAMETERS_V1_6; + +//ATOM_COMPUTE_CLOCK_FREQ.ulComputeClockFlag +#define COMPUTE_GPUCLK_INPUT_FLAG_CLK_TYPE_MASK 0x0f +#define COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK 0x00 +#define COMPUTE_GPUCLK_INPUT_FLAG_SCLK 0x01 + +typedef struct _COMPUTE_GPU_CLOCK_OUTPUT_PARAMETERS_V1_6 +{ + COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V4 ulClock; //Output Parameter: ucPostDiv=DFS divider + ATOM_S_MPLL_FB_DIVIDER ulFbDiv; //Output Parameter: PLL FB divider + UCHAR ucPllRefDiv; //Output Parameter: PLL ref divider + UCHAR ucPllPostDiv; //Output Parameter: PLL post divider + UCHAR ucPllCntlFlag; //Output Flags: control flag + UCHAR ucReserved; +}COMPUTE_GPU_CLOCK_OUTPUT_PARAMETERS_V1_6; + +//ucPllCntlFlag +#define SPLL_CNTL_FLAG_VCO_MODE_MASK 0x03 + + // ucInputFlag #define ATOM_PLL_INPUT_FLAG_PLL_STROBE_MODE_EN 1 // 1-StrobeMode, 0-PerformanceMode
@@ -1686,6 +1714,7 @@ typedef struct _PIXEL_CLOCK_PARAMETERS_V6 #define PIXEL_CLOCK_V6_MISC_HDMI_30BPP 0x08 #define PIXEL_CLOCK_V6_MISC_HDMI_48BPP 0x0c #define PIXEL_CLOCK_V6_MISC_REF_DIV_SRC 0x10 +#define PIXEL_CLOCK_V6_MISC_GEN_DPREFCLK 0x40
typedef struct _GET_DISP_PLL_STATUS_INPUT_PARAMETERS_V2 { @@ -2102,6 +2131,17 @@ typedef struct _DVO_ENCODER_CONTROL_PARAMETERS_V3 }DVO_ENCODER_CONTROL_PARAMETERS_V3; #define DVO_ENCODER_CONTROL_PS_ALLOCATION_V3 DVO_ENCODER_CONTROL_PARAMETERS_V3
+typedef struct _DVO_ENCODER_CONTROL_PARAMETERS_V1_4 +{ + USHORT usPixelClock; + UCHAR ucDVOConfig; + UCHAR ucAction; //ATOM_ENABLE/ATOM_DISABLE/ATOM_HPD_INIT + UCHAR ucBitPerColor; //please refer to definition of PANEL_xBIT_PER_COLOR + UCHAR ucReseved[3]; +}DVO_ENCODER_CONTROL_PARAMETERS_V1_4; +#define DVO_ENCODER_CONTROL_PS_ALLOCATION_V1_4 DVO_ENCODER_CONTROL_PARAMETERS_V1_4 + + //ucTableFormatRevision=1 //ucTableContentRevision=3 structure is not changed but usMisc add bit 1 as another input for // bit1=0: non-coherent mode @@ -2165,7 +2205,7 @@ typedef struct _DVO_ENCODER_CONTROL_PARAMETERS_V3 #define SET_ASIC_VOLTAGE_MODE_SOURCE_B 0x4
#define SET_ASIC_VOLTAGE_MODE_SET_VOLTAGE 0x0 -#define SET_ASIC_VOLTAGE_MODE_GET_GPIOVAL 0x1 +#define SET_ASIC_VOLTAGE_MODE_GET_GPIOVAL 0x1 #define SET_ASIC_VOLTAGE_MODE_GET_GPIOMASK 0x2
typedef struct _SET_VOLTAGE_PARAMETERS @@ -2200,15 +2240,20 @@ typedef struct _SET_VOLTAGE_PARAMETERS_V1_3 //SET_VOLTAGE_PARAMETERS_V3.ucVoltageMode #define ATOM_SET_VOLTAGE 0 //Set voltage Level #define ATOM_INIT_VOLTAGE_REGULATOR 3 //Init Regulator -#define ATOM_SET_VOLTAGE_PHASE 4 //Set Vregulator Phase -#define ATOM_GET_MAX_VOLTAGE 6 //Get Max Voltage, not used in SetVoltageTable v1.3 -#define ATOM_GET_VOLTAGE_LEVEL 6 //Get Voltage level from vitual voltage ID +#define ATOM_SET_VOLTAGE_PHASE 4 //Set Vregulator Phase, only for SVID/PVID regulator +#define ATOM_GET_MAX_VOLTAGE 6 //Get Max Voltage, not used from SetVoltageTable v1.3 +#define ATOM_GET_VOLTAGE_LEVEL 6 //Get Voltage level from vitual voltage ID, not used for SetVoltage v1.4 +#define ATOM_GET_LEAKAGE_ID 8 //Get Leakage Voltage Id ( starting from SMU7x IP ), SetVoltage v1.4
// define vitual voltage id in usVoltageLevel #define ATOM_VIRTUAL_VOLTAGE_ID0 0xff01 #define ATOM_VIRTUAL_VOLTAGE_ID1 0xff02 #define ATOM_VIRTUAL_VOLTAGE_ID2 0xff03 #define ATOM_VIRTUAL_VOLTAGE_ID3 0xff04 +#define ATOM_VIRTUAL_VOLTAGE_ID4 0xff05 +#define ATOM_VIRTUAL_VOLTAGE_ID5 0xff06 +#define ATOM_VIRTUAL_VOLTAGE_ID6 0xff07 +#define ATOM_VIRTUAL_VOLTAGE_ID7 0xff08
typedef struct _SET_VOLTAGE_PS_ALLOCATION { @@ -2628,7 +2673,8 @@ typedef struct _ATOM_FIRMWARE_INFO_V2_2 ULONG ulFirmwareRevision; ULONG ulDefaultEngineClock; //In 10Khz unit ULONG ulDefaultMemoryClock; //In 10Khz unit - ULONG ulReserved[2]; + ULONG ulSPLL_OutputFreq; //In 10Khz unit + ULONG ulGPUPLL_OutputFreq; //In 10Khz unit ULONG ulReserved1; //Was ulMaxEngineClockPLL_Output; //In 10Khz unit* ULONG ulReserved2; //Was ulMaxMemoryClockPLL_Output; //In 10Khz unit* ULONG ulMaxPixelClockPLL_Output; //In 10Khz unit @@ -3813,6 +3859,12 @@ typedef struct _ATOM_GPIO_PIN_ASSIGNMENT UCHAR ucGPIO_ID; }ATOM_GPIO_PIN_ASSIGNMENT;
+//ucGPIO_ID pre-define id for multiple usage +//from SMU7.x, if ucGPIO_ID=PP_AC_DC_SWITCH_GPIO_PINID in GPIO_LUTTable, AC/DC swithing feature is enable +#define PP_AC_DC_SWITCH_GPIO_PINID 60 +//from SMU7.x, if ucGPIO_ID=VDDC_REGULATOR_VRHOT_GPIO_PINID in GPIO_LUTable, VRHot feature is enable +#define VDDC_VRHOT_GPIO_PINID 61 + typedef struct _ATOM_GPIO_PIN_LUT { ATOM_COMMON_TABLE_HEADER sHeader; @@ -4074,17 +4126,19 @@ typedef struct _EXT_DISPLAY_PATH
//usCaps #define EXT_DISPLAY_PATH_CAPS__HBR2_DISABLE 0x01 +#define EXT_DISPLAY_PATH_CAPS__DP_FIXED_VS_EN 0x02
typedef struct _ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO { ATOM_COMMON_TABLE_HEADER sHeader; UCHAR ucGuid [NUMBER_OF_UCHAR_FOR_GUID]; // a GUID is a 16 byte long string EXT_DISPLAY_PATH sPath[MAX_NUMBER_OF_EXT_DISPLAY_PATH]; // total of fixed 7 entries. - UCHAR ucChecksum; // a simple Checksum of the sum of whole structure equal to 0x0. + UCHAR ucChecksum; // a simple Checksum of the sum of whole structure equal to 0x0. UCHAR uc3DStereoPinId; // use for eDP panel UCHAR ucRemoteDisplayConfig; UCHAR uceDPToLVDSRxId; - UCHAR Reserved[4]; // for potential expansion + UCHAR ucFixDPVoltageSwing; // usCaps[1]=1, this indicate DP_LANE_SET value + UCHAR Reserved[3]; // for potential expansion }ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO;
//Related definitions, all records are different but they have a commond header @@ -4416,6 +4470,13 @@ typedef struct _ATOM_VOLTAGE_CONTROL #define VOLTAGE_CONTROL_ID_CHL822x 0x08 #define VOLTAGE_CONTROL_ID_VT1586M 0x09 #define VOLTAGE_CONTROL_ID_UP1637 0x0A +#define VOLTAGE_CONTROL_ID_CHL8214 0x0B +#define VOLTAGE_CONTROL_ID_UP1801 0x0C +#define VOLTAGE_CONTROL_ID_ST6788A 0x0D +#define VOLTAGE_CONTROL_ID_CHLIR3564SVI2 0x0E +#define VOLTAGE_CONTROL_ID_AD527x 0x0F +#define VOLTAGE_CONTROL_ID_NCP81022 0x10 +#define VOLTAGE_CONTROL_ID_LTC2635 0x11
typedef struct _ATOM_VOLTAGE_OBJECT { @@ -4458,6 +4519,15 @@ typedef struct _ATOM_VOLTAGE_OBJECT_HEADER_V3{ USHORT usSize; //Size of Object }ATOM_VOLTAGE_OBJECT_HEADER_V3;
+// ATOM_VOLTAGE_OBJECT_HEADER_V3.ucVoltageMode +#define VOLTAGE_OBJ_GPIO_LUT 0 //VOLTAGE and GPIO Lookup table ->ATOM_GPIO_VOLTAGE_OBJECT_V3 +#define VOLTAGE_OBJ_VR_I2C_INIT_SEQ 3 //VOLTAGE REGULATOR INIT sequece through I2C -> ATOM_I2C_VOLTAGE_OBJECT_V3 +#define VOLTAGE_OBJ_PHASE_LUT 4 //Set Vregulator Phase lookup table ->ATOM_GPIO_VOLTAGE_OBJECT_V3 +#define VOLTAGE_OBJ_SVID2 7 //Indicate voltage control by SVID2 ->ATOM_SVID2_VOLTAGE_OBJECT_V3 +#define VOLTAGE_OBJ_PWRBOOST_LEAKAGE_LUT 0x10 //Powerboost Voltage and LeakageId lookup table->ATOM_LEAKAGE_VOLTAGE_OBJECT_V3 +#define VOLTAGE_OBJ_HIGH_STATE_LEAKAGE_LUT 0x11 //High voltage state Voltage and LeakageId lookup table->ATOM_LEAKAGE_VOLTAGE_OBJECT_V3 +#define VOLTAGE_OBJ_HIGH1_STATE_LEAKAGE_LUT 0x12 //High1 voltage state Voltage and LeakageId lookup table->ATOM_LEAKAGE_VOLTAGE_OBJECT_V3 + typedef struct _VOLTAGE_LUT_ENTRY_V2 { ULONG ulVoltageId; // The Voltage ID which is used to program GPIO register @@ -4473,7 +4543,7 @@ typedef struct _LEAKAGE_VOLTAGE_LUT_ENTRY_V2
typedef struct _ATOM_I2C_VOLTAGE_OBJECT_V3 { - ATOM_VOLTAGE_OBJECT_HEADER_V3 sHeader; + ATOM_VOLTAGE_OBJECT_HEADER_V3 sHeader; // voltage mode = VOLTAGE_OBJ_VR_I2C_INIT_SEQ UCHAR ucVoltageRegulatorId; //Indicate Voltage Regulator Id UCHAR ucVoltageControlI2cLine; UCHAR ucVoltageControlAddress; @@ -4484,7 +4554,7 @@ typedef struct _ATOM_I2C_VOLTAGE_OBJECT_V3
typedef struct _ATOM_GPIO_VOLTAGE_OBJECT_V3 { - ATOM_VOLTAGE_OBJECT_HEADER_V3 sHeader; + ATOM_VOLTAGE_OBJECT_HEADER_V3 sHeader; // voltage mode = VOLTAGE_OBJ_GPIO_LUT or VOLTAGE_OBJ_PHASE_LUT UCHAR ucVoltageGpioCntlId; // default is 0 which indicate control through CG VID mode UCHAR ucGpioEntryNum; // indiate the entry numbers of Votlage/Gpio value Look up table UCHAR ucPhaseDelay; // phase delay in unit of micro second @@ -4495,7 +4565,7 @@ typedef struct _ATOM_GPIO_VOLTAGE_OBJECT_V3
typedef struct _ATOM_LEAKAGE_VOLTAGE_OBJECT_V3 { - ATOM_VOLTAGE_OBJECT_HEADER_V3 sHeader; + ATOM_VOLTAGE_OBJECT_HEADER_V3 sHeader; // voltage mode = 0x10/0x11/0x12 UCHAR ucLeakageCntlId; // default is 0 UCHAR ucLeakageEntryNum; // indicate the entry number of LeakageId/Voltage Lut table UCHAR ucReserved[2]; @@ -4503,10 +4573,26 @@ typedef struct _ATOM_LEAKAGE_VOLTAGE_OBJECT_V3 LEAKAGE_VOLTAGE_LUT_ENTRY_V2 asLeakageIdLut[1]; }ATOM_LEAKAGE_VOLTAGE_OBJECT_V3;
+ +typedef struct _ATOM_SVID2_VOLTAGE_OBJECT_V3 +{ + ATOM_VOLTAGE_OBJECT_HEADER_V3 sHeader; // voltage mode = VOLTAGE_OBJ_SVID2 +// 14:7 PSI0_VID +// 6 PSI0_EN +// 5 PSI1 +// 4:2 load line slope trim. +// 1:0 offset trim, + USHORT usLoadLine_PSI; +// GPU GPIO pin Id to SVID2 regulator VRHot pin. possible value 0~31. 0 means GPIO0, 31 means GPIO31 + UCHAR ucReserved[2]; + ULONG ulReserved; +}ATOM_SVID2_VOLTAGE_OBJECT_V3; + typedef union _ATOM_VOLTAGE_OBJECT_V3{ ATOM_GPIO_VOLTAGE_OBJECT_V3 asGpioVoltageObj; ATOM_I2C_VOLTAGE_OBJECT_V3 asI2cVoltageObj; ATOM_LEAKAGE_VOLTAGE_OBJECT_V3 asLeakageObj; + ATOM_SVID2_VOLTAGE_OBJECT_V3 asSVID2Obj; }ATOM_VOLTAGE_OBJECT_V3;
typedef struct _ATOM_VOLTAGE_OBJECT_INFO_V3_1 @@ -4536,6 +4622,21 @@ typedef struct _ATOM_ASIC_PROFILING_INFO ATOM_ASIC_PROFILE_VOLTAGE asVoltage; }ATOM_ASIC_PROFILING_INFO;
+typedef struct _ATOM_ASIC_PROFILING_INFO_V2_1 +{ + ATOM_COMMON_TABLE_HEADER asHeader; + UCHAR ucLeakageBinNum; // indicate the entry number of LeakageId/Voltage Lut table + USHORT usLeakageBinArrayOffset; // offset of USHORT Leakage Bin list array ( from lower LeakageId to higher) + + UCHAR ucElbVDDC_Num; + USHORT usElbVDDC_IdArrayOffset; // offset of USHORT virtual VDDC voltage id ( 0xff01~0xff08 ) + USHORT usElbVDDC_LevelArrayOffset; // offset of 2 dimension voltage level USHORT array + + UCHAR ucElbVDDCI_Num; + USHORT usElbVDDCI_IdArrayOffset; // offset of USHORT virtual VDDCI voltage id ( 0xff01~0xff08 ) + USHORT usElbVDDCI_LevelArrayOffset; // offset of 2 dimension voltage level USHORT array +}ATOM_ASIC_PROFILING_INFO_V2_1; + typedef struct _ATOM_POWER_SOURCE_OBJECT { UCHAR ucPwrSrcId; // Power source @@ -4652,6 +4753,8 @@ typedef struct _ATOM_INTEGRATED_SYSTEM_INFO_V6 #define SYS_INFO_LVDSMISC__888_BPC 0x04 #define SYS_INFO_LVDSMISC__OVERRIDE_EN 0x08 #define SYS_INFO_LVDSMISC__BLON_ACTIVE_LOW 0x10 +// new since Trinity +#define SYS_INFO_LVDSMISC__TRAVIS_LVDS_VOL_OVERRIDE_EN 0x20
// not used any more #define SYS_INFO_LVDSMISC__VSYNC_ACTIVE_LOW 0x04 @@ -4752,6 +4855,29 @@ typedef struct _ATOM_FUSION_SYSTEM_INFO_V1 ATOM_INTEGRATED_SYSTEM_INFO_V6 sIntegratedSysInfo; ULONG ulPowerplayTable[128]; }ATOM_FUSION_SYSTEM_INFO_V1; + + +typedef struct _ATOM_TDP_CONFIG_BITS +{ +#if ATOM_BIG_ENDIAN + ULONG uReserved:2; + ULONG uTDP_Value:14; // Original TDP value in tens of milli watts + ULONG uCTDP_Value:14; // Override value in tens of milli watts + ULONG uCTDP_Enable:2; // = (uCTDP_Value > uTDP_Value? 2: (uCTDP_Value < uTDP_Value)) +#else + ULONG uCTDP_Enable:2; // = (uCTDP_Value > uTDP_Value? 2: (uCTDP_Value < uTDP_Value)) + ULONG uCTDP_Value:14; // Override value in tens of milli watts + ULONG uTDP_Value:14; // Original TDP value in tens of milli watts + ULONG uReserved:2; +#endif +}ATOM_TDP_CONFIG_BITS; + +typedef union _ATOM_TDP_CONFIG +{ + ATOM_TDP_CONFIG_BITS TDP_config; + ULONG TDP_config_all; +}ATOM_TDP_CONFIG; + /********************************************************************************************************************** ATOM_FUSION_SYSTEM_INFO_V1 Description sIntegratedSysInfo: refer to ATOM_INTEGRATED_SYSTEM_INFO_V6 definition. @@ -4784,7 +4910,8 @@ typedef struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_7 UCHAR ucMemoryType; UCHAR ucUMAChannelNumber; UCHAR strVBIOSMsg[40]; - ULONG ulReserved[20]; + ATOM_TDP_CONFIG asTdpConfig; + ULONG ulReserved[19]; ATOM_AVAILABLE_SCLK_LIST sAvail_SCLK[5]; ULONG ulGMCRestoreResetTime; ULONG ulMinimumNClk; @@ -4809,7 +4936,7 @@ typedef struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_7 USHORT GnbTdpLimit; USHORT usMaxLVDSPclkFreqInSingleLink; UCHAR ucLvdsMisc; - UCHAR ucLVDSReserved; + UCHAR ucTravisLVDSVolAdjust; UCHAR ucLVDSPwrOnSeqDIGONtoDE_in4Ms; UCHAR ucLVDSPwrOnSeqDEtoVARY_BL_in4Ms; UCHAR ucLVDSPwrOffSeqVARY_BLtoDE_in4Ms; @@ -4817,7 +4944,7 @@ typedef struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_7 UCHAR ucLVDSOffToOnDelay_in4Ms; UCHAR ucLVDSPwrOnSeqVARY_BLtoBLON_in4Ms; UCHAR ucLVDSPwrOffSeqBLONtoVARY_BL_in4Ms; - UCHAR ucLVDSReserved1; + UCHAR ucMinAllowedBL_Level; ULONG ulLCDBitDepthControlVal; ULONG ulNbpStateMemclkFreq[4]; USHORT usNBP2Voltage; @@ -4846,6 +4973,7 @@ typedef struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_7 #define SYS_INFO_GPUCAPS__TMDSHDMI_COHERENT_SINGLEPLL_MODE 0x01 #define SYS_INFO_GPUCAPS__DP_SINGLEPLL_MODE 0x02 #define SYS_INFO_GPUCAPS__DISABLE_AUX_MODE_DETECT 0x08 +#define SYS_INFO_GPUCAPS__ENABEL_DFS_BYPASS 0x10
/********************************************************************************************************************** ATOM_INTEGRATED_SYSTEM_INFO_V1_7 Description @@ -4945,6 +5073,9 @@ ucLVDSMisc: [bit0] LVDS 888bit panel mode =0: LVDS 888 pan [bit2] LVDS 888bit per color mode =0: 666 bit per color =1:888 bit per color [bit3] LVDS parameter override enable =0: ucLvdsMisc parameter are not used =1: ucLvdsMisc parameter should be used [bit4] Polarity of signal sent to digital BLON output pin. =0: not inverted(active high) =1: inverted ( active low ) + [bit5] Travid LVDS output voltage override enable, when =1, use ucTravisLVDSVolAdjust value to overwrite Traivs register LVDS_CTRL_4 +ucTravisLVDSVolAdjust When ucLVDSMisc[5]=1,it means platform SBIOS want to overwrite TravisLVDSVoltage. Then VBIOS will use ucTravisLVDSVolAdjust + value to program Travis register LVDS_CTRL_4 ucLVDSPwrOnSeqDIGONtoDE_in4Ms: LVDS power up sequence time in unit of 4ms, time delay from DIGON signal active to data enable signal active( DE ). =0 mean use VBIOS default which is 8 ( 32ms ). The LVDS power up sequence is as following: DIGON->DE->VARY_BL->BLON. This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable. @@ -4964,18 +5095,241 @@ ucLVDSOffToOnDelay_in4Ms: LVDS power down sequence time in unit of 4ms. =0 means to use VBIOS default delay which is 125 ( 500ms ). This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable.
-ucLVDSPwrOnVARY_BLtoBLON_in4Ms: LVDS power up sequence time in unit of 4ms. Time delay from VARY_BL signal on to DLON signal active. +ucLVDSPwrOnSeqVARY_BLtoBLON_in4Ms: + LVDS power up sequence time in unit of 4ms. Time delay from VARY_BL signal on to DLON signal active. =0 means to use VBIOS default delay which is 0 ( 0ms ). This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable.
-ucLVDSPwrOffBLONtoVARY_BL_in4Ms: LVDS power down sequence time in unit of 4ms. Time delay from BLON signal off to VARY_BL signal off. +ucLVDSPwrOffSeqBLONtoVARY_BL_in4Ms: + LVDS power down sequence time in unit of 4ms. Time delay from BLON signal off to VARY_BL signal off. =0 means to use VBIOS default delay which is 0 ( 0ms ). This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable.
+ucMinAllowedBL_Level: Lowest LCD backlight PWM level. This is customer platform specific parameters. By default it is 0. + ulNbpStateMemclkFreq[4]: system memory clock frequncey in unit of 10Khz in different NB pstate.
**********************************************************************************************************************/
+// this IntegrateSystemInfoTable is used for Kaveri & Kabini APU +typedef struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_8 +{ + ATOM_COMMON_TABLE_HEADER sHeader; + ULONG ulBootUpEngineClock; + ULONG ulDentistVCOFreq; + ULONG ulBootUpUMAClock; + ATOM_CLK_VOLT_CAPABILITY sDISPCLK_Voltage[4]; + ULONG ulBootUpReqDisplayVector; + ULONG ulVBIOSMisc; + ULONG ulGPUCapInfo; + ULONG ulDISP_CLK2Freq; + USHORT usRequestedPWMFreqInHz; + UCHAR ucHtcTmpLmt; + UCHAR ucHtcHystLmt; + ULONG ulReserved2; + ULONG ulSystemConfig; + ULONG ulCPUCapInfo; + ULONG ulReserved3; + USHORT usGPUReservedSysMemSize; + USHORT usExtDispConnInfoOffset; + USHORT usPanelRefreshRateRange; + UCHAR ucMemoryType; + UCHAR ucUMAChannelNumber; + UCHAR strVBIOSMsg[40]; + ATOM_TDP_CONFIG asTdpConfig; + ULONG ulReserved[19]; + ATOM_AVAILABLE_SCLK_LIST sAvail_SCLK[5]; + ULONG ulGMCRestoreResetTime; + ULONG ulReserved4; + ULONG ulIdleNClk; + ULONG ulDDR_DLL_PowerUpTime; + ULONG ulDDR_PLL_PowerUpTime; + USHORT usPCIEClkSSPercentage; + USHORT usPCIEClkSSType; + USHORT usLvdsSSPercentage; + USHORT usLvdsSSpreadRateIn10Hz; + USHORT usHDMISSPercentage; + USHORT usHDMISSpreadRateIn10Hz; + USHORT usDVISSPercentage; + USHORT usDVISSpreadRateIn10Hz; + ULONG ulGPUReservedSysMemBaseAddrLo; + ULONG ulGPUReservedSysMemBaseAddrHi; + ULONG ulReserved5[3]; + USHORT usMaxLVDSPclkFreqInSingleLink; + UCHAR ucLvdsMisc; + UCHAR ucTravisLVDSVolAdjust; + UCHAR ucLVDSPwrOnSeqDIGONtoDE_in4Ms; + UCHAR ucLVDSPwrOnSeqDEtoVARY_BL_in4Ms; + UCHAR ucLVDSPwrOffSeqVARY_BLtoDE_in4Ms; + UCHAR ucLVDSPwrOffSeqDEtoDIGON_in4Ms; + UCHAR ucLVDSOffToOnDelay_in4Ms; + UCHAR ucLVDSPwrOnSeqVARY_BLtoBLON_in4Ms; + UCHAR ucLVDSPwrOffSeqBLONtoVARY_BL_in4Ms; + UCHAR ucMinAllowedBL_Level; + ULONG ulLCDBitDepthControlVal; + ULONG ulNbpStateMemclkFreq[4]; + ULONG ulReserved6; + ULONG ulNbpStateNClkFreq[4]; + USHORT usNBPStateVoltage[4]; + USHORT usBootUpNBVoltage; + USHORT usReserved2; + ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO sExtDispConnInfo; +}ATOM_INTEGRATED_SYSTEM_INFO_V1_8; + +/********************************************************************************************************************** + ATOM_INTEGRATED_SYSTEM_INFO_V1_8 Description +ulBootUpEngineClock: VBIOS bootup Engine clock frequency, in 10kHz unit. if it is equal 0, then VBIOS use pre-defined bootup engine clock +ulDentistVCOFreq: Dentist VCO clock in 10kHz unit. +ulBootUpUMAClock: System memory boot up clock frequency in 10Khz unit. +sDISPCLK_Voltage: Report Display clock frequency requirement on GNB voltage(up to 4 voltage levels). + +ulBootUpReqDisplayVector: VBIOS boot up display IDs, following are supported devices in Trinity projects: + ATOM_DEVICE_CRT1_SUPPORT 0x0001 + ATOM_DEVICE_DFP1_SUPPORT 0x0008 + ATOM_DEVICE_DFP6_SUPPORT 0x0040 + ATOM_DEVICE_DFP2_SUPPORT 0x0080 + ATOM_DEVICE_DFP3_SUPPORT 0x0200 + ATOM_DEVICE_DFP4_SUPPORT 0x0400 + ATOM_DEVICE_DFP5_SUPPORT 0x0800 + ATOM_DEVICE_LCD1_SUPPORT 0x0002 + +ulVBIOSMisc: Miscellenous flags for VBIOS requirement and interface + bit[0]=0: INT15 callback function Get LCD EDID ( ax=4e08, bl=1b ) is not supported by SBIOS. + =1: INT15 callback function Get LCD EDID ( ax=4e08, bl=1b ) is supported by SBIOS. + bit[1]=0: INT15 callback function Get boot display( ax=4e08, bl=01h) is not supported by SBIOS + =1: INT15 callback function Get boot display( ax=4e08, bl=01h) is supported by SBIOS + bit[2]=0: INT15 callback function Get panel Expansion ( ax=4e08, bl=02h) is not supported by SBIOS + =1: INT15 callback function Get panel Expansion ( ax=4e08, bl=02h) is supported by SBIOS + bit[3]=0: VBIOS fast boot is disable + =1: VBIOS fast boot is enable. ( VBIOS skip display device detection in every set mode if LCD panel is connect and LID is open) + +ulGPUCapInfo: bit[0~2]= Reserved + bit[3]=0: Enable AUX HW mode detection logic + =1: Disable AUX HW mode detection logic + bit[4]=0: Disable DFS bypass feature + =1: Enable DFS bypass feature + +usRequestedPWMFreqInHz: When it's set to 0x0 by SBIOS: the LCD BackLight is not controlled by GPU(SW). + Any attempt to change BL using VBIOS function or enable VariBri from PP table is not effective since ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU==0; + + When it's set to a non-zero frequency, the BackLight is controlled by GPU (SW) in one of two ways below: + 1. SW uses the GPU BL PWM output to control the BL, in chis case, this non-zero frequency determines what freq GPU should use; + VBIOS will set up proper PWM frequency and ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU==1,as the result, + Changing BL using VBIOS function is functional in both driver and non-driver present environment; + and enabling VariBri under the driver environment from PP table is optional. + + 2. SW uses other means to control BL (like DPCD),this non-zero frequency serves as a flag only indicating + that BL control from GPU is expected. + VBIOS will NOT set up PWM frequency but make ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU==1 + Changing BL using VBIOS function could be functional in both driver and non-driver present environment,but + it's per platform + and enabling VariBri under the driver environment from PP table is optional. + +ucHtcTmpLmt: Refer to D18F3x64 bit[22:16], HtcTmpLmt. Threshold on value to enter HTC_active state. +ucHtcHystLmt: Refer to D18F3x64 bit[27:24], HtcHystLmt. + To calculate threshold off value to exit HTC_active state, which is Threshold on vlaue minus ucHtcHystLmt. + +ulSystemConfig: Bit[0]=0: PCIE Power Gating Disabled + =1: PCIE Power Gating Enabled + Bit[1]=0: DDR-DLL shut-down feature disabled. + 1: DDR-DLL shut-down feature enabled. + Bit[2]=0: DDR-PLL Power down feature disabled. + 1: DDR-PLL Power down feature enabled. + Bit[3]=0: GNB DPM is disabled + =1: GNB DPM is enabled +ulCPUCapInfo: TBD + +usExtDispConnInfoOffset: Offset to sExtDispConnInfo inside the structure +usPanelRefreshRateRange: Bit vector for LCD supported refresh rate range. If DRR is requestd by the platform, at least two bits need to be set + to indicate a range. + SUPPORTED_LCD_REFRESHRATE_30Hz 0x0004 + SUPPORTED_LCD_REFRESHRATE_40Hz 0x0008 + SUPPORTED_LCD_REFRESHRATE_50Hz 0x0010 + SUPPORTED_LCD_REFRESHRATE_60Hz 0x0020 + +ucMemoryType: [3:0]=1:DDR1;=2:DDR2;=3:DDR3;=5:GDDR5; [7:4] is reserved. +ucUMAChannelNumber: System memory channel numbers. + +strVBIOSMsg[40]: VBIOS boot up customized message string + +sAvail_SCLK[5]: Arrays to provide availabe list of SLCK and corresponding voltage, order from low to high + +ulGMCRestoreResetTime: GMC power restore and GMC reset time to calculate data reconnection latency. Unit in ns. +ulIdleNClk: NCLK speed while memory runs in self-refresh state, used to calculate self-refresh latency. Unit in 10kHz. +ulDDR_DLL_PowerUpTime: DDR PHY DLL power up time. Unit in ns. +ulDDR_PLL_PowerUpTime: DDR PHY PLL power up time. Unit in ns. + +usPCIEClkSSPercentage: PCIE Clock Spread Spectrum Percentage in unit 0.01%; 100 mean 1%. +usPCIEClkSSType: PCIE Clock Spread Spectrum Type. 0 for Down spread(default); 1 for Center spread. +usLvdsSSPercentage: LVDS panel ( not include eDP ) Spread Spectrum Percentage in unit of 0.01%, =0, use VBIOS default setting. +usLvdsSSpreadRateIn10Hz: LVDS panel ( not include eDP ) Spread Spectrum frequency in unit of 10Hz, =0, use VBIOS default setting. +usHDMISSPercentage: HDMI Spread Spectrum Percentage in unit 0.01%; 100 mean 1%, =0, use VBIOS default setting. +usHDMISSpreadRateIn10Hz: HDMI Spread Spectrum frequency in unit of 10Hz, =0, use VBIOS default setting. +usDVISSPercentage: DVI Spread Spectrum Percentage in unit 0.01%; 100 mean 1%, =0, use VBIOS default setting. +usDVISSpreadRateIn10Hz: DVI Spread Spectrum frequency in unit of 10Hz, =0, use VBIOS default setting. + +usGPUReservedSysMemSize: Reserved system memory size for ACP engine in APU GNB, units in MB. 0/2/4MB based on CMOS options, current default could be 0MB. KV only, not on KB. +ulGPUReservedSysMemBaseAddrLo: Low 32 bits base address to the reserved system memory. +ulGPUReservedSysMemBaseAddrHi: High 32 bits base address to the reserved system memory. + +usMaxLVDSPclkFreqInSingleLink: Max pixel clock LVDS panel single link, if=0 means VBIOS use default threhold, right now it is 85Mhz +ucLVDSMisc: [bit0] LVDS 888bit panel mode =0: LVDS 888 panel in LDI mode, =1: LVDS 888 panel in FPDI mode + [bit1] LVDS panel lower and upper link mapping =0: lower link and upper link not swap, =1: lower link and upper link are swapped + [bit2] LVDS 888bit per color mode =0: 666 bit per color =1:888 bit per color + [bit3] LVDS parameter override enable =0: ucLvdsMisc parameter are not used =1: ucLvdsMisc parameter should be used + [bit4] Polarity of signal sent to digital BLON output pin. =0: not inverted(active high) =1: inverted ( active low ) + [bit5] Travid LVDS output voltage override enable, when =1, use ucTravisLVDSVolAdjust value to overwrite Traivs register LVDS_CTRL_4 +ucTravisLVDSVolAdjust When ucLVDSMisc[5]=1,it means platform SBIOS want to overwrite TravisLVDSVoltage. Then VBIOS will use ucTravisLVDSVolAdjust + value to program Travis register LVDS_CTRL_4 +ucLVDSPwrOnSeqDIGONtoDE_in4Ms: + LVDS power up sequence time in unit of 4ms, time delay from DIGON signal active to data enable signal active( DE ). + =0 mean use VBIOS default which is 8 ( 32ms ). The LVDS power up sequence is as following: DIGON->DE->VARY_BL->BLON. + This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable. +ucLVDSPwrOnDEtoVARY_BL_in4Ms: + LVDS power up sequence time in unit of 4ms., time delay from DE( data enable ) active to Vary Brightness enable signal active( VARY_BL ). + =0 mean use VBIOS default which is 90 ( 360ms ). The LVDS power up sequence is as following: DIGON->DE->VARY_BL->BLON. + This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable. +ucLVDSPwrOffVARY_BLtoDE_in4Ms: + LVDS power down sequence time in unit of 4ms, time delay from data enable ( DE ) signal off to LCDVCC (DIGON) off. + =0 mean use VBIOS default delay which is 8 ( 32ms ). The LVDS power down sequence is as following: BLON->VARY_BL->DE->DIGON + This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable. +ucLVDSPwrOffDEtoDIGON_in4Ms: + LVDS power down sequence time in unit of 4ms, time delay from vary brightness enable signal( VARY_BL) off to data enable ( DE ) signal off. + =0 mean use VBIOS default which is 90 ( 360ms ). The LVDS power down sequence is as following: BLON->VARY_BL->DE->DIGON + This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable. +ucLVDSOffToOnDelay_in4Ms: + LVDS power down sequence time in unit of 4ms. Time delay from DIGON signal off to DIGON signal active. + =0 means to use VBIOS default delay which is 125 ( 500ms ). + This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable. +ucLVDSPwrOnSeqVARY_BLtoBLON_in4Ms: + LVDS power up sequence time in unit of 4ms. Time delay from VARY_BL signal on to DLON signal active. + =0 means to use VBIOS default delay which is 0 ( 0ms ). + This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable. + +ucLVDSPwrOffSeqBLONtoVARY_BL_in4Ms: + LVDS power down sequence time in unit of 4ms. Time delay from BLON signal off to VARY_BL signal off. + =0 means to use VBIOS default delay which is 0 ( 0ms ). + This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable. +ucMinAllowedBL_Level: Lowest LCD backlight PWM level. This is customer platform specific parameters. By default it is 0. + +ulLCDBitDepthControlVal: GPU display control encoder bit dither control setting, used to program register mmFMT_BIT_DEPTH_CONTROL + +ulNbpStateMemclkFreq[4]: system memory clock frequncey in unit of 10Khz in different NB P-State(P0, P1, P2 & P3). +ulNbpStateNClkFreq[4]: NB P-State NClk frequency in different NB P-State +usNBPStateVoltage[4]: NB P-State (P0/P1 & P2/P3) voltage; NBP3 refers to lowes voltage +usBootUpNBVoltage: NB P-State voltage during boot up before driver loaded +sExtDispConnInfo: Display connector information table provided to VBIOS + +**********************************************************************************************************************/ + +// this Table is used for Kaveri/Kabini APU +typedef struct _ATOM_FUSION_SYSTEM_INFO_V2 +{ + ATOM_INTEGRATED_SYSTEM_INFO_V1_8 sIntegratedSysInfo; // refer to ATOM_INTEGRATED_SYSTEM_INFO_V1_8 definition + ULONG ulPowerplayTable[128]; // Update comments here to link new powerplay table definition structure +}ATOM_FUSION_SYSTEM_INFO_V2; + + /**************************************************************************/ // This portion is only used when ext thermal chip or engine/memory clock SS chip is populated on a design //Memory SS Info Table @@ -5026,22 +5380,24 @@ typedef struct _ATOM_ASIC_SS_ASSIGNMENT
//Define ucClockIndication, SW uses the IDs below to search if the SS is required/enabled on a clock branch/signal type. //SS is not required or enabled if a match is not found. -#define ASIC_INTERNAL_MEMORY_SS 1 -#define ASIC_INTERNAL_ENGINE_SS 2 -#define ASIC_INTERNAL_UVD_SS 3 -#define ASIC_INTERNAL_SS_ON_TMDS 4 -#define ASIC_INTERNAL_SS_ON_HDMI 5 -#define ASIC_INTERNAL_SS_ON_LVDS 6 -#define ASIC_INTERNAL_SS_ON_DP 7 -#define ASIC_INTERNAL_SS_ON_DCPLL 8 -#define ASIC_EXTERNAL_SS_ON_DP_CLOCK 9 -#define ASIC_INTERNAL_VCE_SS 10 +#define ASIC_INTERNAL_MEMORY_SS 1 +#define ASIC_INTERNAL_ENGINE_SS 2 +#define ASIC_INTERNAL_UVD_SS 3 +#define ASIC_INTERNAL_SS_ON_TMDS 4 +#define ASIC_INTERNAL_SS_ON_HDMI 5 +#define ASIC_INTERNAL_SS_ON_LVDS 6 +#define ASIC_INTERNAL_SS_ON_DP 7 +#define ASIC_INTERNAL_SS_ON_DCPLL 8 +#define ASIC_EXTERNAL_SS_ON_DP_CLOCK 9 +#define ASIC_INTERNAL_VCE_SS 10 +#define ASIC_INTERNAL_GPUPLL_SS 11 +
typedef struct _ATOM_ASIC_SS_ASSIGNMENT_V2 { ULONG ulTargetClockRange; //For mem/engine/uvd, Clock Out frequence (VCO ), in unit of 10Khz //For TMDS/HDMI/LVDS, it is pixel clock , for DP, it is link clock ( 27000 or 16200 ) - USHORT usSpreadSpectrumPercentage; //in unit of 0.01% + USHORT usSpreadSpectrumPercentage; //in unit of 0.01% or 0.001%, decided by ucSpreadSpectrumMode bit4 USHORT usSpreadRateIn10Hz; //in unit of 10Hz, modulation freq UCHAR ucClockIndication; //Indicate which clock source needs SS UCHAR ucSpreadSpectrumMode; //Bit0=0 Down Spread,=1 Center Spread, bit1=0: internal SS bit1=1: external SS @@ -5079,6 +5435,11 @@ typedef struct _ATOM_ASIC_SS_ASSIGNMENT_V3 UCHAR ucReserved[2]; }ATOM_ASIC_SS_ASSIGNMENT_V3;
+//ATOM_ASIC_SS_ASSIGNMENT_V3.ucSpreadSpectrumMode +#define SS_MODE_V3_CENTRE_SPREAD_MASK 0x01 +#define SS_MODE_V3_EXTERNAL_SS_MASK 0x02 +#define SS_MODE_V3_PERCENTAGE_DIV_BY_1000_MASK 0x10 + typedef struct _ATOM_ASIC_INTERNAL_SS_INFO_V3 { ATOM_COMMON_TABLE_HEADER sHeader; @@ -5719,6 +6080,7 @@ typedef struct _INDIRECT_IO_ACCESS #define INDIRECT_IO_PCIE 3 #define INDIRECT_IO_PCIEP 4 #define INDIRECT_IO_NBMISC 5 +#define INDIRECT_IO_SMU 5
#define INDIRECT_IO_PLL_READ INDIRECT_IO_PLL | INDIRECT_READ #define INDIRECT_IO_PLL_WRITE INDIRECT_IO_PLL | INDIRECT_WRITE @@ -5730,6 +6092,8 @@ typedef struct _INDIRECT_IO_ACCESS #define INDIRECT_IO_PCIEP_WRITE INDIRECT_IO_PCIEP | INDIRECT_WRITE #define INDIRECT_IO_NBMISC_READ INDIRECT_IO_NBMISC | INDIRECT_READ #define INDIRECT_IO_NBMISC_WRITE INDIRECT_IO_NBMISC | INDIRECT_WRITE +#define INDIRECT_IO_SMU_READ INDIRECT_IO_SMU | INDIRECT_READ +#define INDIRECT_IO_SMU_WRITE INDIRECT_IO_SMU | INDIRECT_WRITE
typedef struct _ATOM_OEM_INFO { @@ -5875,6 +6239,7 @@ typedef struct _ATOM_MC_INIT_PARAM_TABLE #define _64Mx32 0x43 #define _128Mx8 0x51 #define _128Mx16 0x52 +#define _128Mx32 0x53 #define _256Mx8 0x61 #define _256Mx16 0x62
@@ -5893,6 +6258,8 @@ typedef struct _ATOM_MC_INIT_PARAM_TABLE #define PROMOS MOSEL #define KRETON INFINEON #define ELIXIR NANYA +#define MEZZA ELPIDA +
/////////////Support for GDDR5 MC uCode to reside in upper 64K of ROM/////////////
@@ -6625,6 +6992,10 @@ typedef struct _ATOM_DISP_OUT_INFO_V3 ASIC_TRANSMITTER_INFO_V2 asTransmitterInfo[1]; // for alligment only }ATOM_DISP_OUT_INFO_V3;
+//ucDispCaps +#define DISPLAY_CAPS__DP_PCLK_FROM_PPLL 0x01 +#define DISPLAY_CAPS__FORCE_DISPDEV_CONNECTED 0x02 + typedef enum CORE_REF_CLK_SOURCE{ CLOCK_SRC_XTALIN=0, CLOCK_SRC_XO_IN=1, @@ -6829,6 +7200,17 @@ typedef struct _DIG_TRANSMITTER_INFO_HEADER_V3_1{ USHORT usPhyPllSettingOffset; // offset of CLOCK_CONDITION_SETTING_ENTRY* with Phy Pll Settings }DIG_TRANSMITTER_INFO_HEADER_V3_1;
+typedef struct _DIG_TRANSMITTER_INFO_HEADER_V3_2{ + ATOM_COMMON_TABLE_HEADER sHeader; + USHORT usDPVsPreEmphSettingOffset; // offset of PHY_ANALOG_SETTING_INFO * with DP Voltage Swing and Pre-Emphasis for each Link clock + USHORT usPhyAnalogRegListOffset; // offset of CLOCK_CONDITION_REGESTER_INFO* with None-DP mode Analog Setting's register Info + USHORT usPhyAnalogSettingOffset; // offset of CLOCK_CONDITION_SETTING_ENTRY* with None-DP mode Analog Setting for each link clock range + USHORT usPhyPllRegListOffset; // offset of CLOCK_CONDITION_REGESTER_INFO* with Phy Pll register Info + USHORT usPhyPllSettingOffset; // offset of CLOCK_CONDITION_SETTING_ENTRY* with Phy Pll Settings + USHORT usDPSSRegListOffset; // offset of CLOCK_CONDITION_REGESTER_INFO* with Phy SS Pll register Info + USHORT usDPSSSettingOffset; // offset of CLOCK_CONDITION_SETTING_ENTRY* with Phy SS Pll Settings +}DIG_TRANSMITTER_INFO_HEADER_V3_2; + typedef struct _CLOCK_CONDITION_REGESTER_INFO{ USHORT usRegisterIndex; UCHAR ucStartBit; @@ -6852,12 +7234,24 @@ typedef struct _PHY_CONDITION_REG_VAL{ ULONG ulRegVal; }PHY_CONDITION_REG_VAL;
+typedef struct _PHY_CONDITION_REG_VAL_V2{ + ULONG ulCondition; + UCHAR ucCondition2; + ULONG ulRegVal; +}PHY_CONDITION_REG_VAL_V2; + typedef struct _PHY_CONDITION_REG_INFO{ USHORT usRegIndex; USHORT usSize; PHY_CONDITION_REG_VAL asRegVal[1]; }PHY_CONDITION_REG_INFO;
+typedef struct _PHY_CONDITION_REG_INFO_V2{ + USHORT usRegIndex; + USHORT usSize; + PHY_CONDITION_REG_VAL_V2 asRegVal[1]; +}PHY_CONDITION_REG_INFO_V2; + typedef struct _PHY_ANALOG_SETTING_INFO{ UCHAR ucEncodeMode; UCHAR ucPhySel; @@ -6865,6 +7259,25 @@ typedef struct _PHY_ANALOG_SETTING_INFO{ PHY_CONDITION_REG_INFO asAnalogSetting[1]; }PHY_ANALOG_SETTING_INFO;
+typedef struct _PHY_ANALOG_SETTING_INFO_V2{ + UCHAR ucEncodeMode; + UCHAR ucPhySel; + USHORT usSize; + PHY_CONDITION_REG_INFO_V2 asAnalogSetting[1]; +}PHY_ANALOG_SETTING_INFO_V2; + +typedef struct _GFX_HAVESTING_PARAMETERS { + UCHAR ucGfxBlkId; //GFX blk id to be harvested, like CU, RB or PRIM + UCHAR ucReserved; //reserved + UCHAR ucActiveUnitNumPerSH; //requested active CU/RB/PRIM number per shader array + UCHAR ucMaxUnitNumPerSH; //max CU/RB/PRIM number per shader array +} GFX_HAVESTING_PARAMETERS; + +//ucGfxBlkId +#define GFX_HARVESTING_CU_ID 0 +#define GFX_HARVESTING_RB_ID 1 +#define GFX_HARVESTING_PRIM_ID 2 + /****************************************************************************/ //Portion VI: Definitinos for vbios MC scratch registers that driver used /****************************************************************************/ @@ -6875,8 +7288,17 @@ typedef struct _PHY_ANALOG_SETTING_INFO{ #define MC_MISC0__MEMORY_TYPE__GDDR3 0x30000000 #define MC_MISC0__MEMORY_TYPE__GDDR4 0x40000000 #define MC_MISC0__MEMORY_TYPE__GDDR5 0x50000000 +#define MC_MISC0__MEMORY_TYPE__HBM 0x60000000 #define MC_MISC0__MEMORY_TYPE__DDR3 0xB0000000
+#define ATOM_MEM_TYPE_DDR_STRING "DDR" +#define ATOM_MEM_TYPE_DDR2_STRING "DDR2" +#define ATOM_MEM_TYPE_GDDR3_STRING "GDDR3" +#define ATOM_MEM_TYPE_GDDR4_STRING "GDDR4" +#define ATOM_MEM_TYPE_GDDR5_STRING "GDDR5" +#define ATOM_MEM_TYPE_HBM_STRING "HBM" +#define ATOM_MEM_TYPE_DDR3_STRING "DDR3" + /****************************************************************************/ //Portion VI: Definitinos being oboselete /****************************************************************************/
From: Alex Deucher alexander.deucher@amd.com
v2: further updates
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/atombios.h | 58 ++++++++++++++++++++++++++++++++++-- 1 files changed, 54 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/radeon/atombios.h b/drivers/gpu/drm/radeon/atombios.h index f19d9a6..7ba9588 100644 --- a/drivers/gpu/drm/radeon/atombios.h +++ b/drivers/gpu/drm/radeon/atombios.h @@ -7696,6 +7696,7 @@ typedef struct _ATOM_PPLIB_THERMALCONTROLLER #define ATOM_PP_THERMALCONTROLLER_NISLANDS 15 #define ATOM_PP_THERMALCONTROLLER_SISLANDS 16 #define ATOM_PP_THERMALCONTROLLER_LM96163 17 +#define ATOM_PP_THERMALCONTROLLER_CISLANDS 18
// Thermal controller 'combo type' to use an external controller for Fan control and an internal controller for thermal. // We probably should reserve the bit 0x80 for this use. @@ -7738,6 +7739,8 @@ typedef struct _ATOM_PPLIB_EXTENDEDHEADER // Add extra system parameters here, always adjust size to include all fields. USHORT usVCETableOffset; //points to ATOM_PPLIB_VCE_Table USHORT usUVDTableOffset; //points to ATOM_PPLIB_UVD_Table + USHORT usSAMUTableOffset; //points to ATOM_PPLIB_SAMU_Table + USHORT usPPMTableOffset; //points to ATOM_PPLIB_PPM_Table } ATOM_PPLIB_EXTENDEDHEADER;
//// ATOM_PPLIB_POWERPLAYTABLE::ulPlatformCaps @@ -7759,7 +7762,7 @@ typedef struct _ATOM_PPLIB_EXTENDEDHEADER #define ATOM_PP_PLATFORM_CAP_VDDCI_CONTROL 0x8000 // Does the driver control VDDCI independently from VDDC. #define ATOM_PP_PLATFORM_CAP_REGULATOR_HOT 0x00010000 // Enable the 'regulator hot' feature. #define ATOM_PP_PLATFORM_CAP_BACO 0x00020000 // Does the driver supports BACO state. - +#define ATOM_PP_PLATFORM_CAP_NEW_CAC_VOLTAGE 0x00040000 // Does the driver supports new CAC voltage table.
typedef struct _ATOM_PPLIB_POWERPLAYTABLE { @@ -7820,7 +7823,7 @@ typedef struct _ATOM_PPLIB_POWERPLAYTABLE4 USHORT usVddcDependencyOnMCLKOffset; USHORT usMaxClockVoltageOnDCOffset; USHORT usVddcPhaseShedLimitsTableOffset; // Points to ATOM_PPLIB_PhaseSheddingLimits_Table - USHORT usReserved; + USHORT usMvddDependencyOnMCLKOffset; } ATOM_PPLIB_POWERPLAYTABLE4, *LPATOM_PPLIB_POWERPLAYTABLE4;
typedef struct _ATOM_PPLIB_POWERPLAYTABLE5 @@ -7985,6 +7988,17 @@ typedef struct _ATOM_PPLIB_SI_CLOCK_INFO
} ATOM_PPLIB_SI_CLOCK_INFO;
+typedef struct _ATOM_PPLIB_CI_CLOCK_INFO +{ + USHORT usEngineClockLow; + UCHAR ucEngineClockHigh; + + USHORT usMemoryClockLow; + UCHAR ucMemoryClockHigh; + + UCHAR ucPCIEGen; + USHORT usPCIELane; +} ATOM_PPLIB_CI_CLOCK_INFO;
typedef struct _ATOM_PPLIB_RS780_CLOCK_INFO
@@ -8102,8 +8116,8 @@ typedef struct _ATOM_PPLIB_Clock_Voltage_Limit_Table
typedef struct _ATOM_PPLIB_CAC_Leakage_Record { - USHORT usVddc; // We use this field for the "fake" standardized VDDC for power calculations - ULONG ulLeakageValue; + USHORT usVddc; // We use this field for the "fake" standardized VDDC for power calculations; For CI and newer, we use this as the real VDDC value. + ULONG ulLeakageValue; // For CI and newer we use this as the "fake" standar VDDC value. }ATOM_PPLIB_CAC_Leakage_Record;
typedef struct _ATOM_PPLIB_CAC_Leakage_Table @@ -8218,6 +8232,42 @@ typedef struct _ATOM_PPLIB_UVD_Table // ATOM_PPLIB_UVD_State_Table states; }ATOM_PPLIB_UVD_Table;
+ +typedef struct _ATOM_PPLIB_SAMClk_Voltage_Limit_Record +{ + USHORT usVoltage; + USHORT usSAMClockLow; + UCHAR ucSAMClockHigh; +}ATOM_PPLIB_SAMClk_Voltage_Limit_Record; + +typedef struct _ATOM_PPLIB_SAMClk_Voltage_Limit_Table{ + UCHAR numEntries; + ATOM_PPLIB_SAMClk_Voltage_Limit_Record entries[1]; +}ATOM_PPLIB_SAMClk_Voltage_Limit_Table; + +typedef struct _ATOM_PPLIB_SAMU_Table +{ + UCHAR revid; + ATOM_PPLIB_SAMClk_Voltage_Limit_Table limits; +}ATOM_PPLIB_SAMU_Table; + +#define ATOM_PPM_A_A 1 +#define ATOM_PPM_A_I 2 +typedef struct _ATOM_PPLIB_PPM_Table +{ + UCHAR ucRevId; + UCHAR ucPpmDesign; //A+I or A+A + USHORT usCpuCoreNumber; + ULONG ulPlatformTDP; + ULONG ulSmallACPlatformTDP; + ULONG ulPlatformTDC; + ULONG ulSmallACPlatformTDC; + ULONG ulApuTDP; + ULONG ulDGpuTDP; + ULONG ulDGpuUlvPower; + ULONG ulTjmax; +} ATOM_PPLIB_PPM_Table; + /**************************************************************************/
From: Alex Deucher alexander.deucher@amd.com
No support for reading the temperature yet.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/radeon.h | 1 + drivers/gpu/drm/radeon/radeon_atombios.c | 6 ++++++ 2 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 919c4d8..04e8dbd 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1033,6 +1033,7 @@ enum radeon_int_thermal_type { THERMAL_TYPE_SUMO, THERMAL_TYPE_NI, THERMAL_TYPE_SI, + THERMAL_TYPE_CI, };
struct radeon_voltage { diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index dea6f63c..cb3273b 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -1927,6 +1927,7 @@ static const char *pp_lib_thermal_controller_names[] = { "Northern Islands", "Southern Islands", "lm96163", + "Sea Islands", };
union power_info { @@ -2209,6 +2210,11 @@ static void radeon_atombios_add_pplib_thermal_controller(struct radeon_device *r (controller->ucFanParameters & ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); rdev->pm.int_thermal_type = THERMAL_TYPE_SI; + } else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_CISLANDS) { + DRM_INFO("Internal thermal controller %s fan control\n", + (controller->ucFanParameters & + ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with"); + rdev->pm.int_thermal_type = THERMAL_TYPE_CI; } else if ((controller->ucType == ATOM_PP_THERMALCONTROLLER_EXTERNAL_GPIO) || (controller->ucType ==
From: Alex Deucher alexander.deucher@amd.com
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/radeon_atombios.c | 10 ++++++++++ 1 files changed, 10 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index cb3273b..88a55af 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -1945,6 +1945,7 @@ union pplib_clock_info { struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO evergreen; struct _ATOM_PPLIB_SUMO_CLOCK_INFO sumo; struct _ATOM_PPLIB_SI_CLOCK_INFO si; + struct _ATOM_PPLIB_CI_CLOCK_INFO ci; };
union pplib_power_state { @@ -2353,6 +2354,15 @@ static bool radeon_atombios_parse_pplib_clock_info(struct radeon_device *rdev, sclk |= clock_info->rs780.ucLowEngineClockHigh << 16; rdev->pm.power_state[state_index].clock_info[mode_index].sclk = sclk; } + } else if (rdev->family >= CHIP_BONAIRE) { + sclk = le16_to_cpu(clock_info->ci.usEngineClockLow); + sclk |= clock_info->ci.ucEngineClockHigh << 16; + mclk = le16_to_cpu(clock_info->ci.usMemoryClockLow); + mclk |= clock_info->ci.ucMemoryClockHigh << 16; + rdev->pm.power_state[state_index].clock_info[mode_index].mclk = mclk; + rdev->pm.power_state[state_index].clock_info[mode_index].sclk = sclk; + rdev->pm.power_state[state_index].clock_info[mode_index].voltage.type = + VOLTAGE_NONE; } else if (rdev->family >= CHIP_TAHITI) { sclk = le16_to_cpu(clock_info->si.usEngineClockLow); sclk |= clock_info->si.ucEngineClockHigh << 16;
From: Alex Deucher alexander.deucher@amd.com
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/cik.c | 537 +++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/cikd.h | 11 + 2 files changed, 548 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index cbc64a2..228d399 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -5021,3 +5021,540 @@ void cik_fini(struct radeon_device *rdev) kfree(rdev->bios); rdev->bios = NULL; } + +/* display watermark setup */ +/** + * dce8_line_buffer_adjust - Set up the line buffer + * + * @rdev: radeon_device pointer + * @radeon_crtc: the selected display controller + * @mode: the current display mode on the selected display + * controller + * + * Setup up the line buffer allocation for + * the selected display controller (CIK). + * Returns the line buffer size in pixels. + */ +static u32 dce8_line_buffer_adjust(struct radeon_device *rdev, + struct radeon_crtc *radeon_crtc, + struct drm_display_mode *mode) +{ + u32 tmp; + + /* + * Line Buffer Setup + * There are 6 line buffers, one for each display controllers. + * There are 3 partitions per LB. Select the number of partitions + * to enable based on the display width. For display widths larger + * than 4096, you need use to use 2 display controllers and combine + * them using the stereo blender. + */ + if (radeon_crtc->base.enabled && mode) { + if (mode->crtc_hdisplay < 1920) + tmp = 1; + else if (mode->crtc_hdisplay < 2560) + tmp = 2; + else if (mode->crtc_hdisplay < 4096) + tmp = 0; + else { + DRM_DEBUG_KMS("Mode too big for LB!\n"); + tmp = 0; + } + } else + tmp = 1; + + WREG32(LB_MEMORY_CTRL + radeon_crtc->crtc_offset, + LB_MEMORY_CONFIG(tmp) | LB_MEMORY_SIZE(0x6B0)); + + if (radeon_crtc->base.enabled && mode) { + switch (tmp) { + case 0: + default: + return 4096 * 2; + case 1: + return 1920 * 2; + case 2: + return 2560 * 2; + } + } + + /* controller not enabled, so no lb used */ + return 0; +} + +/** + * cik_get_number_of_dram_channels - get the number of dram channels + * + * @rdev: radeon_device pointer + * + * Look up the number of video ram channels (CIK). + * Used for display watermark bandwidth calculations + * Returns the number of dram channels + */ +static u32 cik_get_number_of_dram_channels(struct radeon_device *rdev) +{ + u32 tmp = RREG32(MC_SHARED_CHMAP); + + switch ((tmp & NOOFCHAN_MASK) >> NOOFCHAN_SHIFT) { + case 0: + default: + return 1; + case 1: + return 2; + case 2: + return 4; + case 3: + return 8; + case 4: + return 3; + case 5: + return 6; + case 6: + return 10; + case 7: + return 12; + case 8: + return 16; + } +} + +struct dce8_wm_params { + u32 dram_channels; /* number of dram channels */ + u32 yclk; /* bandwidth per dram data pin in kHz */ + u32 sclk; /* engine clock in kHz */ + u32 disp_clk; /* display clock in kHz */ + u32 src_width; /* viewport width */ + u32 active_time; /* active display time in ns */ + u32 blank_time; /* blank time in ns */ + bool interlaced; /* mode is interlaced */ + fixed20_12 vsc; /* vertical scale ratio */ + u32 num_heads; /* number of active crtcs */ + u32 bytes_per_pixel; /* bytes per pixel display + overlay */ + u32 lb_size; /* line buffer allocated to pipe */ + u32 vtaps; /* vertical scaler taps */ +}; + +/** + * dce8_dram_bandwidth - get the dram bandwidth + * + * @wm: watermark calculation data + * + * Calculate the raw dram bandwidth (CIK). + * Used for display watermark bandwidth calculations + * Returns the dram bandwidth in MBytes/s + */ +static u32 dce8_dram_bandwidth(struct dce8_wm_params *wm) +{ + /* Calculate raw DRAM Bandwidth */ + fixed20_12 dram_efficiency; /* 0.7 */ + fixed20_12 yclk, dram_channels, bandwidth; + fixed20_12 a; + + a.full = dfixed_const(1000); + yclk.full = dfixed_const(wm->yclk); + yclk.full = dfixed_div(yclk, a); + dram_channels.full = dfixed_const(wm->dram_channels * 4); + a.full = dfixed_const(10); + dram_efficiency.full = dfixed_const(7); + dram_efficiency.full = dfixed_div(dram_efficiency, a); + bandwidth.full = dfixed_mul(dram_channels, yclk); + bandwidth.full = dfixed_mul(bandwidth, dram_efficiency); + + return dfixed_trunc(bandwidth); +} + +/** + * dce8_dram_bandwidth_for_display - get the dram bandwidth for display + * + * @wm: watermark calculation data + * + * Calculate the dram bandwidth used for display (CIK). + * Used for display watermark bandwidth calculations + * Returns the dram bandwidth for display in MBytes/s + */ +static u32 dce8_dram_bandwidth_for_display(struct dce8_wm_params *wm) +{ + /* Calculate DRAM Bandwidth and the part allocated to display. */ + fixed20_12 disp_dram_allocation; /* 0.3 to 0.7 */ + fixed20_12 yclk, dram_channels, bandwidth; + fixed20_12 a; + + a.full = dfixed_const(1000); + yclk.full = dfixed_const(wm->yclk); + yclk.full = dfixed_div(yclk, a); + dram_channels.full = dfixed_const(wm->dram_channels * 4); + a.full = dfixed_const(10); + disp_dram_allocation.full = dfixed_const(3); /* XXX worse case value 0.3 */ + disp_dram_allocation.full = dfixed_div(disp_dram_allocation, a); + bandwidth.full = dfixed_mul(dram_channels, yclk); + bandwidth.full = dfixed_mul(bandwidth, disp_dram_allocation); + + return dfixed_trunc(bandwidth); +} + +/** + * dce8_data_return_bandwidth - get the data return bandwidth + * + * @wm: watermark calculation data + * + * Calculate the data return bandwidth used for display (CIK). + * Used for display watermark bandwidth calculations + * Returns the data return bandwidth in MBytes/s + */ +static u32 dce8_data_return_bandwidth(struct dce8_wm_params *wm) +{ + /* Calculate the display Data return Bandwidth */ + fixed20_12 return_efficiency; /* 0.8 */ + fixed20_12 sclk, bandwidth; + fixed20_12 a; + + a.full = dfixed_const(1000); + sclk.full = dfixed_const(wm->sclk); + sclk.full = dfixed_div(sclk, a); + a.full = dfixed_const(10); + return_efficiency.full = dfixed_const(8); + return_efficiency.full = dfixed_div(return_efficiency, a); + a.full = dfixed_const(32); + bandwidth.full = dfixed_mul(a, sclk); + bandwidth.full = dfixed_mul(bandwidth, return_efficiency); + + return dfixed_trunc(bandwidth); +} + +/** + * dce8_dmif_request_bandwidth - get the dmif bandwidth + * + * @wm: watermark calculation data + * + * Calculate the dmif bandwidth used for display (CIK). + * Used for display watermark bandwidth calculations + * Returns the dmif bandwidth in MBytes/s + */ +static u32 dce8_dmif_request_bandwidth(struct dce8_wm_params *wm) +{ + /* Calculate the DMIF Request Bandwidth */ + fixed20_12 disp_clk_request_efficiency; /* 0.8 */ + fixed20_12 disp_clk, bandwidth; + fixed20_12 a, b; + + a.full = dfixed_const(1000); + disp_clk.full = dfixed_const(wm->disp_clk); + disp_clk.full = dfixed_div(disp_clk, a); + a.full = dfixed_const(32); + b.full = dfixed_mul(a, disp_clk); + + a.full = dfixed_const(10); + disp_clk_request_efficiency.full = dfixed_const(8); + disp_clk_request_efficiency.full = dfixed_div(disp_clk_request_efficiency, a); + + bandwidth.full = dfixed_mul(b, disp_clk_request_efficiency); + + return dfixed_trunc(bandwidth); +} + +/** + * dce8_available_bandwidth - get the min available bandwidth + * + * @wm: watermark calculation data + * + * Calculate the min available bandwidth used for display (CIK). + * Used for display watermark bandwidth calculations + * Returns the min available bandwidth in MBytes/s + */ +static u32 dce8_available_bandwidth(struct dce8_wm_params *wm) +{ + /* Calculate the Available bandwidth. Display can use this temporarily but not in average. */ + u32 dram_bandwidth = dce8_dram_bandwidth(wm); + u32 data_return_bandwidth = dce8_data_return_bandwidth(wm); + u32 dmif_req_bandwidth = dce8_dmif_request_bandwidth(wm); + + return min(dram_bandwidth, min(data_return_bandwidth, dmif_req_bandwidth)); +} + +/** + * dce8_average_bandwidth - get the average available bandwidth + * + * @wm: watermark calculation data + * + * Calculate the average available bandwidth used for display (CIK). + * Used for display watermark bandwidth calculations + * Returns the average available bandwidth in MBytes/s + */ +static u32 dce8_average_bandwidth(struct dce8_wm_params *wm) +{ + /* Calculate the display mode Average Bandwidth + * DisplayMode should contain the source and destination dimensions, + * timing, etc. + */ + fixed20_12 bpp; + fixed20_12 line_time; + fixed20_12 src_width; + fixed20_12 bandwidth; + fixed20_12 a; + + a.full = dfixed_const(1000); + line_time.full = dfixed_const(wm->active_time + wm->blank_time); + line_time.full = dfixed_div(line_time, a); + bpp.full = dfixed_const(wm->bytes_per_pixel); + src_width.full = dfixed_const(wm->src_width); + bandwidth.full = dfixed_mul(src_width, bpp); + bandwidth.full = dfixed_mul(bandwidth, wm->vsc); + bandwidth.full = dfixed_div(bandwidth, line_time); + + return dfixed_trunc(bandwidth); +} + +/** + * dce8_latency_watermark - get the latency watermark + * + * @wm: watermark calculation data + * + * Calculate the latency watermark (CIK). + * Used for display watermark bandwidth calculations + * Returns the latency watermark in ns + */ +static u32 dce8_latency_watermark(struct dce8_wm_params *wm) +{ + /* First calculate the latency in ns */ + u32 mc_latency = 2000; /* 2000 ns. */ + u32 available_bandwidth = dce8_available_bandwidth(wm); + u32 worst_chunk_return_time = (512 * 8 * 1000) / available_bandwidth; + u32 cursor_line_pair_return_time = (128 * 4 * 1000) / available_bandwidth; + u32 dc_latency = 40000000 / wm->disp_clk; /* dc pipe latency */ + u32 other_heads_data_return_time = ((wm->num_heads + 1) * worst_chunk_return_time) + + (wm->num_heads * cursor_line_pair_return_time); + u32 latency = mc_latency + other_heads_data_return_time + dc_latency; + u32 max_src_lines_per_dst_line, lb_fill_bw, line_fill_time; + u32 tmp, dmif_size = 12288; + fixed20_12 a, b, c; + + if (wm->num_heads == 0) + return 0; + + a.full = dfixed_const(2); + b.full = dfixed_const(1); + if ((wm->vsc.full > a.full) || + ((wm->vsc.full > b.full) && (wm->vtaps >= 3)) || + (wm->vtaps >= 5) || + ((wm->vsc.full >= a.full) && wm->interlaced)) + max_src_lines_per_dst_line = 4; + else + max_src_lines_per_dst_line = 2; + + a.full = dfixed_const(available_bandwidth); + b.full = dfixed_const(wm->num_heads); + a.full = dfixed_div(a, b); + + b.full = dfixed_const(mc_latency + 512); + c.full = dfixed_const(wm->disp_clk); + b.full = dfixed_div(b, c); + + c.full = dfixed_const(dmif_size); + b.full = dfixed_div(c, b); + + tmp = min(dfixed_trunc(a), dfixed_trunc(b)); + + b.full = dfixed_const(1000); + c.full = dfixed_const(wm->disp_clk); + b.full = dfixed_div(c, b); + c.full = dfixed_const(wm->bytes_per_pixel); + b.full = dfixed_mul(b, c); + + lb_fill_bw = min(tmp, dfixed_trunc(b)); + + a.full = dfixed_const(max_src_lines_per_dst_line * wm->src_width * wm->bytes_per_pixel); + b.full = dfixed_const(1000); + c.full = dfixed_const(lb_fill_bw); + b.full = dfixed_div(c, b); + a.full = dfixed_div(a, b); + line_fill_time = dfixed_trunc(a); + + if (line_fill_time < wm->active_time) + return latency; + else + return latency + (line_fill_time - wm->active_time); + +} + +/** + * dce8_average_bandwidth_vs_dram_bandwidth_for_display - check + * average and available dram bandwidth + * + * @wm: watermark calculation data + * + * Check if the display average bandwidth fits in the display + * dram bandwidth (CIK). + * Used for display watermark bandwidth calculations + * Returns true if the display fits, false if not. + */ +static bool dce8_average_bandwidth_vs_dram_bandwidth_for_display(struct dce8_wm_params *wm) +{ + if (dce8_average_bandwidth(wm) <= + (dce8_dram_bandwidth_for_display(wm) / wm->num_heads)) + return true; + else + return false; +} + +/** + * dce8_average_bandwidth_vs_available_bandwidth - check + * average and available bandwidth + * + * @wm: watermark calculation data + * + * Check if the display average bandwidth fits in the display + * available bandwidth (CIK). + * Used for display watermark bandwidth calculations + * Returns true if the display fits, false if not. + */ +static bool dce8_average_bandwidth_vs_available_bandwidth(struct dce8_wm_params *wm) +{ + if (dce8_average_bandwidth(wm) <= + (dce8_available_bandwidth(wm) / wm->num_heads)) + return true; + else + return false; +} + +/** + * dce8_check_latency_hiding - check latency hiding + * + * @wm: watermark calculation data + * + * Check latency hiding (CIK). + * Used for display watermark bandwidth calculations + * Returns true if the display fits, false if not. + */ +static bool dce8_check_latency_hiding(struct dce8_wm_params *wm) +{ + u32 lb_partitions = wm->lb_size / wm->src_width; + u32 line_time = wm->active_time + wm->blank_time; + u32 latency_tolerant_lines; + u32 latency_hiding; + fixed20_12 a; + + a.full = dfixed_const(1); + if (wm->vsc.full > a.full) + latency_tolerant_lines = 1; + else { + if (lb_partitions <= (wm->vtaps + 1)) + latency_tolerant_lines = 1; + else + latency_tolerant_lines = 2; + } + + latency_hiding = (latency_tolerant_lines * line_time + wm->blank_time); + + if (dce8_latency_watermark(wm) <= latency_hiding) + return true; + else + return false; +} + +/** + * dce8_program_watermarks - program display watermarks + * + * @rdev: radeon_device pointer + * @radeon_crtc: the selected display controller + * @lb_size: line buffer size + * @num_heads: number of display controllers in use + * + * Calculate and program the display watermarks for the + * selected display controller (CIK). + */ +static void dce8_program_watermarks(struct radeon_device *rdev, + struct radeon_crtc *radeon_crtc, + u32 lb_size, u32 num_heads) +{ + struct drm_display_mode *mode = &radeon_crtc->base.mode; + struct dce8_wm_params wm; + u32 pixel_period; + u32 line_time = 0; + u32 latency_watermark_a = 0, latency_watermark_b = 0; + u32 tmp, wm_mask; + + if (radeon_crtc->base.enabled && num_heads && mode) { + pixel_period = 1000000 / (u32)mode->clock; + line_time = min((u32)mode->crtc_htotal * pixel_period, (u32)65535); + + wm.yclk = rdev->pm.current_mclk * 10; + wm.sclk = rdev->pm.current_sclk * 10; + wm.disp_clk = mode->clock; + wm.src_width = mode->crtc_hdisplay; + wm.active_time = mode->crtc_hdisplay * pixel_period; + wm.blank_time = line_time - wm.active_time; + wm.interlaced = false; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + wm.interlaced = true; + wm.vsc = radeon_crtc->vsc; + wm.vtaps = 1; + if (radeon_crtc->rmx_type != RMX_OFF) + wm.vtaps = 2; + wm.bytes_per_pixel = 4; /* XXX: get this from fb config */ + wm.lb_size = lb_size; + wm.dram_channels = cik_get_number_of_dram_channels(rdev); + wm.num_heads = num_heads; + + /* set for high clocks */ + latency_watermark_a = min(dce8_latency_watermark(&wm), (u32)65535); + /* set for low clocks */ + /* wm.yclk = low clk; wm.sclk = low clk */ + latency_watermark_b = min(dce8_latency_watermark(&wm), (u32)65535); + + /* possibly force display priority to high */ + /* should really do this at mode validation time... */ + if (!dce8_average_bandwidth_vs_dram_bandwidth_for_display(&wm) || + !dce8_average_bandwidth_vs_available_bandwidth(&wm) || + !dce8_check_latency_hiding(&wm) || + (rdev->disp_priority == 2)) { + DRM_DEBUG_KMS("force priority to high\n"); + } + } + + /* select wm A */ + wm_mask = RREG32(DPG_WATERMARK_MASK_CONTROL + radeon_crtc->crtc_offset); + tmp = wm_mask; + tmp &= ~LATENCY_WATERMARK_MASK(3); + tmp |= LATENCY_WATERMARK_MASK(1); + WREG32(DPG_WATERMARK_MASK_CONTROL + radeon_crtc->crtc_offset, tmp); + WREG32(DPG_PIPE_LATENCY_CONTROL + radeon_crtc->crtc_offset, + (LATENCY_LOW_WATERMARK(latency_watermark_a) | + LATENCY_HIGH_WATERMARK(line_time))); + /* select wm B */ + tmp = RREG32(DPG_WATERMARK_MASK_CONTROL + radeon_crtc->crtc_offset); + tmp &= ~LATENCY_WATERMARK_MASK(3); + tmp |= LATENCY_WATERMARK_MASK(2); + WREG32(DPG_WATERMARK_MASK_CONTROL + radeon_crtc->crtc_offset, tmp); + WREG32(DPG_PIPE_LATENCY_CONTROL + radeon_crtc->crtc_offset, + (LATENCY_LOW_WATERMARK(latency_watermark_b) | + LATENCY_HIGH_WATERMARK(line_time))); + /* restore original selection */ + WREG32(DPG_WATERMARK_MASK_CONTROL + radeon_crtc->crtc_offset, wm_mask); +} + +/** + * dce8_bandwidth_update - program display watermarks + * + * @rdev: radeon_device pointer + * + * Calculate and program the display watermarks and line + * buffer allocation (CIK). + */ +void dce8_bandwidth_update(struct radeon_device *rdev) +{ + struct drm_display_mode *mode = NULL; + u32 num_heads = 0, lb_size; + int i; + + radeon_update_display_priority(rdev); + + for (i = 0; i < rdev->num_crtc; i++) { + if (rdev->mode_info.crtcs[i]->base.enabled) + num_heads++; + } + for (i = 0; i < rdev->num_crtc; i++) { + mode = &rdev->mode_info.crtcs[i]->base.mode; + lb_size = dce8_line_buffer_adjust(rdev, rdev->mode_info.crtcs[i], mode); + dce8_program_watermarks(rdev, rdev->mode_info.crtcs[i], lb_size, num_heads); + } +} diff --git a/drivers/gpu/drm/radeon/cikd.h b/drivers/gpu/drm/radeon/cikd.h index 39ed517..3349e37 100644 --- a/drivers/gpu/drm/radeon/cikd.h +++ b/drivers/gpu/drm/radeon/cikd.h @@ -259,6 +259,17 @@ #define SDMA0 (1 << 10) #define SDMA1 (1 << 11)
+/* 0x6b04, 0x7704, 0x10304, 0x10f04, 0x11b04, 0x12704 */ +#define LB_MEMORY_CTRL 0x6b04 +#define LB_MEMORY_SIZE(x) ((x) << 0) +#define LB_MEMORY_CONFIG(x) ((x) << 20) + +#define DPG_WATERMARK_MASK_CONTROL 0x6cc8 +# define LATENCY_WATERMARK_MASK(x) ((x) << 8) +#define DPG_PIPE_LATENCY_CONTROL 0x6ccc +# define LATENCY_LOW_WATERMARK(x) ((x) << 0) +# define LATENCY_HIGH_WATERMARK(x) ((x) << 16) + /* 0x6b24, 0x7724, 0x10324, 0x10f24, 0x11b24, 0x12724 */ #define LB_VLINE_STATUS 0x6b24 # define VLINE_OCCURRED (1 << 0)
From: Alex Deucher alexander.deucher@amd.com
CIK (DCE8) hw cursors are programmed the same as evergreen (DCE4) with the following caveats: - cursors are now 128x128 pixels - new alpha blend enable bit
v2: rebase
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/cik_reg.h | 65 +++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/radeon.h | 7 +++ drivers/gpu/drm/radeon/radeon_cursor.c | 10 ++--- drivers/gpu/drm/radeon/radeon_display.c | 16 +++++++- drivers/gpu/drm/radeon/radeon_mode.h | 2 + drivers/gpu/drm/radeon/radeon_reg.h | 1 + 6 files changed, 94 insertions(+), 7 deletions(-) create mode 100644 drivers/gpu/drm/radeon/cik_reg.h
diff --git a/drivers/gpu/drm/radeon/cik_reg.h b/drivers/gpu/drm/radeon/cik_reg.h new file mode 100644 index 0000000..b96dac0 --- /dev/null +++ b/drivers/gpu/drm/radeon/cik_reg.h @@ -0,0 +1,65 @@ +/* + * Copyright 2012 Advanced Micro Devices, 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: Alex Deucher + */ +#ifndef __CIK_REG_H__ +#define __CIK_REG_H__ + +#define CIK_DC_GPIO_HPD_MASK 0x65b0 +#define CIK_DC_GPIO_HPD_A 0x65b4 +#define CIK_DC_GPIO_HPD_EN 0x65b8 +#define CIK_DC_GPIO_HPD_Y 0x65bc + +/* CUR blocks at 0x6998, 0x7598, 0x10198, 0x10d98, 0x11998, 0x12598 */ +#define CIK_CUR_CONTROL 0x6998 +# define CIK_CURSOR_EN (1 << 0) +# define CIK_CURSOR_MODE(x) (((x) & 0x3) << 8) +# define CIK_CURSOR_MONO 0 +# define CIK_CURSOR_24_1 1 +# define CIK_CURSOR_24_8_PRE_MULT 2 +# define CIK_CURSOR_24_8_UNPRE_MULT 3 +# define CIK_CURSOR_2X_MAGNIFY (1 << 16) +# define CIK_CURSOR_FORCE_MC_ON (1 << 20) +# define CIK_CURSOR_URGENT_CONTROL(x) (((x) & 0x7) << 24) +# define CIK_CURSOR_URGENT_ALWAYS 0 +# define CIK_CURSOR_URGENT_1_8 1 +# define CIK_CURSOR_URGENT_1_4 2 +# define CIK_CURSOR_URGENT_3_8 3 +# define CIK_CURSOR_URGENT_1_2 4 +#define CIK_CUR_SURFACE_ADDRESS 0x699c +# define CIK_CUR_SURFACE_ADDRESS_MASK 0xfffff000 +#define CIK_CUR_SIZE 0x69a0 +#define CIK_CUR_SURFACE_ADDRESS_HIGH 0x69a4 +#define CIK_CUR_POSITION 0x69a8 +#define CIK_CUR_HOT_SPOT 0x69ac +#define CIK_CUR_COLOR1 0x69b0 +#define CIK_CUR_COLOR2 0x69b4 +#define CIK_CUR_UPDATE 0x69b8 +# define CIK_CURSOR_UPDATE_PENDING (1 << 0) +# define CIK_CURSOR_UPDATE_TAKEN (1 << 1) +# define CIK_CURSOR_UPDATE_LOCK (1 << 16) +# define CIK_CURSOR_DISABLE_MULTIPLE_UPDATE (1 << 24) + +#define CIK_ALPHA_CONTROL 0x6af0 +# define CIK_CURSOR_ALPHA_BLND_ENA (1 << 1) + +#endif diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 04e8dbd..b329e99 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -150,6 +150,13 @@ extern int radeon_fastfb; #define RADEON_RESET_MC (1 << 10) #define RADEON_RESET_DISPLAY (1 << 11)
+/* max cursor sizes (in pixels) */ +#define CURSOR_WIDTH 64 +#define CURSOR_HEIGHT 64 + +#define CIK_CURSOR_WIDTH 128 +#define CIK_CURSOR_HEIGHT 128 + /* * Errata workarounds. */ diff --git a/drivers/gpu/drm/radeon/radeon_cursor.c b/drivers/gpu/drm/radeon/radeon_cursor.c index b097d5b..9630e8d 100644 --- a/drivers/gpu/drm/radeon/radeon_cursor.c +++ b/drivers/gpu/drm/radeon/radeon_cursor.c @@ -27,9 +27,6 @@ #include <drm/radeon_drm.h> #include "radeon.h"
-#define CURSOR_WIDTH 64 -#define CURSOR_HEIGHT 64 - static void radeon_lock_cursor(struct drm_crtc *crtc, bool lock) { struct radeon_device *rdev = crtc->dev->dev_private; @@ -167,7 +164,8 @@ int radeon_crtc_cursor_set(struct drm_crtc *crtc, goto unpin; }
- if ((width > CURSOR_WIDTH) || (height > CURSOR_HEIGHT)) { + if ((width > radeon_crtc->max_cursor_width) || + (height > radeon_crtc->max_cursor_height)) { DRM_ERROR("bad cursor width or height %d x %d\n", width, height); return -EINVAL; } @@ -233,11 +231,11 @@ int radeon_crtc_cursor_move(struct drm_crtc *crtc, DRM_DEBUG("x %d y %d c->x %d c->y %d\n", x, y, crtc->x, crtc->y);
if (x < 0) { - xorigin = min(-x, CURSOR_WIDTH - 1); + xorigin = min(-x, radeon_crtc->max_cursor_width - 1); x = 0; } if (y < 0) { - yorigin = min(-y, CURSOR_HEIGHT - 1); + yorigin = min(-y, radeon_crtc->max_cursor_height - 1); y = 0; }
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index eb18bb7..1f850eb 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -153,7 +153,13 @@ static void dce5_crtc_load_lut(struct drm_crtc *crtc) NI_OUTPUT_CSC_OVL_MODE(NI_OUTPUT_CSC_BYPASS))); /* XXX match this to the depth of the crtc fmt block, move to modeset? */ WREG32(0x6940 + radeon_crtc->crtc_offset, 0); - + if (ASIC_IS_DCE8(rdev)) { + /* XXX this only needs to be programmed once per crtc at startup, + * not sure where the best place for it is + */ + WREG32(CIK_ALPHA_CONTROL + radeon_crtc->crtc_offset, + CIK_CURSOR_ALPHA_BLND_ENA); + } }
static void legacy_crtc_load_lut(struct drm_crtc *crtc) @@ -512,6 +518,14 @@ static void radeon_crtc_init(struct drm_device *dev, int index) radeon_crtc->crtc_id = index; rdev->mode_info.crtcs[index] = radeon_crtc;
+ if (rdev->family >= CHIP_BONAIRE) { + radeon_crtc->max_cursor_width = CIK_CURSOR_WIDTH; + radeon_crtc->max_cursor_height = CIK_CURSOR_HEIGHT; + } else { + radeon_crtc->max_cursor_width = CURSOR_WIDTH; + radeon_crtc->max_cursor_height = CURSOR_HEIGHT; + } + #if 0 radeon_crtc->mode_set.crtc = &radeon_crtc->base; radeon_crtc->mode_set.connectors = (struct drm_connector **)(radeon_crtc + 1); diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index 69ad4fe..4ed0a4c 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -307,6 +307,8 @@ struct radeon_crtc { uint64_t cursor_addr; int cursor_width; int cursor_height; + int max_cursor_width; + int max_cursor_height; uint32_t legacy_display_base_addr; uint32_t legacy_cursor_offset; enum radeon_rmx_type rmx_type; diff --git a/drivers/gpu/drm/radeon/radeon_reg.h b/drivers/gpu/drm/radeon/radeon_reg.h index 7e2c2b7..62d5497 100644 --- a/drivers/gpu/drm/radeon/radeon_reg.h +++ b/drivers/gpu/drm/radeon/radeon_reg.h @@ -57,6 +57,7 @@ #include "evergreen_reg.h" #include "ni_reg.h" #include "si_reg.h" +#include "cik_reg.h"
#define RADEON_MC_AGP_LOCATION 0x014c #define RADEON_MC_AGP_START_MASK 0x0000FFFF
From: Alex Deucher alexander.deucher@amd.com
The register bits changed on DCE8 compared to previous families.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/atombios_encoders.c | 8 +++++++- drivers/gpu/drm/radeon/cik_reg.h | 3 +++ 2 files changed, 10 insertions(+), 1 deletions(-)
diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c index 4120d35..4439419 100644 --- a/drivers/gpu/drm/radeon/atombios_encoders.c +++ b/drivers/gpu/drm/radeon/atombios_encoders.c @@ -1962,7 +1962,13 @@ atombios_apply_encoder_quirks(struct drm_encoder *encoder, /* set scaler clears this on some chips */ if (ASIC_IS_AVIVO(rdev) && (!(radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)))) { - if (ASIC_IS_DCE4(rdev)) { + if (ASIC_IS_DCE8(rdev)) { + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + WREG32(CIK_LB_DATA_FORMAT + radeon_crtc->crtc_offset, + CIK_INTERLEAVE_EN); + else + WREG32(CIK_LB_DATA_FORMAT + radeon_crtc->crtc_offset, 0); + } else if (ASIC_IS_DCE4(rdev)) { if (mode->flags & DRM_MODE_FLAG_INTERLACE) WREG32(EVERGREEN_DATA_FORMAT + radeon_crtc->crtc_offset, EVERGREEN_INTERLEAVE_EN); diff --git a/drivers/gpu/drm/radeon/cik_reg.h b/drivers/gpu/drm/radeon/cik_reg.h index b96dac0..58b29b5 100644 --- a/drivers/gpu/drm/radeon/cik_reg.h +++ b/drivers/gpu/drm/radeon/cik_reg.h @@ -62,4 +62,7 @@ #define CIK_ALPHA_CONTROL 0x6af0 # define CIK_CURSOR_ALPHA_BLND_ENA (1 << 1)
+#define CIK_LB_DATA_FORMAT 0x6b00 +# define CIK_INTERLEAVE_EN (1 << 3) + #endif
From: Alex Deucher alexander.deucher@amd.com
Some new fields and DESKTOP_HEIGHT register moved.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/atombios_crtc.c | 34 ++++++++++++-- drivers/gpu/drm/radeon/cik_reg.h | 79 ++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index d5df8fd..4ba5184 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -1143,7 +1143,9 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc, }
if (tiling_flags & RADEON_TILING_MACRO) { - if (rdev->family >= CHIP_TAHITI) + if (rdev->family >= CHIP_BONAIRE) + tmp = rdev->config.cik.tile_config; + else if (rdev->family >= CHIP_TAHITI) tmp = rdev->config.si.tile_config; else if (rdev->family >= CHIP_CAYMAN) tmp = rdev->config.cayman.tile_config; @@ -1170,11 +1172,29 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc, fb_format |= EVERGREEN_GRPH_BANK_WIDTH(bankw); fb_format |= EVERGREEN_GRPH_BANK_HEIGHT(bankh); fb_format |= EVERGREEN_GRPH_MACRO_TILE_ASPECT(mtaspect); + if (rdev->family >= CHIP_BONAIRE) { + /* XXX need to know more about the surface tiling mode */ + fb_format |= CIK_GRPH_MICRO_TILE_MODE(CIK_DISPLAY_MICRO_TILING); + } } else if (tiling_flags & RADEON_TILING_MICRO) fb_format |= EVERGREEN_GRPH_ARRAY_MODE(EVERGREEN_GRPH_ARRAY_1D_TILED_THIN1);
- if ((rdev->family == CHIP_TAHITI) || - (rdev->family == CHIP_PITCAIRN)) + if (rdev->family >= CHIP_BONAIRE) { + u32 num_pipe_configs = rdev->config.cik.max_tile_pipes; + u32 num_rb = rdev->config.cik.max_backends_per_se; + if (num_pipe_configs > 8) + num_pipe_configs = 8; + if (num_pipe_configs == 8) + fb_format |= CIK_GRPH_PIPE_CONFIG(CIK_ADDR_SURF_P8_32x32_16x16); + else if (num_pipe_configs == 4) { + if (num_rb == 4) + fb_format |= CIK_GRPH_PIPE_CONFIG(CIK_ADDR_SURF_P4_16x16); + else if (num_rb < 4) + fb_format |= CIK_GRPH_PIPE_CONFIG(CIK_ADDR_SURF_P4_8x16); + } else if (num_pipe_configs == 2) + fb_format |= CIK_GRPH_PIPE_CONFIG(CIK_ADDR_SURF_P2); + } else if ((rdev->family == CHIP_TAHITI) || + (rdev->family == CHIP_PITCAIRN)) fb_format |= SI_GRPH_PIPE_CONFIG(SI_ADDR_SURF_P8_32x32_8x16); else if (rdev->family == CHIP_VERDE) fb_format |= SI_GRPH_PIPE_CONFIG(SI_ADDR_SURF_P4_8x16); @@ -1224,8 +1244,12 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc, WREG32(EVERGREEN_GRPH_PITCH + radeon_crtc->crtc_offset, fb_pitch_pixels); WREG32(EVERGREEN_GRPH_ENABLE + radeon_crtc->crtc_offset, 1);
- WREG32(EVERGREEN_DESKTOP_HEIGHT + radeon_crtc->crtc_offset, - target_fb->height); + if (rdev->family >= CHIP_BONAIRE) + WREG32(CIK_LB_DESKTOP_HEIGHT + radeon_crtc->crtc_offset, + target_fb->height); + else + WREG32(EVERGREEN_DESKTOP_HEIGHT + radeon_crtc->crtc_offset, + target_fb->height); x &= ~3; y &= ~1; WREG32(EVERGREEN_VIEWPORT_START + radeon_crtc->crtc_offset, diff --git a/drivers/gpu/drm/radeon/cik_reg.h b/drivers/gpu/drm/radeon/cik_reg.h index 58b29b5..d71e46d 100644 --- a/drivers/gpu/drm/radeon/cik_reg.h +++ b/drivers/gpu/drm/radeon/cik_reg.h @@ -29,6 +29,83 @@ #define CIK_DC_GPIO_HPD_EN 0x65b8 #define CIK_DC_GPIO_HPD_Y 0x65bc
+#define CIK_GRPH_CONTROL 0x6804 +# define CIK_GRPH_DEPTH(x) (((x) & 0x3) << 0) +# define CIK_GRPH_DEPTH_8BPP 0 +# define CIK_GRPH_DEPTH_16BPP 1 +# define CIK_GRPH_DEPTH_32BPP 2 +# define CIK_GRPH_NUM_BANKS(x) (((x) & 0x3) << 2) +# define CIK_ADDR_SURF_2_BANK 0 +# define CIK_ADDR_SURF_4_BANK 1 +# define CIK_ADDR_SURF_8_BANK 2 +# define CIK_ADDR_SURF_16_BANK 3 +# define CIK_GRPH_Z(x) (((x) & 0x3) << 4) +# define CIK_GRPH_BANK_WIDTH(x) (((x) & 0x3) << 6) +# define CIK_ADDR_SURF_BANK_WIDTH_1 0 +# define CIK_ADDR_SURF_BANK_WIDTH_2 1 +# define CIK_ADDR_SURF_BANK_WIDTH_4 2 +# define CIK_ADDR_SURF_BANK_WIDTH_8 3 +# define CIK_GRPH_FORMAT(x) (((x) & 0x7) << 8) +/* 8 BPP */ +# define CIK_GRPH_FORMAT_INDEXED 0 +/* 16 BPP */ +# define CIK_GRPH_FORMAT_ARGB1555 0 +# define CIK_GRPH_FORMAT_ARGB565 1 +# define CIK_GRPH_FORMAT_ARGB4444 2 +# define CIK_GRPH_FORMAT_AI88 3 +# define CIK_GRPH_FORMAT_MONO16 4 +# define CIK_GRPH_FORMAT_BGRA5551 5 +/* 32 BPP */ +# define CIK_GRPH_FORMAT_ARGB8888 0 +# define CIK_GRPH_FORMAT_ARGB2101010 1 +# define CIK_GRPH_FORMAT_32BPP_DIG 2 +# define CIK_GRPH_FORMAT_8B_ARGB2101010 3 +# define CIK_GRPH_FORMAT_BGRA1010102 4 +# define CIK_GRPH_FORMAT_8B_BGRA1010102 5 +# define CIK_GRPH_FORMAT_RGB111110 6 +# define CIK_GRPH_FORMAT_BGR101111 7 +# define CIK_GRPH_BANK_HEIGHT(x) (((x) & 0x3) << 11) +# define CIK_ADDR_SURF_BANK_HEIGHT_1 0 +# define CIK_ADDR_SURF_BANK_HEIGHT_2 1 +# define CIK_ADDR_SURF_BANK_HEIGHT_4 2 +# define CIK_ADDR_SURF_BANK_HEIGHT_8 3 +# define CIK_GRPH_TILE_SPLIT(x) (((x) & 0x7) << 13) +# define CIK_ADDR_SURF_TILE_SPLIT_64B 0 +# define CIK_ADDR_SURF_TILE_SPLIT_128B 1 +# define CIK_ADDR_SURF_TILE_SPLIT_256B 2 +# define CIK_ADDR_SURF_TILE_SPLIT_512B 3 +# define CIK_ADDR_SURF_TILE_SPLIT_1KB 4 +# define CIK_ADDR_SURF_TILE_SPLIT_2KB 5 +# define CIK_ADDR_SURF_TILE_SPLIT_4KB 6 +# define CIK_GRPH_MACRO_TILE_ASPECT(x) (((x) & 0x3) << 18) +# define CIK_ADDR_SURF_MACRO_TILE_ASPECT_1 0 +# define CIK_ADDR_SURF_MACRO_TILE_ASPECT_2 1 +# define CIK_ADDR_SURF_MACRO_TILE_ASPECT_4 2 +# define CIK_ADDR_SURF_MACRO_TILE_ASPECT_8 3 +# define CIK_GRPH_ARRAY_MODE(x) (((x) & 0x7) << 20) +# define CIK_GRPH_ARRAY_LINEAR_GENERAL 0 +# define CIK_GRPH_ARRAY_LINEAR_ALIGNED 1 +# define CIK_GRPH_ARRAY_1D_TILED_THIN1 2 +# define CIK_GRPH_ARRAY_2D_TILED_THIN1 4 +# define CIK_GRPH_PIPE_CONFIG(x) (((x) & 0x1f) << 24) +# define CIK_ADDR_SURF_P2 0 +# define CIK_ADDR_SURF_P4_8x16 4 +# define CIK_ADDR_SURF_P4_16x16 5 +# define CIK_ADDR_SURF_P4_16x32 6 +# define CIK_ADDR_SURF_P4_32x32 7 +# define CIK_ADDR_SURF_P8_16x16_8x16 8 +# define CIK_ADDR_SURF_P8_16x32_8x16 9 +# define CIK_ADDR_SURF_P8_32x32_8x16 10 +# define CIK_ADDR_SURF_P8_16x32_16x16 11 +# define CIK_ADDR_SURF_P8_32x32_16x16 12 +# define CIK_ADDR_SURF_P8_32x32_16x32 13 +# define CIK_ADDR_SURF_P8_32x64_32x32 14 +# define CIK_GRPH_MICRO_TILE_MODE(x) (((x) & 0x7) << 29) +# define CIK_DISPLAY_MICRO_TILING 0 +# define CIK_THIN_MICRO_TILING 1 +# define CIK_DEPTH_MICRO_TILING 2 +# define CIK_ROTATED_MICRO_TILING 4 + /* CUR blocks at 0x6998, 0x7598, 0x10198, 0x10d98, 0x11998, 0x12598 */ #define CIK_CUR_CONTROL 0x6998 # define CIK_CURSOR_EN (1 << 0) @@ -65,4 +142,6 @@ #define CIK_LB_DATA_FORMAT 0x6b00 # define CIK_INTERLEAVE_EN (1 << 3)
+#define CIK_LB_DESKTOP_HEIGHT 0x6b0c + #endif
From: Alex Deucher alexander.deucher@amd.com
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/atombios_encoders.c | 27 ++++++++++++++++++++++++--- drivers/gpu/drm/radeon/radeon_display.c | 5 +++-- 2 files changed, 27 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c index 4439419..1bf13b3 100644 --- a/drivers/gpu/drm/radeon/atombios_encoders.c +++ b/drivers/gpu/drm/radeon/atombios_encoders.c @@ -303,6 +303,7 @@ static inline bool radeon_encoder_is_digital(struct drm_encoder *encoder) case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3: return true; default: return false; @@ -922,10 +923,14 @@ atombios_dig_encoder_setup(struct drm_encoder *encoder, int action, int panel_mo args.v4.ucLaneNum = 4;
if (ENCODER_MODE_IS_DP(args.v4.ucEncoderMode)) { - if (dp_clock == 270000) - args.v1.ucConfig |= ATOM_ENCODER_CONFIG_V4_DPLINKRATE_2_70GHZ; - else if (dp_clock == 540000) + if (dp_clock == 540000) args.v1.ucConfig |= ATOM_ENCODER_CONFIG_V4_DPLINKRATE_5_40GHZ; + else if (dp_clock == 324000) + args.v1.ucConfig |= ATOM_ENCODER_CONFIG_V4_DPLINKRATE_3_24GHZ; + else if (dp_clock == 270000) + args.v1.ucConfig |= ATOM_ENCODER_CONFIG_V4_DPLINKRATE_2_70GHZ; + else + args.v1.ucConfig |= ATOM_ENCODER_CONFIG_V4_DPLINKRATE_1_62GHZ; } args.v4.acConfig.ucDigSel = dig->dig_encoder; args.v4.ucBitPerColor = radeon_atom_get_bpc(encoder); @@ -1019,6 +1024,7 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3: index = GetIndexIntoMasterTable(COMMAND, UNIPHYTransmitterControl); break; case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: @@ -1278,6 +1284,9 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t else args.v5.ucPhyId = ATOM_PHY_ID_UNIPHYE; break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3: + args.v5.ucPhyId = ATOM_PHY_ID_UNIPHYG; + break; } if (is_dp) args.v5.ucLaneNum = dp_lane_count; @@ -1742,6 +1751,7 @@ radeon_atom_encoder_dpms(struct drm_encoder *encoder, int mode) case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: radeon_atom_encoder_dpms_dig(encoder, mode); break; @@ -1879,6 +1889,7 @@ atombios_set_encoder_crtc_source(struct drm_encoder *encoder) case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: dig = radeon_encoder->enc_priv; switch (dig->dig_encoder) { @@ -1900,6 +1911,9 @@ atombios_set_encoder_crtc_source(struct drm_encoder *encoder) case 5: args.v2.ucEncoderID = ASIC_INT_DIG6_ENCODER_ID; break; + case 6: + args.v2.ucEncoderID = ASIC_INT_DIG7_ENCODER_ID; + break; } break; case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1: @@ -2015,6 +2029,9 @@ static int radeon_atom_pick_dig_encoder(struct drm_encoder *encoder) else return 4; break; + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3: + return 6; + break; } } else if (ASIC_IS_DCE4(rdev)) { /* DCE4/5 */ @@ -2099,6 +2116,7 @@ radeon_atom_encoder_init(struct radeon_device *rdev) case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_INIT, 0, 0); break; @@ -2143,6 +2161,7 @@ radeon_atom_encoder_mode_set(struct drm_encoder *encoder, case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: /* handled in dpms */ break; @@ -2408,6 +2427,7 @@ static void radeon_atom_encoder_disable(struct drm_encoder *encoder) case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: /* handled in dpms */ break; @@ -2639,6 +2659,7 @@ radeon_add_atom_encoder(struct drm_device *dev, case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: + case ENCODER_OBJECT_ID_INTERNAL_UNIPHY3: if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { radeon_encoder->rmx_type = RMX_FULL; drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_LVDS); diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 1f850eb..c2b67b4 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -544,7 +544,7 @@ static void radeon_crtc_init(struct drm_device *dev, int index) radeon_legacy_init_crtc(dev, radeon_crtc); }
-static const char *encoder_names[37] = { +static const char *encoder_names[38] = { "NONE", "INTERNAL_LVDS", "INTERNAL_TMDS1", @@ -581,7 +581,8 @@ static const char *encoder_names[37] = { "INTERNAL_UNIPHY2", "NUTMEG", "TRAVIS", - "INTERNAL_VCE" + "INTERNAL_VCE", + "INTERNAL_UNIPHY3", };
static const char *hpd_names[6] = {
From: Alex Deucher alexander.deucher@amd.com
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/atombios_encoders.c | 9 ++++++++- 1 files changed, 8 insertions(+), 1 deletions(-)
diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c index 1bf13b3..092275d 100644 --- a/drivers/gpu/drm/radeon/atombios_encoders.c +++ b/drivers/gpu/drm/radeon/atombios_encoders.c @@ -487,11 +487,11 @@ static u8 radeon_atom_get_bpc(struct drm_encoder *encoder) } }
- union dvo_encoder_control { ENABLE_EXTERNAL_TMDS_ENCODER_PS_ALLOCATION ext_tmds; DVO_ENCODER_CONTROL_PS_ALLOCATION dvo; DVO_ENCODER_CONTROL_PS_ALLOCATION_V3 dvo_v3; + DVO_ENCODER_CONTROL_PS_ALLOCATION_V1_4 dvo_v4; };
void @@ -541,6 +541,13 @@ atombios_dvo_setup(struct drm_encoder *encoder, int action) args.dvo_v3.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); args.dvo_v3.ucDVOConfig = 0; /* XXX */ break; + case 4: + /* DCE8 */ + args.dvo_v4.ucAction = action; + args.dvo_v4.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10); + args.dvo_v4.ucDVOConfig = 0; /* XXX */ + args.dvo_v4.ucBitPerColor = radeon_atom_get_bpc(encoder); + break; default: DRM_ERROR("Unknown table version %d, %d\n", frev, crev); break;
From: Alex Deucher alexander.deucher@amd.com
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/atombios_crtc.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index 4ba5184..586c452 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -743,7 +743,7 @@ static void atombios_crtc_set_disp_eng_pll(struct radeon_device *rdev, * SetPixelClock provides the dividers */ args.v6.ulDispEngClkFreq = cpu_to_le32(dispclk); - if (ASIC_IS_DCE61(rdev)) + if (ASIC_IS_DCE61(rdev) || ASIC_IS_DCE8(rdev)) args.v6.ucPpll = ATOM_EXT_PLL1; else if (ASIC_IS_DCE6(rdev)) args.v6.ucPpll = ATOM_PPLL0;
From: Alex Deucher alexander.deucher@amd.com
v2: make PPLL0 is available for non-DP on CI v3: rebase changes, update documentation v4: fix kabini
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/atombios_crtc.c | 48 +++++++++++++++++++++++++++++++- 1 files changed, 47 insertions(+), 1 deletions(-)
diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index 586c452..590e4eb 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -1621,6 +1621,12 @@ static int radeon_get_shared_nondp_ppll(struct drm_crtc *crtc) * * Asic specific PLL information * + * DCE 8.x + * KB/KV + * - PPLL1, PPLL2 are available for all UNIPHY (both DP and non-DP) + * CI + * - PPLL0, PPLL1, PPLL2 are available for all UNIPHY (both DP and non-DP) and DAC + * * DCE 6.1 * - PPLL2 is only available to UNIPHYA (both DP and non-DP) * - PPLL0, PPLL1 are available for UNIPHYB/C/D/E/F (both DP and non-DP) @@ -1647,7 +1653,47 @@ static int radeon_atom_pick_pll(struct drm_crtc *crtc) u32 pll_in_use; int pll;
- if (ASIC_IS_DCE61(rdev)) { + if (ASIC_IS_DCE8(rdev)) { + if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(radeon_crtc->encoder))) { + if (rdev->clock.dp_extclk) + /* skip PPLL programming if using ext clock */ + return ATOM_PPLL_INVALID; + else { + /* use the same PPLL for all DP monitors */ + pll = radeon_get_shared_dp_ppll(crtc); + if (pll != ATOM_PPLL_INVALID) + return pll; + } + } else { + /* use the same PPLL for all monitors with the same clock */ + pll = radeon_get_shared_nondp_ppll(crtc); + if (pll != ATOM_PPLL_INVALID) + return pll; + } + /* otherwise, pick one of the plls */ + if ((rdev->family == CHIP_KAVERI) || + (rdev->family == CHIP_KABINI)) { + /* KB/KV has PPLL1 and PPLL2 */ + pll_in_use = radeon_get_pll_use_mask(crtc); + if (!(pll_in_use & (1 << ATOM_PPLL2))) + return ATOM_PPLL2; + if (!(pll_in_use & (1 << ATOM_PPLL1))) + return ATOM_PPLL1; + DRM_ERROR("unable to allocate a PPLL\n"); + return ATOM_PPLL_INVALID; + } else { + /* CI has PPLL0, PPLL1, and PPLL2 */ + pll_in_use = radeon_get_pll_use_mask(crtc); + if (!(pll_in_use & (1 << ATOM_PPLL2))) + return ATOM_PPLL2; + if (!(pll_in_use & (1 << ATOM_PPLL1))) + return ATOM_PPLL1; + if (!(pll_in_use & (1 << ATOM_PPLL0))) + return ATOM_PPLL0; + DRM_ERROR("unable to allocate a PPLL\n"); + return ATOM_PPLL_INVALID; + } + } else if (ASIC_IS_DCE61(rdev)) { struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
From: Alex Deucher alexander.deucher@amd.com
Only Bonaire has PPLL0.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/atombios_crtc.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index 590e4eb..24eee7c 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -1931,7 +1931,7 @@ static void atombios_crtc_disable(struct drm_crtc *crtc) break; case ATOM_PPLL0: /* disable the ppll */ - if (ASIC_IS_DCE61(rdev)) + if ((rdev->family == CHIP_ARUBA) || (rdev->family == CHIP_BONAIRE)) atombios_crtc_program_pll(crtc, radeon_crtc->crtc_id, radeon_crtc->pll_id, 0, 0, ATOM_DISABLE, 0, 0, 0, 0, 0, false, &ss); break;
From: Alex Deucher alexander.deucher@amd.com
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/atombios_crtc.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index 24eee7c..c7ad4b9 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -555,7 +555,7 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc, if (rdev->family < CHIP_RV770) radeon_crtc->pll_flags |= RADEON_PLL_PREFER_MINM_OVER_MAXP; /* use frac fb div on APUs */ - if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE61(rdev)) + if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE61(rdev) || ASIC_IS_DCE8(rdev)) radeon_crtc->pll_flags |= RADEON_PLL_USE_FRAC_FB_DIV; /* use frac fb div on RS780/RS880 */ if ((rdev->family == CHIP_RS780) || (rdev->family == CHIP_RS880))
From: Alex Deucher alexander.deucher@amd.com
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/radeon_atombios.c | 17 +++++++++++++++++ 1 files changed, 17 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index 88a55af..3236755 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -1269,6 +1269,7 @@ union igp_info { struct _ATOM_INTEGRATED_SYSTEM_INFO_V2 info_2; struct _ATOM_INTEGRATED_SYSTEM_INFO_V6 info_6; struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_7 info_7; + struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_8 info_8; };
bool radeon_atombios_sideport_present(struct radeon_device *rdev) @@ -1438,6 +1439,22 @@ static void radeon_atombios_get_igp_ss_overrides(struct radeon_device *rdev, break; } break; + case 8: + switch (id) { + case ASIC_INTERNAL_SS_ON_TMDS: + percentage = le16_to_cpu(igp_info->info_8.usDVISSPercentage); + rate = le16_to_cpu(igp_info->info_8.usDVISSpreadRateIn10Hz); + break; + case ASIC_INTERNAL_SS_ON_HDMI: + percentage = le16_to_cpu(igp_info->info_8.usHDMISSPercentage); + rate = le16_to_cpu(igp_info->info_8.usHDMISSpreadRateIn10Hz); + break; + case ASIC_INTERNAL_SS_ON_LVDS: + percentage = le16_to_cpu(igp_info->info_8.usLvdsSSPercentage); + rate = le16_to_cpu(igp_info->info_8.usLvdsSSpreadRateIn10Hz); + break; + } + break; default: DRM_ERROR("Unsupported IGP table: %d %d\n", frev, crev); break;
From: Alex Deucher alexander.deucher@amd.com
v2: rebase changes, fix a couple missed cases
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/radeon_kms.c | 33 ++++++++++++++++++++++++++------- 1 files changed, 26 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index 4f2d4f4..c650228 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -229,7 +229,9 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) *value = rdev->accel_working; break; case RADEON_INFO_TILING_CONFIG: - if (rdev->family >= CHIP_TAHITI) + if (rdev->family >= CHIP_BONAIRE) + *value = rdev->config.cik.tile_config; + else if (rdev->family >= CHIP_TAHITI) *value = rdev->config.si.tile_config; else if (rdev->family >= CHIP_CAYMAN) *value = rdev->config.cayman.tile_config; @@ -281,7 +283,10 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) *value = rdev->clock.spll.reference_freq * 10; break; case RADEON_INFO_NUM_BACKENDS: - if (rdev->family >= CHIP_TAHITI) + if (rdev->family >= CHIP_BONAIRE) + *value = rdev->config.cik.max_backends_per_se * + rdev->config.cik.max_shader_engines; + else if (rdev->family >= CHIP_TAHITI) *value = rdev->config.si.max_backends_per_se * rdev->config.si.max_shader_engines; else if (rdev->family >= CHIP_CAYMAN) @@ -298,7 +303,9 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) } break; case RADEON_INFO_NUM_TILE_PIPES: - if (rdev->family >= CHIP_TAHITI) + if (rdev->family >= CHIP_BONAIRE) + *value = rdev->config.cik.max_tile_pipes; + else if (rdev->family >= CHIP_TAHITI) *value = rdev->config.si.max_tile_pipes; else if (rdev->family >= CHIP_CAYMAN) *value = rdev->config.cayman.max_tile_pipes; @@ -316,7 +323,9 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) *value = 1; break; case RADEON_INFO_BACKEND_MAP: - if (rdev->family >= CHIP_TAHITI) + if (rdev->family >= CHIP_BONAIRE) + return -EINVAL; + else if (rdev->family >= CHIP_TAHITI) *value = rdev->config.si.backend_map; else if (rdev->family >= CHIP_CAYMAN) *value = rdev->config.cayman.backend_map; @@ -343,7 +352,9 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) *value = RADEON_IB_VM_MAX_SIZE; break; case RADEON_INFO_MAX_PIPES: - if (rdev->family >= CHIP_TAHITI) + if (rdev->family >= CHIP_BONAIRE) + *value = rdev->config.cik.max_cu_per_sh; + else if (rdev->family >= CHIP_TAHITI) *value = rdev->config.si.max_cu_per_sh; else if (rdev->family >= CHIP_CAYMAN) *value = rdev->config.cayman.max_pipes_per_simd; @@ -367,7 +378,9 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) value64 = radeon_get_gpu_clock_counter(rdev); break; case RADEON_INFO_MAX_SE: - if (rdev->family >= CHIP_TAHITI) + if (rdev->family >= CHIP_BONAIRE) + *value = rdev->config.cik.max_shader_engines; + else if (rdev->family >= CHIP_TAHITI) *value = rdev->config.si.max_shader_engines; else if (rdev->family >= CHIP_CAYMAN) *value = rdev->config.cayman.max_shader_engines; @@ -377,7 +390,9 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) *value = 1; break; case RADEON_INFO_MAX_SH_PER_SE: - if (rdev->family >= CHIP_TAHITI) + if (rdev->family >= CHIP_BONAIRE) + *value = rdev->config.cik.max_sh_per_se; + else if (rdev->family >= CHIP_TAHITI) *value = rdev->config.si.max_sh_per_se; else return -EINVAL; @@ -407,6 +422,10 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) } break; case RADEON_INFO_SI_TILE_MODE_ARRAY: + if (rdev->family >= CHIP_BONAIRE) { + DRM_DEBUG_KMS("tile mode array is not implemented yet\n"); + return -EINVAL; + } if (rdev->family < CHIP_TAHITI) { DRM_DEBUG_KMS("tile mode array is si only!\n"); return -EINVAL;
From: Alex Deucher alexander.deucher@amd.com
Used for GPU clock counter snapshots.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/cik.c | 21 +++++++++++++++++++++ drivers/gpu/drm/radeon/cikd.h | 4 +++- drivers/gpu/drm/radeon/radeon_asic.h | 5 +++++ 3 files changed, 29 insertions(+), 1 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 228d399..d241cfd 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -5558,3 +5558,24 @@ void dce8_bandwidth_update(struct radeon_device *rdev) dce8_program_watermarks(rdev, rdev->mode_info.crtcs[i], lb_size, num_heads); } } + +/** + * cik_get_gpu_clock_counter - return GPU clock counter snapshot + * + * @rdev: radeon_device pointer + * + * Fetches a GPU clock counter snapshot (SI). + * Returns the 64 bit clock counter snapshot. + */ +uint64_t cik_get_gpu_clock_counter(struct radeon_device *rdev) +{ + uint64_t clock; + + mutex_lock(&rdev->gpu_clock_mutex); + WREG32(RLC_CAPTURE_GPU_CLOCK_COUNT, 1); + clock = (uint64_t)RREG32(RLC_GPU_CLOCK_COUNT_LSB) | + ((uint64_t)RREG32(RLC_GPU_CLOCK_COUNT_MSB) << 32ULL); + mutex_unlock(&rdev->gpu_clock_mutex); + return clock; +} + diff --git a/drivers/gpu/drm/radeon/cikd.h b/drivers/gpu/drm/radeon/cikd.h index 3349e37..daa51ac 100644 --- a/drivers/gpu/drm/radeon/cikd.h +++ b/drivers/gpu/drm/radeon/cikd.h @@ -730,7 +730,9 @@
#define RLC_GPM_UCODE_ADDR 0xC388 #define RLC_GPM_UCODE_DATA 0xC38C - +#define RLC_GPU_CLOCK_COUNT_LSB 0xC390 +#define RLC_GPU_CLOCK_COUNT_MSB 0xC394 +#define RLC_CAPTURE_GPU_CLOCK_COUNT 0xC398 #define RLC_UCODE_CNTL 0xC39C
#define RLC_CGCG_CGLS_CTRL 0xC424 diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index a72759e..248da72 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -553,4 +553,9 @@ u32 si_get_xclk(struct radeon_device *rdev); uint64_t si_get_gpu_clock_counter(struct radeon_device *rdev); int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk);
+/* + * cik + */ +uint64_t cik_get_gpu_clock_counter(struct radeon_device *rdev); + #endif
From: Alex Deucher alexander.deucher@amd.com
Update to the newer programming model.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/cik.c | 387 +++++++++++++++++++++++++---------------- drivers/gpu/drm/radeon/cikd.h | 12 ++ 2 files changed, 253 insertions(+), 146 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index d241cfd..aaf7bba 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -72,9 +72,11 @@ extern int r600_ih_ring_alloc(struct radeon_device *rdev); extern void r600_ih_ring_fini(struct radeon_device *rdev); extern void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_save *save); extern void evergreen_mc_resume(struct radeon_device *rdev, struct evergreen_mc_save *save); +extern bool evergreen_is_display_hung(struct radeon_device *rdev); extern void si_vram_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc); extern void si_rlc_fini(struct radeon_device *rdev); extern int si_rlc_init(struct radeon_device *rdev); +static void cik_rlc_stop(struct radeon_device *rdev);
#define BONAIRE_IO_MC_REGS_SIZE 36
@@ -2733,56 +2735,9 @@ int cik_sdma_ib_test(struct radeon_device *rdev, struct radeon_ring *ring) return r; }
-/** - * cik_gpu_is_lockup - check if the 3D engine is locked up - * - * @rdev: radeon_device pointer - * @ring: radeon_ring structure holding ring information - * - * Check if the 3D engine is locked up (CIK). - * Returns true if the engine is locked, false if not. - */ -bool cik_gpu_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring) -{ - u32 srbm_status, srbm_status2; - u32 grbm_status, grbm_status2; - u32 grbm_status_se0, grbm_status_se1, grbm_status_se2, grbm_status_se3; - - srbm_status = RREG32(SRBM_STATUS); - srbm_status2 = RREG32(SRBM_STATUS2); - grbm_status = RREG32(GRBM_STATUS); - grbm_status2 = RREG32(GRBM_STATUS2); - grbm_status_se0 = RREG32(GRBM_STATUS_SE0); - grbm_status_se1 = RREG32(GRBM_STATUS_SE1); - grbm_status_se2 = RREG32(GRBM_STATUS_SE2); - grbm_status_se3 = RREG32(GRBM_STATUS_SE3); - if (!(grbm_status & GUI_ACTIVE)) { - radeon_ring_lockup_update(ring); - return false; - } - /* force CP activities */ - radeon_ring_force_activity(rdev, ring); - return radeon_ring_test_lockup(rdev, ring); -}
-/** - * cik_gfx_gpu_soft_reset - soft reset the 3D engine and CPG - * - * @rdev: radeon_device pointer - * - * Soft reset the GFX engine and CPG blocks (CIK). - * XXX: deal with reseting RLC and CPF - * Returns 0 for success. - */ -static int cik_gfx_gpu_soft_reset(struct radeon_device *rdev) +static void cik_print_gpu_status_regs(struct radeon_device *rdev) { - struct evergreen_mc_save save; - u32 grbm_reset = 0; - - if (!(RREG32(GRBM_STATUS) & GUI_ACTIVE)) - return 0; - - dev_info(rdev->dev, "GPU GFX softreset \n"); dev_info(rdev->dev, " GRBM_STATUS=0x%08X\n", RREG32(GRBM_STATUS)); dev_info(rdev->dev, " GRBM_STATUS2=0x%08X\n", @@ -2799,132 +2754,270 @@ static int cik_gfx_gpu_soft_reset(struct radeon_device *rdev) RREG32(SRBM_STATUS)); dev_info(rdev->dev, " SRBM_STATUS2=0x%08X\n", RREG32(SRBM_STATUS2)); - evergreen_mc_stop(rdev, &save); - if (radeon_mc_wait_for_idle(rdev)) { - dev_warn(rdev->dev, "Wait for MC idle timedout !\n"); - } - /* Disable CP parsing/prefetching */ - WREG32(CP_ME_CNTL, CP_ME_HALT | CP_PFP_HALT | CP_CE_HALT); + dev_info(rdev->dev, " SDMA0_STATUS_REG = 0x%08X\n", + RREG32(SDMA0_STATUS_REG + SDMA0_REGISTER_OFFSET)); + dev_info(rdev->dev, " SDMA1_STATUS_REG = 0x%08X\n", + RREG32(SDMA0_STATUS_REG + SDMA1_REGISTER_OFFSET)); +}
- /* reset all the gfx block and all CPG blocks */ - grbm_reset = SOFT_RESET_CPG | SOFT_RESET_GFX; +/** + * cik_gpu_check_soft_reset - check which blocks are busy + * + * @rdev: radeon_device pointer + * + * Check which blocks are busy and return the relevant reset + * mask to be used by cik_gpu_soft_reset(). + * Returns a mask of the blocks to be reset. + */ +static u32 cik_gpu_check_soft_reset(struct radeon_device *rdev) +{ + u32 reset_mask = 0; + u32 tmp;
- dev_info(rdev->dev, " GRBM_SOFT_RESET=0x%08X\n", grbm_reset); - WREG32(GRBM_SOFT_RESET, grbm_reset); - (void)RREG32(GRBM_SOFT_RESET); - udelay(50); - WREG32(GRBM_SOFT_RESET, 0); - (void)RREG32(GRBM_SOFT_RESET); - /* Wait a little for things to settle down */ - udelay(50); - dev_info(rdev->dev, " GRBM_STATUS=0x%08X\n", - RREG32(GRBM_STATUS)); - dev_info(rdev->dev, " GRBM_STATUS2=0x%08X\n", - RREG32(GRBM_STATUS2)); - dev_info(rdev->dev, " GRBM_STATUS_SE0=0x%08X\n", - RREG32(GRBM_STATUS_SE0)); - dev_info(rdev->dev, " GRBM_STATUS_SE1=0x%08X\n", - RREG32(GRBM_STATUS_SE1)); - dev_info(rdev->dev, " GRBM_STATUS_SE2=0x%08X\n", - RREG32(GRBM_STATUS_SE2)); - dev_info(rdev->dev, " GRBM_STATUS_SE3=0x%08X\n", - RREG32(GRBM_STATUS_SE3)); - dev_info(rdev->dev, " SRBM_STATUS=0x%08X\n", - RREG32(SRBM_STATUS)); - dev_info(rdev->dev, " SRBM_STATUS2=0x%08X\n", - RREG32(SRBM_STATUS2)); - evergreen_mc_resume(rdev, &save); - return 0; + /* GRBM_STATUS */ + tmp = RREG32(GRBM_STATUS); + if (tmp & (PA_BUSY | SC_BUSY | + BCI_BUSY | SX_BUSY | + TA_BUSY | VGT_BUSY | + DB_BUSY | CB_BUSY | + GDS_BUSY | SPI_BUSY | + IA_BUSY | IA_BUSY_NO_DMA)) + reset_mask |= RADEON_RESET_GFX; + + if (tmp & (CP_BUSY | CP_COHERENCY_BUSY)) + reset_mask |= RADEON_RESET_CP; + + /* GRBM_STATUS2 */ + tmp = RREG32(GRBM_STATUS2); + if (tmp & RLC_BUSY) + reset_mask |= RADEON_RESET_RLC; + + /* SDMA0_STATUS_REG */ + tmp = RREG32(SDMA0_STATUS_REG + SDMA0_REGISTER_OFFSET); + if (!(tmp & SDMA_IDLE)) + reset_mask |= RADEON_RESET_DMA; + + /* SDMA1_STATUS_REG */ + tmp = RREG32(SDMA0_STATUS_REG + SDMA1_REGISTER_OFFSET); + if (!(tmp & SDMA_IDLE)) + reset_mask |= RADEON_RESET_DMA1; + + /* SRBM_STATUS2 */ + tmp = RREG32(SRBM_STATUS2); + if (tmp & SDMA_BUSY) + reset_mask |= RADEON_RESET_DMA; + + if (tmp & SDMA1_BUSY) + reset_mask |= RADEON_RESET_DMA1; + + /* SRBM_STATUS */ + tmp = RREG32(SRBM_STATUS); + + if (tmp & IH_BUSY) + reset_mask |= RADEON_RESET_IH; + + if (tmp & SEM_BUSY) + reset_mask |= RADEON_RESET_SEM; + + if (tmp & GRBM_RQ_PENDING) + reset_mask |= RADEON_RESET_GRBM; + + if (tmp & VMC_BUSY) + reset_mask |= RADEON_RESET_VMC; + + if (tmp & (MCB_BUSY | MCB_NON_DISPLAY_BUSY | + MCC_BUSY | MCD_BUSY)) + reset_mask |= RADEON_RESET_MC; + + if (evergreen_is_display_hung(rdev)) + reset_mask |= RADEON_RESET_DISPLAY; + + /* Skip MC reset as it's mostly likely not hung, just busy */ + if (reset_mask & RADEON_RESET_MC) { + DRM_DEBUG("MC busy: 0x%08X, clearing.\n", reset_mask); + reset_mask &= ~RADEON_RESET_MC; + } + + return reset_mask; }
/** - * cik_compute_gpu_soft_reset - soft reset CPC + * cik_gpu_soft_reset - soft reset GPU * * @rdev: radeon_device pointer + * @reset_mask: mask of which blocks to reset * - * Soft reset the CPC blocks (CIK). - * XXX: deal with reseting RLC and CPF - * Returns 0 for success. + * Soft reset the blocks specified in @reset_mask. */ -static int cik_compute_gpu_soft_reset(struct radeon_device *rdev) +static void cik_gpu_soft_reset(struct radeon_device *rdev, u32 reset_mask) { struct evergreen_mc_save save; - u32 grbm_reset = 0; + u32 grbm_soft_reset = 0, srbm_soft_reset = 0; + u32 tmp; + + if (reset_mask == 0) + return; + + dev_info(rdev->dev, "GPU softreset: 0x%08X\n", reset_mask); + + cik_print_gpu_status_regs(rdev); + dev_info(rdev->dev, " VM_CONTEXT1_PROTECTION_FAULT_ADDR 0x%08X\n", + RREG32(VM_CONTEXT1_PROTECTION_FAULT_ADDR)); + dev_info(rdev->dev, " VM_CONTEXT1_PROTECTION_FAULT_STATUS 0x%08X\n", + RREG32(VM_CONTEXT1_PROTECTION_FAULT_STATUS)); + + /* stop the rlc */ + cik_rlc_stop(rdev); + + /* Disable GFX parsing/prefetching */ + WREG32(CP_ME_CNTL, CP_ME_HALT | CP_PFP_HALT | CP_CE_HALT); + + /* Disable MEC parsing/prefetching */ + WREG32(CP_MEC_CNTL, MEC_ME1_HALT | MEC_ME2_HALT); + + if (reset_mask & RADEON_RESET_DMA) { + /* sdma0 */ + tmp = RREG32(SDMA0_ME_CNTL + SDMA0_REGISTER_OFFSET); + tmp |= SDMA_HALT; + WREG32(SDMA0_ME_CNTL + SDMA0_REGISTER_OFFSET, tmp); + } + if (reset_mask & RADEON_RESET_DMA1) { + /* sdma1 */ + tmp = RREG32(SDMA0_ME_CNTL + SDMA1_REGISTER_OFFSET); + tmp |= SDMA_HALT; + WREG32(SDMA0_ME_CNTL + SDMA1_REGISTER_OFFSET, tmp); + }
- dev_info(rdev->dev, "GPU compute softreset \n"); - dev_info(rdev->dev, " GRBM_STATUS=0x%08X\n", - RREG32(GRBM_STATUS)); - dev_info(rdev->dev, " GRBM_STATUS2=0x%08X\n", - RREG32(GRBM_STATUS2)); - dev_info(rdev->dev, " GRBM_STATUS_SE0=0x%08X\n", - RREG32(GRBM_STATUS_SE0)); - dev_info(rdev->dev, " GRBM_STATUS_SE1=0x%08X\n", - RREG32(GRBM_STATUS_SE1)); - dev_info(rdev->dev, " GRBM_STATUS_SE2=0x%08X\n", - RREG32(GRBM_STATUS_SE2)); - dev_info(rdev->dev, " GRBM_STATUS_SE3=0x%08X\n", - RREG32(GRBM_STATUS_SE3)); - dev_info(rdev->dev, " SRBM_STATUS=0x%08X\n", - RREG32(SRBM_STATUS)); - dev_info(rdev->dev, " SRBM_STATUS2=0x%08X\n", - RREG32(SRBM_STATUS2)); evergreen_mc_stop(rdev, &save); - if (radeon_mc_wait_for_idle(rdev)) { + if (evergreen_mc_wait_for_idle(rdev)) { dev_warn(rdev->dev, "Wait for MC idle timedout !\n"); } - /* Disable CP parsing/prefetching */ - WREG32(CP_MEC_CNTL, MEC_ME1_HALT | MEC_ME2_HALT);
- /* reset all the CPC blocks */ - grbm_reset = SOFT_RESET_CPG; + if (reset_mask & (RADEON_RESET_GFX | RADEON_RESET_COMPUTE | RADEON_RESET_CP)) + grbm_soft_reset = SOFT_RESET_CP | SOFT_RESET_GFX; + + if (reset_mask & RADEON_RESET_CP) { + grbm_soft_reset |= SOFT_RESET_CP; + + srbm_soft_reset |= SOFT_RESET_GRBM; + } + + if (reset_mask & RADEON_RESET_DMA) + srbm_soft_reset |= SOFT_RESET_SDMA; + + if (reset_mask & RADEON_RESET_DMA1) + srbm_soft_reset |= SOFT_RESET_SDMA1; + + if (reset_mask & RADEON_RESET_DISPLAY) + srbm_soft_reset |= SOFT_RESET_DC; + + if (reset_mask & RADEON_RESET_RLC) + grbm_soft_reset |= SOFT_RESET_RLC; + + if (reset_mask & RADEON_RESET_SEM) + srbm_soft_reset |= SOFT_RESET_SEM; + + if (reset_mask & RADEON_RESET_IH) + srbm_soft_reset |= SOFT_RESET_IH; + + if (reset_mask & RADEON_RESET_GRBM) + srbm_soft_reset |= SOFT_RESET_GRBM; + + if (reset_mask & RADEON_RESET_VMC) + srbm_soft_reset |= SOFT_RESET_VMC; + + if (!(rdev->flags & RADEON_IS_IGP)) { + if (reset_mask & RADEON_RESET_MC) + srbm_soft_reset |= SOFT_RESET_MC; + } + + if (grbm_soft_reset) { + tmp = RREG32(GRBM_SOFT_RESET); + tmp |= grbm_soft_reset; + dev_info(rdev->dev, "GRBM_SOFT_RESET=0x%08X\n", tmp); + WREG32(GRBM_SOFT_RESET, tmp); + tmp = RREG32(GRBM_SOFT_RESET); + + udelay(50); + + tmp &= ~grbm_soft_reset; + WREG32(GRBM_SOFT_RESET, tmp); + tmp = RREG32(GRBM_SOFT_RESET); + } + + if (srbm_soft_reset) { + tmp = RREG32(SRBM_SOFT_RESET); + tmp |= srbm_soft_reset; + dev_info(rdev->dev, "SRBM_SOFT_RESET=0x%08X\n", tmp); + WREG32(SRBM_SOFT_RESET, tmp); + tmp = RREG32(SRBM_SOFT_RESET); + + udelay(50); + + tmp &= ~srbm_soft_reset; + WREG32(SRBM_SOFT_RESET, tmp); + tmp = RREG32(SRBM_SOFT_RESET); + }
- dev_info(rdev->dev, " GRBM_SOFT_RESET=0x%08X\n", grbm_reset); - WREG32(GRBM_SOFT_RESET, grbm_reset); - (void)RREG32(GRBM_SOFT_RESET); - udelay(50); - WREG32(GRBM_SOFT_RESET, 0); - (void)RREG32(GRBM_SOFT_RESET); /* Wait a little for things to settle down */ udelay(50); - dev_info(rdev->dev, " GRBM_STATUS=0x%08X\n", - RREG32(GRBM_STATUS)); - dev_info(rdev->dev, " GRBM_STATUS2=0x%08X\n", - RREG32(GRBM_STATUS2)); - dev_info(rdev->dev, " GRBM_STATUS_SE0=0x%08X\n", - RREG32(GRBM_STATUS_SE0)); - dev_info(rdev->dev, " GRBM_STATUS_SE1=0x%08X\n", - RREG32(GRBM_STATUS_SE1)); - dev_info(rdev->dev, " GRBM_STATUS_SE2=0x%08X\n", - RREG32(GRBM_STATUS_SE2)); - dev_info(rdev->dev, " GRBM_STATUS_SE3=0x%08X\n", - RREG32(GRBM_STATUS_SE3)); - dev_info(rdev->dev, " SRBM_STATUS=0x%08X\n", - RREG32(SRBM_STATUS)); - dev_info(rdev->dev, " SRBM_STATUS2=0x%08X\n", - RREG32(SRBM_STATUS2)); + evergreen_mc_resume(rdev, &save); - return 0; + udelay(50); + + cik_print_gpu_status_regs(rdev); }
/** - * cik_asic_reset - soft reset compute and gfx + * cik_asic_reset - soft reset GPU * * @rdev: radeon_device pointer * - * Soft reset the CPC blocks (CIK). - * XXX: make this more fine grained and only reset - * what is necessary. + * Look up which blocks are hung and attempt + * to reset them. * Returns 0 for success. */ int cik_asic_reset(struct radeon_device *rdev) { - int r; + u32 reset_mask;
- r = cik_compute_gpu_soft_reset(rdev); - if (r) - dev_info(rdev->dev, "Compute reset failed!\n"); + reset_mask = cik_gpu_check_soft_reset(rdev); + + if (reset_mask) + r600_set_bios_scratch_engine_hung(rdev, true); + + cik_gpu_soft_reset(rdev, reset_mask);
- return cik_gfx_gpu_soft_reset(rdev); + reset_mask = cik_gpu_check_soft_reset(rdev); + + if (!reset_mask) + r600_set_bios_scratch_engine_hung(rdev, false); + + return 0; +} + +/** + * cik_gfx_is_lockup - check if the 3D engine is locked up + * + * @rdev: radeon_device pointer + * @ring: radeon_ring structure holding ring information + * + * Check if the 3D engine is locked up (CIK). + * Returns true if the engine is locked, false if not. + */ +bool cik_gfx_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring) +{ + u32 reset_mask = cik_gpu_check_soft_reset(rdev); + + if (!(reset_mask & (RADEON_RESET_GFX | + RADEON_RESET_COMPUTE | + RADEON_RESET_CP))) { + radeon_ring_lockup_update(ring); + return false; + } + /* force CP activities */ + radeon_ring_force_activity(rdev, ring); + return radeon_ring_test_lockup(rdev, ring); }
/** @@ -2938,13 +3031,15 @@ int cik_asic_reset(struct radeon_device *rdev) */ bool cik_sdma_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring) { - u32 dma_status_reg; + u32 reset_mask = cik_gpu_check_soft_reset(rdev); + u32 mask;
if (ring->idx == R600_RING_TYPE_DMA_INDEX) - dma_status_reg = RREG32(SDMA0_STATUS_REG + SDMA0_REGISTER_OFFSET); + mask = RADEON_RESET_DMA; else - dma_status_reg = RREG32(SDMA0_STATUS_REG + SDMA1_REGISTER_OFFSET); - if (dma_status_reg & SDMA_IDLE) { + mask = RADEON_RESET_DMA1; + + if (!(reset_mask & mask)) { radeon_ring_lockup_update(ring); return false; } diff --git a/drivers/gpu/drm/radeon/cikd.h b/drivers/gpu/drm/radeon/cikd.h index daa51ac..8afb334 100644 --- a/drivers/gpu/drm/radeon/cikd.h +++ b/drivers/gpu/drm/radeon/cikd.h @@ -40,7 +40,19 @@ #define QUEUEID(x) ((x) << 8)
#define SRBM_STATUS2 0xE4C +#define SDMA_BUSY (1 << 5) +#define SDMA1_BUSY (1 << 6) #define SRBM_STATUS 0xE50 +#define UVD_RQ_PENDING (1 << 1) +#define GRBM_RQ_PENDING (1 << 5) +#define VMC_BUSY (1 << 8) +#define MCB_BUSY (1 << 9) +#define MCB_NON_DISPLAY_BUSY (1 << 10) +#define MCC_BUSY (1 << 11) +#define MCD_BUSY (1 << 12) +#define SEM_BUSY (1 << 14) +#define IH_BUSY (1 << 17) +#define UVD_BUSY (1 << 19)
#define SRBM_SOFT_RESET 0xE60 #define SOFT_RESET_BIF (1 << 1)
From: Alex Deucher alexander.deucher@amd.com
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/evergreen_reg.h | 4 ++++ drivers/gpu/drm/radeon/radeon.h | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/evergreen_reg.h b/drivers/gpu/drm/radeon/evergreen_reg.h index 881aba2..50948ac 100644 --- a/drivers/gpu/drm/radeon/evergreen_reg.h +++ b/drivers/gpu/drm/radeon/evergreen_reg.h @@ -24,6 +24,10 @@ #ifndef __EVERGREEN_REG_H__ #define __EVERGREEN_REG_H__
+/* trinity */ +#define TN_SMC_IND_INDEX_0 0x200 +#define TN_SMC_IND_DATA_0 0x204 + /* evergreen */ #define EVERGREEN_VGA_MEMORY_BASE_ADDRESS 0x310 #define EVERGREEN_VGA_MEMORY_BASE_ADDRESS_HIGH 0x324 diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index b329e99..9af0fa6 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1806,6 +1806,8 @@ void r100_io_wreg(struct radeon_device *rdev, u32 reg, u32 v); #define WREG32_PCIE(reg, v) rv370_pcie_wreg(rdev, (reg), (v)) #define RREG32_PCIE_PORT(reg) rdev->pciep_rreg(rdev, (reg)) #define WREG32_PCIE_PORT(reg, v) rdev->pciep_wreg(rdev, (reg), (v)) +#define RREG32_SMC(reg) tn_smc_rreg(rdev, (reg)) +#define WREG32_SMC(reg, v) tn_smc_wreg(rdev, (reg), (v)) #define WREG32_P(reg, val, mask) \ do { \ uint32_t tmp_ = RREG32(reg); \ @@ -1844,6 +1846,21 @@ static inline void rv370_pcie_wreg(struct radeon_device *rdev, uint32_t reg, uin WREG32(RADEON_PCIE_DATA, (v)); }
+static inline u32 tn_smc_rreg(struct radeon_device *rdev, u32 reg) +{ + u32 r; + + WREG32(TN_SMC_IND_INDEX_0, (reg)); + r = RREG32(TN_SMC_IND_DATA_0); + return r; +} + +static inline void tn_smc_wreg(struct radeon_device *rdev, u32 reg, u32 v) +{ + WREG32(TN_SMC_IND_INDEX_0, (reg)); + WREG32(TN_SMC_IND_DATA_0, (v)); +} + void r100_pll_errata_after_index(struct radeon_device *rdev);
From: Alex Deucher alexander.deucher@amd.com
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/cik.c | 22 ++++++++++++++++++++++ drivers/gpu/drm/radeon/cikd.h | 7 +++++++ drivers/gpu/drm/radeon/radeon_asic.h | 1 + 3 files changed, 30 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index aaf7bba..0db0ebc 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -78,6 +78,28 @@ extern void si_rlc_fini(struct radeon_device *rdev); extern int si_rlc_init(struct radeon_device *rdev); static void cik_rlc_stop(struct radeon_device *rdev);
+/** + * cik_get_xclk - get the xclk + * + * @rdev: radeon_device pointer + * + * Returns the reference clock used by the gfx engine + * (CIK). + */ +u32 cik_get_xclk(struct radeon_device *rdev) +{ + u32 reference_clock = rdev->clock.spll.reference_freq; + + if (rdev->flags & RADEON_IS_IGP) { + if (RREG32_SMC(GENERAL_PWRMGT) & GPU_COUNTER_CLK) + return reference_clock / 2; + } else { + if (RREG32_SMC(CG_CLKPIN_CNTL) & XTALIN_DIVIDE) + return reference_clock / 4; + } + return reference_clock; +} + #define BONAIRE_IO_MC_REGS_SIZE 36
static const u32 bonaire_io_mc_regs[BONAIRE_IO_MC_REGS_SIZE][2] = diff --git a/drivers/gpu/drm/radeon/cikd.h b/drivers/gpu/drm/radeon/cikd.h index 8afb334..f00e273 100644 --- a/drivers/gpu/drm/radeon/cikd.h +++ b/drivers/gpu/drm/radeon/cikd.h @@ -28,6 +28,13 @@
#define CIK_RB_BITMAP_WIDTH_PER_SH 2
+/* SMC IND registers */ +#define GENERAL_PWRMGT 0xC0200000 +# define GPU_COUNTER_CLK (1 << 15) + +#define CG_CLKPIN_CNTL 0xC05001A0 +# define XTALIN_DIVIDE (1 << 1) + #define VGA_HDP_CONTROL 0x328 #define VGA_MEMORY_DISABLE (1 << 4)
diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 248da72..05f75f7 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -557,5 +557,6 @@ int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk); * cik */ uint64_t cik_get_gpu_clock_counter(struct radeon_device *rdev); +u32 cik_get_xclk(struct radeon_device *rdev);
#endif
From: Alex Deucher alexander.deucher@amd.com
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/cik.c | 21 +++++++++++++++++++++ drivers/gpu/drm/radeon/cikd.h | 3 +++ drivers/gpu/drm/radeon/radeon_asic.c | 6 +++++- drivers/gpu/drm/radeon/radeon_asic.h | 2 ++ 4 files changed, 31 insertions(+), 1 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 0db0ebc..e8ea845 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -78,6 +78,27 @@ extern void si_rlc_fini(struct radeon_device *rdev); extern int si_rlc_init(struct radeon_device *rdev); static void cik_rlc_stop(struct radeon_device *rdev);
+/* + * Indirect registers accessor + */ +u32 cik_pciep_rreg(struct radeon_device *rdev, u32 reg) +{ + u32 r; + + WREG32(PCIE_INDEX, reg); + (void)RREG32(PCIE_INDEX); + r = RREG32(PCIE_DATA); + return r; +} + +void cik_pciep_wreg(struct radeon_device *rdev, u32 reg, u32 v) +{ + WREG32(PCIE_INDEX, reg); + (void)RREG32(PCIE_INDEX); + WREG32(PCIE_DATA, v); + (void)RREG32(PCIE_DATA); +} + /** * cik_get_xclk - get the xclk * diff --git a/drivers/gpu/drm/radeon/cikd.h b/drivers/gpu/drm/radeon/cikd.h index f00e273..d23809a 100644 --- a/drivers/gpu/drm/radeon/cikd.h +++ b/drivers/gpu/drm/radeon/cikd.h @@ -35,6 +35,9 @@ #define CG_CLKPIN_CNTL 0xC05001A0 # define XTALIN_DIVIDE (1 << 1)
+#define PCIE_INDEX 0x38 +#define PCIE_DATA 0x3C + #define VGA_HDP_CONTROL 0x328 #define VGA_MEMORY_DISABLE (1 << 4)
diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index a2802b47..717b537 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -126,7 +126,11 @@ static void radeon_register_accessor_init(struct radeon_device *rdev) rdev->mc_rreg = &rs780_mc_rreg; rdev->mc_wreg = &rs780_mc_wreg; } - if (rdev->family >= CHIP_R600) { + + if (rdev->family >= CHIP_BONAIRE) { + rdev->pciep_rreg = &cik_pciep_rreg; + rdev->pciep_wreg = &cik_pciep_wreg; + } else if (rdev->family >= CHIP_R600) { rdev->pciep_rreg = &r600_pciep_rreg; rdev->pciep_wreg = &r600_pciep_wreg; } diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 05f75f7..8c19e36 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -558,5 +558,7 @@ int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk); */ uint64_t cik_get_gpu_clock_counter(struct radeon_device *rdev); u32 cik_get_xclk(struct radeon_device *rdev); +uint32_t cik_pciep_rreg(struct radeon_device *rdev, uint32_t reg); +void cik_pciep_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v);
#endif
From: Alex Deucher alexander.deucher@amd.com
SI uses v5 of the command table and uses a different table for memory PLLs.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/radeon_atombios.c | 6 +++++- 1 files changed, 5 insertions(+), 1 deletions(-)
diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index 3236755..774e354 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -2732,7 +2732,8 @@ int radeon_atom_get_clock_dividers(struct radeon_device *rdev, break; case 2: case 3: - /* r6xx, r7xx, evergreen, ni */ + case 5: + /* r6xx, r7xx, evergreen, ni, si */ if (rdev->family <= CHIP_RV770) { args.v2.ucAction = clock_type; args.v2.ulClock = cpu_to_le32(clock); /* 10 khz */ @@ -2765,6 +2766,9 @@ int radeon_atom_get_clock_dividers(struct radeon_device *rdev, dividers->vco_mode = (args.v3.ucCntlFlag & ATOM_PLL_CNTL_FLAG_MPLL_VCO_MODE) ? 1 : 0; } else { + /* for SI we use ComputeMemoryClockParam for memory plls */ + if (rdev->family >= CHIP_TAHITI) + return -EINVAL; args.v5.ulClockParams = cpu_to_le32((clock_type << 24) | clock); if (strobe_mode) args.v5.ucInputFlag = ATOM_PLL_INPUT_FLAG_PLL_STROBE_MODE_EN;
From: Alex Deucher alexander.deucher@amd.com
CIK uses a slightly different variant of the table structs and params.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/radeon_atombios.c | 20 +++++++++++++++++++- drivers/gpu/drm/radeon/radeon_mode.h | 3 +++ 2 files changed, 22 insertions(+), 1 deletions(-)
diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index 774e354..bf3b924 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -2700,6 +2700,8 @@ union get_clock_dividers { struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V3 v3; struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V4 v4; struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V5 v5; + struct _COMPUTE_GPU_CLOCK_INPUT_PARAMETERS_V1_6 v6_in; + struct _COMPUTE_GPU_CLOCK_OUTPUT_PARAMETERS_V1_6 v6_out; };
int radeon_atom_get_clock_dividers(struct radeon_device *rdev, @@ -2794,9 +2796,25 @@ int radeon_atom_get_clock_dividers(struct radeon_device *rdev,
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
- dividers->post_div = args.v4.ucPostDiv; + dividers->post_divider = dividers->post_div = args.v4.ucPostDiv; dividers->real_clock = le32_to_cpu(args.v4.ulClock); break; + case 6: + /* CI */ + /* COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK, COMPUTE_GPUCLK_INPUT_FLAG_SCLK */ + args.v6_in.ulClock.ulComputeClockFlag = clock_type; + args.v6_in.ulClock.ulClockFreq = cpu_to_le32(clock); /* 10 khz */ + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + + dividers->whole_fb_div = le16_to_cpu(args.v6_out.ulFbDiv.usFbDiv); + dividers->frac_fb_div = le16_to_cpu(args.v6_out.ulFbDiv.usFbDivFrac); + dividers->ref_div = args.v6_out.ucPllRefDiv; + dividers->post_div = args.v6_out.ucPllPostDiv; + dividers->flags = args.v6_out.ucPllCntlFlag; + dividers->real_clock = le32_to_cpu(args.v6_out.ulClock.ulClock); + dividers->post_divider = args.v6_out.ulClock.ucPostDiv; + break; default: return -EINVAL; } diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index 4ed0a4c..576511f 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -514,6 +514,9 @@ struct atom_clock_dividers { bool enable_dithen; u32 vco_mode; u32 real_clock; + /* added for CI */ + u32 post_divider; + u32 flags; };
extern enum radeon_tv_std
From: Christian König christian.koenig@amd.com
v2: agd5f: fix clock dividers setup for bonaire v3: agd5f: rebase
Signed-off-by: Christian König christian.koenig@amd.com Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/cik.c | 111 ++++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/cikd.h | 28 +++++++++ drivers/gpu/drm/radeon/radeon_asic.h | 2 + drivers/gpu/drm/radeon/radeon_uvd.c | 8 +++ 4 files changed, 149 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index e8ea845..29e0cdd 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -1495,6 +1495,9 @@ static void cik_gpu_init(struct radeon_device *rdev) WREG32(DMIF_ADDR_CALC, gb_addr_config); WREG32(SDMA0_TILING_CONFIG + SDMA0_REGISTER_OFFSET, gb_addr_config & 0x70); WREG32(SDMA0_TILING_CONFIG + SDMA1_REGISTER_OFFSET, gb_addr_config & 0x70); + WREG32(UVD_UDEC_ADDR_CONFIG, gb_addr_config); + WREG32(UVD_UDEC_DB_ADDR_CONFIG, gb_addr_config); + WREG32(UVD_UDEC_DBW_ADDR_CONFIG, gb_addr_config);
cik_tiling_mode_table_init(rdev);
@@ -4906,6 +4909,16 @@ static int cik_startup(struct radeon_device *rdev) return r; }
+ r = cik_uvd_resume(rdev); + if (!r) { + r = radeon_fence_driver_start_ring(rdev, + R600_RING_TYPE_UVD_INDEX); + if (r) + dev_err(rdev->dev, "UVD fences init error (%d).\n", r); + } + if (r) + rdev->ring[R600_RING_TYPE_UVD_INDEX].ring_size = 0; + /* Enable IRQ */ if (!rdev->irq.installed) { r = radeon_irq_kms_init(rdev); @@ -4952,6 +4965,18 @@ static int cik_startup(struct radeon_device *rdev) if (r) return r;
+ ring = &rdev->ring[R600_RING_TYPE_UVD_INDEX]; + if (ring->ring_size) { + r = radeon_ring_init(rdev, ring, ring->ring_size, + R600_WB_UVD_RPTR_OFFSET, + UVD_RBC_RB_RPTR, UVD_RBC_RB_WPTR, + 0, 0xfffff, RADEON_CP_PACKET2); + if (!r) + r = r600_uvd_init(rdev); + if (r) + DRM_ERROR("radeon: failed initializing UVD (%d).\n", r); + } + r = radeon_ib_pool_init(rdev); if (r) { dev_err(rdev->dev, "IB initialization failed (%d).\n", r); @@ -5013,6 +5038,8 @@ int cik_suspend(struct radeon_device *rdev) radeon_vm_manager_fini(rdev); cik_cp_enable(rdev, false); cik_sdma_enable(rdev, false); + r600_uvd_rbc_stop(rdev); + radeon_uvd_suspend(rdev); cik_irq_suspend(rdev); radeon_wb_disable(rdev); cik_pcie_gart_disable(rdev); @@ -5096,6 +5123,13 @@ int cik_init(struct radeon_device *rdev) ring->ring_obj = NULL; r600_ring_init(rdev, ring, 256 * 1024);
+ r = radeon_uvd_init(rdev); + if (!r) { + ring = &rdev->ring[R600_RING_TYPE_UVD_INDEX]; + ring->ring_obj = NULL; + r600_ring_init(rdev, ring, 4096); + } + rdev->ih.ring_obj = NULL; r600_ih_ring_init(rdev, 64 * 1024);
@@ -5150,6 +5184,7 @@ void cik_fini(struct radeon_device *rdev) radeon_vm_manager_fini(rdev); radeon_ib_pool_fini(rdev); radeon_irq_kms_fini(rdev); + radeon_uvd_fini(rdev); cik_pcie_gart_fini(rdev); r600_vram_scratch_fini(rdev); radeon_gem_fini(rdev); @@ -5717,3 +5752,79 @@ uint64_t cik_get_gpu_clock_counter(struct radeon_device *rdev) return clock; }
+static int cik_set_uvd_clock(struct radeon_device *rdev, u32 clock, + u32 cntl_reg, u32 status_reg) +{ + int r, i; + struct atom_clock_dividers dividers; + uint32_t tmp; + + r = radeon_atom_get_clock_dividers(rdev, COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK, + clock, false, ÷rs); + if (r) + return r; + + tmp = RREG32_SMC(cntl_reg); + tmp &= ~(DCLK_DIR_CNTL_EN|DCLK_DIVIDER_MASK); + tmp |= dividers.post_divider; + WREG32_SMC(cntl_reg, tmp); + + for (i = 0; i < 100; i++) { + if (RREG32_SMC(status_reg) & DCLK_STATUS) + break; + mdelay(10); + } + if (i == 100) + return -ETIMEDOUT; + + return 0; +} + +int cik_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk) +{ + int r = 0; + + r = cik_set_uvd_clock(rdev, vclk, CG_VCLK_CNTL, CG_VCLK_STATUS); + if (r) + return r; + + r = cik_set_uvd_clock(rdev, dclk, CG_DCLK_CNTL, CG_DCLK_STATUS); + return r; +} + +int cik_uvd_resume(struct radeon_device *rdev) +{ + uint64_t addr; + uint32_t size; + int r; + + r = radeon_uvd_resume(rdev); + if (r) + return r; + + /* programm the VCPU memory controller bits 0-27 */ + addr = rdev->uvd.gpu_addr >> 3; + size = RADEON_GPU_PAGE_ALIGN(rdev->uvd_fw->size + 4) >> 3; + WREG32(UVD_VCPU_CACHE_OFFSET0, addr); + WREG32(UVD_VCPU_CACHE_SIZE0, size); + + addr += size; + size = RADEON_UVD_STACK_SIZE >> 3; + WREG32(UVD_VCPU_CACHE_OFFSET1, addr); + WREG32(UVD_VCPU_CACHE_SIZE1, size); + + addr += size; + size = RADEON_UVD_HEAP_SIZE >> 3; + WREG32(UVD_VCPU_CACHE_OFFSET2, addr); + WREG32(UVD_VCPU_CACHE_SIZE2, size); + + /* bits 28-31 */ + addr = (rdev->uvd.gpu_addr >> 28) & 0xF; + WREG32(UVD_LMI_ADDR_EXT, (addr << 12) | (addr << 0)); + + /* bits 32-39 */ + addr = (rdev->uvd.gpu_addr >> 32) & 0xFF; + WREG32(UVD_LMI_EXT40_ADDR, addr | (0x9 << 16) | (0x1 << 31)); + + return 0; +} diff --git a/drivers/gpu/drm/radeon/cikd.h b/drivers/gpu/drm/radeon/cikd.h index d23809a..79be39e 100644 --- a/drivers/gpu/drm/radeon/cikd.h +++ b/drivers/gpu/drm/radeon/cikd.h @@ -1204,4 +1204,32 @@ # define SDMA_SRBM_WRITE_EXTRA_BYTE_ENABLE(x) ((x) << 12) /* byte mask */
+/* UVD */ + +#define UVD_UDEC_ADDR_CONFIG 0xef4c +#define UVD_UDEC_DB_ADDR_CONFIG 0xef50 +#define UVD_UDEC_DBW_ADDR_CONFIG 0xef54 + +#define UVD_LMI_EXT40_ADDR 0xf498 +#define UVD_LMI_ADDR_EXT 0xf594 +#define UVD_VCPU_CACHE_OFFSET0 0xf608 +#define UVD_VCPU_CACHE_SIZE0 0xf60c +#define UVD_VCPU_CACHE_OFFSET1 0xf610 +#define UVD_VCPU_CACHE_SIZE1 0xf614 +#define UVD_VCPU_CACHE_OFFSET2 0xf618 +#define UVD_VCPU_CACHE_SIZE2 0xf61c + +#define UVD_RBC_RB_RPTR 0xf690 +#define UVD_RBC_RB_WPTR 0xf694 + +/* UVD clocks */ + +#define CG_DCLK_CNTL 0xC050009C +# define DCLK_DIVIDER_MASK 0x7f +# define DCLK_DIR_CNTL_EN (1 << 8) +#define CG_DCLK_STATUS 0xC05000A0 +# define DCLK_STATUS (1 << 0) +#define CG_VCLK_CNTL 0xC05000A4 +#define CG_VCLK_STATUS 0xC05000A8 + #endif diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 8c19e36..3405742 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -560,5 +560,7 @@ uint64_t cik_get_gpu_clock_counter(struct radeon_device *rdev); u32 cik_get_xclk(struct radeon_device *rdev); uint32_t cik_pciep_rreg(struct radeon_device *rdev, uint32_t reg); void cik_pciep_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v); +int cik_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk); +int cik_uvd_resume(struct radeon_device *rdev);
#endif diff --git a/drivers/gpu/drm/radeon/radeon_uvd.c b/drivers/gpu/drm/radeon/radeon_uvd.c index cad735d..fdc77d1 100644 --- a/drivers/gpu/drm/radeon/radeon_uvd.c +++ b/drivers/gpu/drm/radeon/radeon_uvd.c @@ -44,11 +44,13 @@ #define FIRMWARE_CYPRESS "radeon/CYPRESS_uvd.bin" #define FIRMWARE_SUMO "radeon/SUMO_uvd.bin" #define FIRMWARE_TAHITI "radeon/TAHITI_uvd.bin" +#define FIRMWARE_BONAIRE "radeon/BONAIRE_uvd.bin"
MODULE_FIRMWARE(FIRMWARE_RV710); MODULE_FIRMWARE(FIRMWARE_CYPRESS); MODULE_FIRMWARE(FIRMWARE_SUMO); MODULE_FIRMWARE(FIRMWARE_TAHITI); +MODULE_FIRMWARE(FIRMWARE_BONAIRE);
static void radeon_uvd_idle_work_handler(struct work_struct *work);
@@ -100,6 +102,12 @@ int radeon_uvd_init(struct radeon_device *rdev) fw_name = FIRMWARE_TAHITI; break;
+ case CHIP_BONAIRE: + case CHIP_KABINI: + case CHIP_KAVERI: + fw_name = FIRMWARE_BONAIRE; + break; + default: return -EINVAL; }
From: Alex Deucher alexander.deucher@amd.com
Allows us to select instanced registers based on: - ME (micro engine - Pipe - Queue - VMID
Switch MC setup to use this new function.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/cik.c | 27 +++++++++++++++++++++++++-- 1 files changed, 25 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 29e0cdd..bb7dbc4 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -163,6 +163,29 @@ static const u32 bonaire_io_mc_regs[BONAIRE_IO_MC_REGS_SIZE][2] = {0x0000009f, 0x00b48000} };
+/** + * cik_srbm_select - select specific register instances + * + * @rdev: radeon_device pointer + * @me: selected ME (micro engine) + * @pipe: pipe + * @queue: queue + * @vmid: VMID + * + * Switches the currently active registers instances. Some + * registers are instanced per VMID, others are instanced per + * me/pipe/queue combination. + */ +static void cik_srbm_select(struct radeon_device *rdev, + u32 me, u32 pipe, u32 queue, u32 vmid) +{ + u32 srbm_gfx_cntl = (PIPEID(pipe & 0x3) | + MEID(me & 0x3) | + VMID(vmid & 0xf) | + QUEUEID(queue & 0x7)); + WREG32(SRBM_GFX_CNTL, srbm_gfx_cntl); +} + /* ucode loading */ /** * ci_mc_load_microcode - load MC ucode into the hw @@ -3351,7 +3374,7 @@ static int cik_pcie_gart_enable(struct radeon_device *rdev) /* XXX SH_MEM regs */ /* where to put LDS, scratch, GPUVM in FSA64 space */ for (i = 0; i < 16; i++) { - WREG32(SRBM_GFX_CNTL, VMID(i)); + cik_srbm_select(rdev, 0, 0, 0, i); /* CP and shaders */ WREG32(SH_MEM_CONFIG, 0); WREG32(SH_MEM_APE1_BASE, 1); @@ -3364,7 +3387,7 @@ static int cik_pcie_gart_enable(struct radeon_device *rdev) WREG32(SDMA0_GFX_APE1_CNTL + SDMA1_REGISTER_OFFSET, 0); /* XXX SDMA RLC - todo */ } - WREG32(SRBM_GFX_CNTL, 0); + cik_srbm_select(rdev, 0, 0, 0, 0);
cik_pcie_gart_tlb_flush(rdev); DRM_INFO("PCIE GART of %uM enabled (table at 0x%016llX).\n",
From: Alex Deucher alexander.deucher@amd.com
Add callbacks to the radeon_ring struct to handle rptr/wptr fetchs and wptr updates. We currently use one version for all rings, but this allows us to override with a ring specific versions.
Needed for compute rings on CIK.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/radeon.h | 5 +++ drivers/gpu/drm/radeon/radeon_ring.c | 55 +++++++++++++++++++++++++-------- 2 files changed, 46 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 9af0fa6..ad4e68a 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -695,6 +695,11 @@ struct radeon_ring { u32 idx; u64 last_semaphore_signal_addr; u64 last_semaphore_wait_addr; + struct { + u32 (*get_rptr)(struct radeon_device *rdev, struct radeon_ring *ring); + u32 (*get_wptr)(struct radeon_device *rdev, struct radeon_ring *ring); + void (*set_wptr)(struct radeon_device *rdev, struct radeon_ring *ring); + } funcs; };
/* diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c index e17faa7..53018e9 100644 --- a/drivers/gpu/drm/radeon/radeon_ring.c +++ b/drivers/gpu/drm/radeon/radeon_ring.c @@ -357,6 +357,38 @@ bool radeon_ring_supports_scratch_reg(struct radeon_device *rdev, } }
+static u32 radeon_ring_get_rptr(struct radeon_device *rdev, + struct radeon_ring *ring) +{ + u32 rptr; + + if (rdev->wb.enabled && ring != &rdev->ring[R600_RING_TYPE_UVD_INDEX]) + rptr = le32_to_cpu(rdev->wb.wb[ring->rptr_offs/4]); + else + rptr = RREG32(ring->rptr_reg); + rptr = (rptr & ring->ptr_reg_mask) >> ring->ptr_reg_shift; + + return rptr; +} + +static u32 radeon_ring_get_wptr(struct radeon_device *rdev, + struct radeon_ring *ring) +{ + u32 wptr; + + wptr = RREG32(ring->wptr_reg); + wptr = (wptr & ring->ptr_reg_mask) >> ring->ptr_reg_shift; + + return wptr; +} + +static void radeon_ring_set_wptr(struct radeon_device *rdev, + struct radeon_ring *ring) +{ + WREG32(ring->wptr_reg, (ring->wptr << ring->ptr_reg_shift) & ring->ptr_reg_mask); + (void)RREG32(ring->wptr_reg); +} + /** * radeon_ring_free_size - update the free size * @@ -367,13 +399,7 @@ bool radeon_ring_supports_scratch_reg(struct radeon_device *rdev, */ void radeon_ring_free_size(struct radeon_device *rdev, struct radeon_ring *ring) { - u32 rptr; - - if (rdev->wb.enabled && ring != &rdev->ring[R600_RING_TYPE_UVD_INDEX]) - rptr = le32_to_cpu(rdev->wb.wb[ring->rptr_offs/4]); - else - rptr = RREG32(ring->rptr_reg); - ring->rptr = (rptr & ring->ptr_reg_mask) >> ring->ptr_reg_shift; + ring->rptr = ring->funcs.get_rptr(rdev, ring); /* This works because ring_size is a power of 2 */ ring->ring_free_dw = (ring->rptr + (ring->ring_size / 4)); ring->ring_free_dw -= ring->wptr; @@ -458,8 +484,7 @@ void radeon_ring_commit(struct radeon_device *rdev, struct radeon_ring *ring) radeon_ring_write(ring, ring->nop); } DRM_MEMORYBARRIER(); - WREG32(ring->wptr_reg, (ring->wptr << ring->ptr_reg_shift) & ring->ptr_reg_mask); - (void)RREG32(ring->wptr_reg); + ring->funcs.set_wptr(rdev, ring); }
/** @@ -561,7 +586,6 @@ void radeon_ring_lockup_update(struct radeon_ring *ring) bool radeon_ring_test_lockup(struct radeon_device *rdev, struct radeon_ring *ring) { unsigned long cjiffies, elapsed; - uint32_t rptr;
cjiffies = jiffies; if (!time_after(cjiffies, ring->last_activity)) { @@ -569,8 +593,7 @@ bool radeon_ring_test_lockup(struct radeon_device *rdev, struct radeon_ring *rin radeon_ring_lockup_update(ring); return false; } - rptr = RREG32(ring->rptr_reg); - ring->rptr = (rptr & ring->ptr_reg_mask) >> ring->ptr_reg_shift; + ring->rptr = ring->funcs.get_rptr(rdev, ring); if (ring->rptr != ring->last_rptr) { /* CP is still working no lockup */ radeon_ring_lockup_update(ring); @@ -708,6 +731,10 @@ int radeon_ring_init(struct radeon_device *rdev, struct radeon_ring *ring, unsig ring->ptr_reg_shift = ptr_reg_shift; ring->ptr_reg_mask = ptr_reg_mask; ring->nop = nop; + /* set the ptr callbacks */ + ring->funcs.get_rptr = &radeon_ring_get_rptr; + ring->funcs.get_wptr = &radeon_ring_get_wptr; + ring->funcs.set_wptr = &radeon_ring_set_wptr; /* Allocate ring buffer */ if (ring->ring_obj == NULL) { r = radeon_bo_create(rdev, ring->ring_size, PAGE_SIZE, true, @@ -797,9 +824,9 @@ static int radeon_debugfs_ring_info(struct seq_file *m, void *data)
radeon_ring_free_size(rdev, ring); count = (ring->ring_size / 4) - ring->ring_free_dw; - tmp = RREG32(ring->wptr_reg) >> ring->ptr_reg_shift; + tmp = ring->funcs.get_wptr(rdev, ring); seq_printf(m, "wptr(0x%04x): 0x%08x [%5d]\n", ring->wptr_reg, tmp, tmp); - tmp = RREG32(ring->rptr_reg) >> ring->ptr_reg_shift; + tmp = ring->funcs.get_rptr(rdev, ring); seq_printf(m, "rptr(0x%04x): 0x%08x [%5d]\n", ring->rptr_reg, tmp, tmp); if (ring->rptr_save_reg) { seq_printf(m, "rptr next(0x%04x): 0x%08x\n", ring->rptr_save_reg,
Am 26.06.2013 15:22, schrieb alexdeucher@gmail.com:
From: Alex Deucher alexander.deucher@amd.com
Add callbacks to the radeon_ring struct to handle rptr/wptr fetchs and wptr updates. We currently use one version for all rings, but this allows us to override with a ring specific versions.
Needed for compute rings on CIK.
Signed-off-by: Alex Deucher alexander.deucher@amd.com
I'm ok with the general idea but let's put those callback into the radeon_asic.ring structure where they belong.
With this we should be able to remove most of the UVD and ptr_reg_shift/ptr_reg_mask special case handling.
Christian.
drivers/gpu/drm/radeon/radeon.h | 5 +++ drivers/gpu/drm/radeon/radeon_ring.c | 55 +++++++++++++++++++++++++-------- 2 files changed, 46 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 9af0fa6..ad4e68a 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -695,6 +695,11 @@ struct radeon_ring { u32 idx; u64 last_semaphore_signal_addr; u64 last_semaphore_wait_addr;
struct {
u32 (*get_rptr)(struct radeon_device *rdev, struct radeon_ring *ring);
u32 (*get_wptr)(struct radeon_device *rdev, struct radeon_ring *ring);
void (*set_wptr)(struct radeon_device *rdev, struct radeon_ring *ring);
} funcs; };
/*
diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c index e17faa7..53018e9 100644 --- a/drivers/gpu/drm/radeon/radeon_ring.c +++ b/drivers/gpu/drm/radeon/radeon_ring.c @@ -357,6 +357,38 @@ bool radeon_ring_supports_scratch_reg(struct radeon_device *rdev, } }
+static u32 radeon_ring_get_rptr(struct radeon_device *rdev,
struct radeon_ring *ring)
+{
- u32 rptr;
- if (rdev->wb.enabled && ring != &rdev->ring[R600_RING_TYPE_UVD_INDEX])
rptr = le32_to_cpu(rdev->wb.wb[ring->rptr_offs/4]);
- else
rptr = RREG32(ring->rptr_reg);
- rptr = (rptr & ring->ptr_reg_mask) >> ring->ptr_reg_shift;
- return rptr;
+}
+static u32 radeon_ring_get_wptr(struct radeon_device *rdev,
struct radeon_ring *ring)
+{
- u32 wptr;
- wptr = RREG32(ring->wptr_reg);
- wptr = (wptr & ring->ptr_reg_mask) >> ring->ptr_reg_shift;
- return wptr;
+}
+static void radeon_ring_set_wptr(struct radeon_device *rdev,
struct radeon_ring *ring)
+{
- WREG32(ring->wptr_reg, (ring->wptr << ring->ptr_reg_shift) & ring->ptr_reg_mask);
- (void)RREG32(ring->wptr_reg);
+}
- /**
- radeon_ring_free_size - update the free size
@@ -367,13 +399,7 @@ bool radeon_ring_supports_scratch_reg(struct radeon_device *rdev, */ void radeon_ring_free_size(struct radeon_device *rdev, struct radeon_ring *ring) {
- u32 rptr;
- if (rdev->wb.enabled && ring != &rdev->ring[R600_RING_TYPE_UVD_INDEX])
rptr = le32_to_cpu(rdev->wb.wb[ring->rptr_offs/4]);
- else
rptr = RREG32(ring->rptr_reg);
- ring->rptr = (rptr & ring->ptr_reg_mask) >> ring->ptr_reg_shift;
- ring->rptr = ring->funcs.get_rptr(rdev, ring); /* This works because ring_size is a power of 2 */ ring->ring_free_dw = (ring->rptr + (ring->ring_size / 4)); ring->ring_free_dw -= ring->wptr;
@@ -458,8 +484,7 @@ void radeon_ring_commit(struct radeon_device *rdev, struct radeon_ring *ring) radeon_ring_write(ring, ring->nop); } DRM_MEMORYBARRIER();
- WREG32(ring->wptr_reg, (ring->wptr << ring->ptr_reg_shift) & ring->ptr_reg_mask);
- (void)RREG32(ring->wptr_reg);
ring->funcs.set_wptr(rdev, ring); }
/**
@@ -561,7 +586,6 @@ void radeon_ring_lockup_update(struct radeon_ring *ring) bool radeon_ring_test_lockup(struct radeon_device *rdev, struct radeon_ring *ring) { unsigned long cjiffies, elapsed;
uint32_t rptr;
cjiffies = jiffies; if (!time_after(cjiffies, ring->last_activity)) {
@@ -569,8 +593,7 @@ bool radeon_ring_test_lockup(struct radeon_device *rdev, struct radeon_ring *rin radeon_ring_lockup_update(ring); return false; }
- rptr = RREG32(ring->rptr_reg);
- ring->rptr = (rptr & ring->ptr_reg_mask) >> ring->ptr_reg_shift;
- ring->rptr = ring->funcs.get_rptr(rdev, ring); if (ring->rptr != ring->last_rptr) { /* CP is still working no lockup */ radeon_ring_lockup_update(ring);
@@ -708,6 +731,10 @@ int radeon_ring_init(struct radeon_device *rdev, struct radeon_ring *ring, unsig ring->ptr_reg_shift = ptr_reg_shift; ring->ptr_reg_mask = ptr_reg_mask; ring->nop = nop;
- /* set the ptr callbacks */
- ring->funcs.get_rptr = &radeon_ring_get_rptr;
- ring->funcs.get_wptr = &radeon_ring_get_wptr;
- ring->funcs.set_wptr = &radeon_ring_set_wptr; /* Allocate ring buffer */ if (ring->ring_obj == NULL) { r = radeon_bo_create(rdev, ring->ring_size, PAGE_SIZE, true,
@@ -797,9 +824,9 @@ static int radeon_debugfs_ring_info(struct seq_file *m, void *data)
radeon_ring_free_size(rdev, ring); count = (ring->ring_size / 4) - ring->ring_free_dw;
- tmp = RREG32(ring->wptr_reg) >> ring->ptr_reg_shift;
- tmp = ring->funcs.get_wptr(rdev, ring); seq_printf(m, "wptr(0x%04x): 0x%08x [%5d]\n", ring->wptr_reg, tmp, tmp);
- tmp = RREG32(ring->rptr_reg) >> ring->ptr_reg_shift;
- tmp = ring->funcs.get_rptr(rdev, ring); seq_printf(m, "rptr(0x%04x): 0x%08x [%5d]\n", ring->rptr_reg, tmp, tmp); if (ring->rptr_save_reg) { seq_printf(m, "rptr next(0x%04x): 0x%08x\n", ring->rptr_save_reg,
From: Alex Deucher alexander.deucher@amd.com
The doorbell aperture is a PCI BAR whose pages can be mapped to compute resources for things like wptrs for userspace queues.
This patch maps the BAR and sets up a simple allocator to allocate pages from the BAR.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/cik.c | 38 +++++++++++++ drivers/gpu/drm/radeon/radeon.h | 21 +++++++ drivers/gpu/drm/radeon/radeon_device.c | 94 ++++++++++++++++++++++++++++++++ 3 files changed, 153 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index bb7dbc4..5c28fa5 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -121,6 +121,44 @@ u32 cik_get_xclk(struct radeon_device *rdev) return reference_clock; }
+/** + * cik_mm_rdoorbell - read a doorbell dword + * + * @rdev: radeon_device pointer + * @offset: byte offset into the aperture + * + * Returns the value in the doorbell aperture at the + * requested offset (CIK). + */ +u32 cik_mm_rdoorbell(struct radeon_device *rdev, u32 offset) +{ + if (offset < rdev->doorbell.size) { + return readl(((void __iomem *)rdev->doorbell.ptr) + offset); + } else { + DRM_ERROR("reading beyond doorbell aperture: 0x%08x!\n", offset); + return 0; + } +} + +/** + * cik_mm_wdoorbell - write a doorbell dword + * + * @rdev: radeon_device pointer + * @offset: byte offset into the aperture + * @v: value to write + * + * Writes @v to the doorbell aperture at the + * requested offset (CIK). + */ +void cik_mm_wdoorbell(struct radeon_device *rdev, u32 offset, u32 v) +{ + if (offset < rdev->doorbell.size) { + writel(v, ((void __iomem *)rdev->doorbell.ptr) + offset); + } else { + DRM_ERROR("writing beyond doorbell aperture: 0x%08x!\n", offset); + } +} + #define BONAIRE_IO_MC_REGS_SIZE 36
static const u32 bonaire_io_mc_regs[BONAIRE_IO_MC_REGS_SIZE][2] = diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index ad4e68a..a2a3430 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -556,6 +556,20 @@ struct radeon_scratch { int radeon_scratch_get(struct radeon_device *rdev, uint32_t *reg); void radeon_scratch_free(struct radeon_device *rdev, uint32_t reg);
+/* + * GPU doorbell structures, functions & helpers + */ +struct radeon_doorbell { + u32 num_pages; + bool free[1024]; + /* doorbell mmio */ + resource_size_t base; + resource_size_t size; + void __iomem *ptr; +}; + +int radeon_doorbell_get(struct radeon_device *rdev, u32 *page); +void radeon_doorbell_free(struct radeon_device *rdev, u32 doorbell);
/* * IRQS. @@ -1711,6 +1725,7 @@ struct radeon_device { struct radeon_gart gart; struct radeon_mode_info mode_info; struct radeon_scratch scratch; + struct radeon_doorbell doorbell; struct radeon_mman mman; struct radeon_fence_driver fence_drv[RADEON_NUM_RINGS]; wait_queue_head_t fence_queue; @@ -1784,6 +1799,9 @@ void r100_mm_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v, u32 r100_io_rreg(struct radeon_device *rdev, u32 reg); void r100_io_wreg(struct radeon_device *rdev, u32 reg, u32 v);
+u32 cik_mm_rdoorbell(struct radeon_device *rdev, u32 offset); +void cik_mm_wdoorbell(struct radeon_device *rdev, u32 offset, u32 v); + /* * Cast helper */ @@ -1833,6 +1851,9 @@ void r100_io_wreg(struct radeon_device *rdev, u32 reg, u32 v); #define RREG32_IO(reg) r100_io_rreg(rdev, (reg)) #define WREG32_IO(reg, v) r100_io_wreg(rdev, (reg), (v))
+#define RDOORBELL32(offset) cik_mm_rdoorbell(rdev, (offset)) +#define WDOORBELL32(offset, v) cik_mm_wdoorbell(rdev, (offset), (v)) + /* * Indirect registers accessor */ diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 4e97ff7..82335e3 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -232,6 +232,94 @@ void radeon_scratch_free(struct radeon_device *rdev, uint32_t reg) }
/* + * GPU doorbell aperture helpers function. + */ +/** + * radeon_doorbell_init - Init doorbell driver information. + * + * @rdev: radeon_device pointer + * + * Init doorbell driver information (CIK) + * Returns 0 on success, error on failure. + */ +int radeon_doorbell_init(struct radeon_device *rdev) +{ + int i; + + /* doorbell bar mapping */ + rdev->doorbell.base = pci_resource_start(rdev->pdev, 2); + rdev->doorbell.size = pci_resource_len(rdev->pdev, 2); + + /* limit to 4 MB for now */ + if (rdev->doorbell.size > (4 * 1024 * 1024)) + rdev->doorbell.size = 4 * 1024 * 1024; + + rdev->doorbell.ptr = ioremap(rdev->doorbell.base, rdev->doorbell.size); + if (rdev->doorbell.ptr == NULL) { + return -ENOMEM; + } + DRM_INFO("doorbell mmio base: 0x%08X\n", (uint32_t)rdev->doorbell.base); + DRM_INFO("doorbell mmio size: %u\n", (unsigned)rdev->doorbell.size); + + rdev->doorbell.num_pages = rdev->doorbell.size / PAGE_SIZE; + + for (i = 0; i < rdev->doorbell.num_pages; i++) { + rdev->doorbell.free[i] = true; + } + return 0; +} + +/** + * radeon_doorbell_fini - Tear down doorbell driver information. + * + * @rdev: radeon_device pointer + * + * Tear down doorbell driver information (CIK) + */ +void radeon_doorbell_fini(struct radeon_device *rdev) +{ + iounmap(rdev->doorbell.ptr); + rdev->doorbell.ptr = NULL; +} + +/** + * radeon_doorbell_get - Allocate a doorbell page + * + * @rdev: radeon_device pointer + * @doorbell: doorbell page number + * + * Allocate a doorbell page for use by the driver (all asics). + * Returns 0 on success or -EINVAL on failure. + */ +int radeon_doorbell_get(struct radeon_device *rdev, u32 *doorbell) +{ + int i; + + for (i = 0; i < rdev->doorbell.num_pages; i++) { + if (rdev->doorbell.free[i]) { + rdev->doorbell.free[i] = false; + *doorbell = i; + return 0; + } + } + return -EINVAL; +} + +/** + * radeon_doorbell_free - Free a doorbell page + * + * @rdev: radeon_device pointer + * @doorbell: doorbell page number + * + * Free a doorbell page allocated for use by the driver (all asics) + */ +void radeon_doorbell_free(struct radeon_device *rdev, u32 doorbell) +{ + if (doorbell < rdev->doorbell.num_pages) + rdev->doorbell.free[doorbell] = true; +} + +/* * radeon_wb_*() * Writeback is the the method by which the the GPU updates special pages * in memory with the status of certain GPU events (fences, ring pointers, @@ -1162,6 +1250,10 @@ int radeon_device_init(struct radeon_device *rdev, DRM_INFO("register mmio base: 0x%08X\n", (uint32_t)rdev->rmmio_base); DRM_INFO("register mmio size: %u\n", (unsigned)rdev->rmmio_size);
+ /* doorbell bar mapping */ + if (rdev->family >= CHIP_BONAIRE) + radeon_doorbell_init(rdev); + /* io port mapping */ for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { if (pci_resource_flags(rdev->pdev, i) & IORESOURCE_IO) { @@ -1239,6 +1331,8 @@ void radeon_device_fini(struct radeon_device *rdev) rdev->rio_mem = NULL; iounmap(rdev->rmmio); rdev->rmmio = NULL; + if (rdev->family >= CHIP_BONAIRE) + radeon_doorbell_fini(rdev); radeon_debugfs_remove_files(rdev); }
On Wed, Jun 26, 2013 at 09:22:11AM -0400, alexdeucher@gmail.com wrote:
From: Alex Deucher alexander.deucher@amd.com
The doorbell aperture is a PCI BAR whose pages can be mapped to compute resources for things like wptrs for userspace queues.
This patch maps the BAR and sets up a simple allocator to allocate pages from the BAR.
This doorbell stuff is cryptic, is that some memory on the GPU ? Or is it more like a register file ? ie what is backing the pci bar.
Also probably want to use bitmap as i dont think gcc will turn bool array into a bitmap.
Cheers, Jerome
Signed-off-by: Alex Deucher alexander.deucher@amd.com
drivers/gpu/drm/radeon/cik.c | 38 +++++++++++++ drivers/gpu/drm/radeon/radeon.h | 21 +++++++ drivers/gpu/drm/radeon/radeon_device.c | 94 ++++++++++++++++++++++++++++++++ 3 files changed, 153 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index bb7dbc4..5c28fa5 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -121,6 +121,44 @@ u32 cik_get_xclk(struct radeon_device *rdev) return reference_clock; }
+/**
- cik_mm_rdoorbell - read a doorbell dword
- @rdev: radeon_device pointer
- @offset: byte offset into the aperture
- Returns the value in the doorbell aperture at the
- requested offset (CIK).
- */
+u32 cik_mm_rdoorbell(struct radeon_device *rdev, u32 offset) +{
- if (offset < rdev->doorbell.size) {
return readl(((void __iomem *)rdev->doorbell.ptr) + offset);
- } else {
DRM_ERROR("reading beyond doorbell aperture: 0x%08x!\n", offset);
return 0;
- }
+}
+/**
- cik_mm_wdoorbell - write a doorbell dword
- @rdev: radeon_device pointer
- @offset: byte offset into the aperture
- @v: value to write
- Writes @v to the doorbell aperture at the
- requested offset (CIK).
- */
+void cik_mm_wdoorbell(struct radeon_device *rdev, u32 offset, u32 v) +{
- if (offset < rdev->doorbell.size) {
writel(v, ((void __iomem *)rdev->doorbell.ptr) + offset);
- } else {
DRM_ERROR("writing beyond doorbell aperture: 0x%08x!\n", offset);
- }
+}
#define BONAIRE_IO_MC_REGS_SIZE 36
static const u32 bonaire_io_mc_regs[BONAIRE_IO_MC_REGS_SIZE][2] = diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index ad4e68a..a2a3430 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -556,6 +556,20 @@ struct radeon_scratch { int radeon_scratch_get(struct radeon_device *rdev, uint32_t *reg); void radeon_scratch_free(struct radeon_device *rdev, uint32_t reg);
+/*
- GPU doorbell structures, functions & helpers
- */
+struct radeon_doorbell {
- u32 num_pages;
- bool free[1024];
- /* doorbell mmio */
- resource_size_t base;
- resource_size_t size;
- void __iomem *ptr;
+};
+int radeon_doorbell_get(struct radeon_device *rdev, u32 *page); +void radeon_doorbell_free(struct radeon_device *rdev, u32 doorbell);
/*
- IRQS.
@@ -1711,6 +1725,7 @@ struct radeon_device { struct radeon_gart gart; struct radeon_mode_info mode_info; struct radeon_scratch scratch;
- struct radeon_doorbell doorbell; struct radeon_mman mman; struct radeon_fence_driver fence_drv[RADEON_NUM_RINGS]; wait_queue_head_t fence_queue;
@@ -1784,6 +1799,9 @@ void r100_mm_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v, u32 r100_io_rreg(struct radeon_device *rdev, u32 reg); void r100_io_wreg(struct radeon_device *rdev, u32 reg, u32 v);
+u32 cik_mm_rdoorbell(struct radeon_device *rdev, u32 offset); +void cik_mm_wdoorbell(struct radeon_device *rdev, u32 offset, u32 v);
/*
- Cast helper
*/ @@ -1833,6 +1851,9 @@ void r100_io_wreg(struct radeon_device *rdev, u32 reg, u32 v); #define RREG32_IO(reg) r100_io_rreg(rdev, (reg)) #define WREG32_IO(reg, v) r100_io_wreg(rdev, (reg), (v))
+#define RDOORBELL32(offset) cik_mm_rdoorbell(rdev, (offset)) +#define WDOORBELL32(offset, v) cik_mm_wdoorbell(rdev, (offset), (v))
/*
- Indirect registers accessor
*/ diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 4e97ff7..82335e3 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -232,6 +232,94 @@ void radeon_scratch_free(struct radeon_device *rdev, uint32_t reg) }
/*
- GPU doorbell aperture helpers function.
- */
+/**
- radeon_doorbell_init - Init doorbell driver information.
- @rdev: radeon_device pointer
- Init doorbell driver information (CIK)
- Returns 0 on success, error on failure.
- */
+int radeon_doorbell_init(struct radeon_device *rdev) +{
- int i;
- /* doorbell bar mapping */
- rdev->doorbell.base = pci_resource_start(rdev->pdev, 2);
- rdev->doorbell.size = pci_resource_len(rdev->pdev, 2);
- /* limit to 4 MB for now */
- if (rdev->doorbell.size > (4 * 1024 * 1024))
rdev->doorbell.size = 4 * 1024 * 1024;
- rdev->doorbell.ptr = ioremap(rdev->doorbell.base, rdev->doorbell.size);
- if (rdev->doorbell.ptr == NULL) {
return -ENOMEM;
- }
- DRM_INFO("doorbell mmio base: 0x%08X\n", (uint32_t)rdev->doorbell.base);
- DRM_INFO("doorbell mmio size: %u\n", (unsigned)rdev->doorbell.size);
- rdev->doorbell.num_pages = rdev->doorbell.size / PAGE_SIZE;
- for (i = 0; i < rdev->doorbell.num_pages; i++) {
rdev->doorbell.free[i] = true;
- }
- return 0;
+}
+/**
- radeon_doorbell_fini - Tear down doorbell driver information.
- @rdev: radeon_device pointer
- Tear down doorbell driver information (CIK)
- */
+void radeon_doorbell_fini(struct radeon_device *rdev) +{
- iounmap(rdev->doorbell.ptr);
- rdev->doorbell.ptr = NULL;
+}
+/**
- radeon_doorbell_get - Allocate a doorbell page
- @rdev: radeon_device pointer
- @doorbell: doorbell page number
- Allocate a doorbell page for use by the driver (all asics).
- Returns 0 on success or -EINVAL on failure.
- */
+int radeon_doorbell_get(struct radeon_device *rdev, u32 *doorbell) +{
- int i;
- for (i = 0; i < rdev->doorbell.num_pages; i++) {
if (rdev->doorbell.free[i]) {
rdev->doorbell.free[i] = false;
*doorbell = i;
return 0;
}
- }
- return -EINVAL;
+}
+/**
- radeon_doorbell_free - Free a doorbell page
- @rdev: radeon_device pointer
- @doorbell: doorbell page number
- Free a doorbell page allocated for use by the driver (all asics)
- */
+void radeon_doorbell_free(struct radeon_device *rdev, u32 doorbell) +{
- if (doorbell < rdev->doorbell.num_pages)
rdev->doorbell.free[doorbell] = true;
+}
+/*
- radeon_wb_*()
- Writeback is the the method by which the the GPU updates special pages
- in memory with the status of certain GPU events (fences, ring pointers,
@@ -1162,6 +1250,10 @@ int radeon_device_init(struct radeon_device *rdev, DRM_INFO("register mmio base: 0x%08X\n", (uint32_t)rdev->rmmio_base); DRM_INFO("register mmio size: %u\n", (unsigned)rdev->rmmio_size);
- /* doorbell bar mapping */
- if (rdev->family >= CHIP_BONAIRE)
radeon_doorbell_init(rdev);
- /* io port mapping */ for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { if (pci_resource_flags(rdev->pdev, i) & IORESOURCE_IO) {
@@ -1239,6 +1331,8 @@ void radeon_device_fini(struct radeon_device *rdev) rdev->rio_mem = NULL; iounmap(rdev->rmmio); rdev->rmmio = NULL;
- if (rdev->family >= CHIP_BONAIRE)
radeon_debugfs_remove_files(rdev);radeon_doorbell_fini(rdev);
}
-- 1.7.7.5
dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
On Wed, Jun 26, 2013 at 8:57 AM, Jerome Glisse j.glisse@gmail.com wrote:
On Wed, Jun 26, 2013 at 09:22:11AM -0400, alexdeucher@gmail.com wrote:
From: Alex Deucher alexander.deucher@amd.com
The doorbell aperture is a PCI BAR whose pages can be mapped to compute resources for things like wptrs for userspace queues.
This patch maps the BAR and sets up a simple allocator to allocate pages from the BAR.
This doorbell stuff is cryptic, is that some memory on the GPU ? Or is it more like a register file ? ie what is backing the pci bar.
There's no memory backing it. It's just an aperture that the GPU can selectively listens to. You assign dw offsets from the doorbell aperture for things like the wrptr for compute rings. When the driver or process write a new wtpr value to the offset assigned to it's specific ring, the GPU starts processing it. Like ringing a doorbell.
Alex
Also probably want to use bitmap as i dont think gcc will turn bool array into a bitmap.
Cheers, Jerome
Signed-off-by: Alex Deucher alexander.deucher@amd.com
drivers/gpu/drm/radeon/cik.c | 38 +++++++++++++ drivers/gpu/drm/radeon/radeon.h | 21 +++++++ drivers/gpu/drm/radeon/radeon_device.c | 94 ++++++++++++++++++++++++++++++++ 3 files changed, 153 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index bb7dbc4..5c28fa5 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -121,6 +121,44 @@ u32 cik_get_xclk(struct radeon_device *rdev) return reference_clock; }
+/**
- cik_mm_rdoorbell - read a doorbell dword
- @rdev: radeon_device pointer
- @offset: byte offset into the aperture
- Returns the value in the doorbell aperture at the
- requested offset (CIK).
- */
+u32 cik_mm_rdoorbell(struct radeon_device *rdev, u32 offset) +{
if (offset < rdev->doorbell.size) {
return readl(((void __iomem *)rdev->doorbell.ptr) + offset);
} else {
DRM_ERROR("reading beyond doorbell aperture: 0x%08x!\n", offset);
return 0;
}
+}
+/**
- cik_mm_wdoorbell - write a doorbell dword
- @rdev: radeon_device pointer
- @offset: byte offset into the aperture
- @v: value to write
- Writes @v to the doorbell aperture at the
- requested offset (CIK).
- */
+void cik_mm_wdoorbell(struct radeon_device *rdev, u32 offset, u32 v) +{
if (offset < rdev->doorbell.size) {
writel(v, ((void __iomem *)rdev->doorbell.ptr) + offset);
} else {
DRM_ERROR("writing beyond doorbell aperture: 0x%08x!\n", offset);
}
+}
#define BONAIRE_IO_MC_REGS_SIZE 36
static const u32 bonaire_io_mc_regs[BONAIRE_IO_MC_REGS_SIZE][2] = diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index ad4e68a..a2a3430 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -556,6 +556,20 @@ struct radeon_scratch { int radeon_scratch_get(struct radeon_device *rdev, uint32_t *reg); void radeon_scratch_free(struct radeon_device *rdev, uint32_t reg);
+/*
- GPU doorbell structures, functions & helpers
- */
+struct radeon_doorbell {
u32 num_pages;
bool free[1024];
/* doorbell mmio */
resource_size_t base;
resource_size_t size;
void __iomem *ptr;
+};
+int radeon_doorbell_get(struct radeon_device *rdev, u32 *page); +void radeon_doorbell_free(struct radeon_device *rdev, u32 doorbell);
/*
- IRQS.
@@ -1711,6 +1725,7 @@ struct radeon_device { struct radeon_gart gart; struct radeon_mode_info mode_info; struct radeon_scratch scratch;
struct radeon_doorbell doorbell; struct radeon_mman mman; struct radeon_fence_driver fence_drv[RADEON_NUM_RINGS]; wait_queue_head_t fence_queue;
@@ -1784,6 +1799,9 @@ void r100_mm_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v, u32 r100_io_rreg(struct radeon_device *rdev, u32 reg); void r100_io_wreg(struct radeon_device *rdev, u32 reg, u32 v);
+u32 cik_mm_rdoorbell(struct radeon_device *rdev, u32 offset); +void cik_mm_wdoorbell(struct radeon_device *rdev, u32 offset, u32 v);
/*
- Cast helper
*/ @@ -1833,6 +1851,9 @@ void r100_io_wreg(struct radeon_device *rdev, u32 reg, u32 v); #define RREG32_IO(reg) r100_io_rreg(rdev, (reg)) #define WREG32_IO(reg, v) r100_io_wreg(rdev, (reg), (v))
+#define RDOORBELL32(offset) cik_mm_rdoorbell(rdev, (offset)) +#define WDOORBELL32(offset, v) cik_mm_wdoorbell(rdev, (offset), (v))
/*
- Indirect registers accessor
*/ diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 4e97ff7..82335e3 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -232,6 +232,94 @@ void radeon_scratch_free(struct radeon_device *rdev, uint32_t reg) }
/*
- GPU doorbell aperture helpers function.
- */
+/**
- radeon_doorbell_init - Init doorbell driver information.
- @rdev: radeon_device pointer
- Init doorbell driver information (CIK)
- Returns 0 on success, error on failure.
- */
+int radeon_doorbell_init(struct radeon_device *rdev) +{
int i;
/* doorbell bar mapping */
rdev->doorbell.base = pci_resource_start(rdev->pdev, 2);
rdev->doorbell.size = pci_resource_len(rdev->pdev, 2);
/* limit to 4 MB for now */
if (rdev->doorbell.size > (4 * 1024 * 1024))
rdev->doorbell.size = 4 * 1024 * 1024;
rdev->doorbell.ptr = ioremap(rdev->doorbell.base, rdev->doorbell.size);
if (rdev->doorbell.ptr == NULL) {
return -ENOMEM;
}
DRM_INFO("doorbell mmio base: 0x%08X\n", (uint32_t)rdev->doorbell.base);
DRM_INFO("doorbell mmio size: %u\n", (unsigned)rdev->doorbell.size);
rdev->doorbell.num_pages = rdev->doorbell.size / PAGE_SIZE;
for (i = 0; i < rdev->doorbell.num_pages; i++) {
rdev->doorbell.free[i] = true;
}
return 0;
+}
+/**
- radeon_doorbell_fini - Tear down doorbell driver information.
- @rdev: radeon_device pointer
- Tear down doorbell driver information (CIK)
- */
+void radeon_doorbell_fini(struct radeon_device *rdev) +{
iounmap(rdev->doorbell.ptr);
rdev->doorbell.ptr = NULL;
+}
+/**
- radeon_doorbell_get - Allocate a doorbell page
- @rdev: radeon_device pointer
- @doorbell: doorbell page number
- Allocate a doorbell page for use by the driver (all asics).
- Returns 0 on success or -EINVAL on failure.
- */
+int radeon_doorbell_get(struct radeon_device *rdev, u32 *doorbell) +{
int i;
for (i = 0; i < rdev->doorbell.num_pages; i++) {
if (rdev->doorbell.free[i]) {
rdev->doorbell.free[i] = false;
*doorbell = i;
return 0;
}
}
return -EINVAL;
+}
+/**
- radeon_doorbell_free - Free a doorbell page
- @rdev: radeon_device pointer
- @doorbell: doorbell page number
- Free a doorbell page allocated for use by the driver (all asics)
- */
+void radeon_doorbell_free(struct radeon_device *rdev, u32 doorbell) +{
if (doorbell < rdev->doorbell.num_pages)
rdev->doorbell.free[doorbell] = true;
+}
+/*
- radeon_wb_*()
- Writeback is the the method by which the the GPU updates special pages
- in memory with the status of certain GPU events (fences, ring pointers,
@@ -1162,6 +1250,10 @@ int radeon_device_init(struct radeon_device *rdev, DRM_INFO("register mmio base: 0x%08X\n", (uint32_t)rdev->rmmio_base); DRM_INFO("register mmio size: %u\n", (unsigned)rdev->rmmio_size);
/* doorbell bar mapping */
if (rdev->family >= CHIP_BONAIRE)
radeon_doorbell_init(rdev);
/* io port mapping */ for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { if (pci_resource_flags(rdev->pdev, i) & IORESOURCE_IO) {
@@ -1239,6 +1331,8 @@ void radeon_device_fini(struct radeon_device *rdev) rdev->rio_mem = NULL; iounmap(rdev->rmmio); rdev->rmmio = NULL;
if (rdev->family >= CHIP_BONAIRE)
radeon_doorbell_fini(rdev); radeon_debugfs_remove_files(rdev);
}
-- 1.7.7.5
dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
From: Alex Deucher alexander.deucher@amd.com
On CIK, the compute rings work slightly differently than on previous asics, however the basic concepts are the same.
The main differences: - New MEC engines for compute queues - Multiple queues per MEC: - CI/KB: 1 MEC, 4 pipes per MEC, 8 queues per pipe = 32 queues - KV: 2 MEC, 4 pipes per MEC, 8 queues per pipe = 64 queues - Queues can be allocated and scheduled by another queue - New doorbell aperture allows you to assign space in the aperture for the wptr which allows for userspace access to queues
v2: add wptr shadow, fix eop setup
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/cik.c | 528 +++++++++++++++++++++++++++++++++++- drivers/gpu/drm/radeon/cikd.h | 62 +++++ drivers/gpu/drm/radeon/radeon.h | 19 ++ drivers/gpu/drm/radeon/radeon_cs.c | 4 +- 4 files changed, 601 insertions(+), 12 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 5c28fa5..9d2d6bb 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -1687,6 +1687,7 @@ int cik_ring_test(struct radeon_device *rdev, struct radeon_ring *ring) radeon_ring_write(ring, ((scratch - PACKET3_SET_UCONFIG_REG_START) >> 2)); radeon_ring_write(ring, 0xDEADBEEF); radeon_ring_unlock_commit(rdev, ring); + for (i = 0; i < rdev->usec_timeout; i++) { tmp = RREG32(scratch); if (tmp == 0xDEADBEEF) @@ -2112,6 +2113,51 @@ static int cik_cp_gfx_resume(struct radeon_device *rdev) return 0; }
+static u32 cik_compute_ring_get_rptr(struct radeon_device *rdev, + struct radeon_ring *ring) +{ + u32 rptr; + + + + if (rdev->wb.enabled) { + rptr = le32_to_cpu(rdev->wb.wb[ring->rptr_offs/4]); + } else { + cik_srbm_select(rdev, ring->me, ring->pipe, ring->queue, 0); + rptr = RREG32(CP_HQD_PQ_RPTR); + cik_srbm_select(rdev, 0, 0, 0, 0); + } + rptr = (rptr & ring->ptr_reg_mask) >> ring->ptr_reg_shift; + + return rptr; +} + +static u32 cik_compute_ring_get_wptr(struct radeon_device *rdev, + struct radeon_ring *ring) +{ + u32 wptr; + + if (rdev->wb.enabled) { + wptr = le32_to_cpu(rdev->wb.wb[ring->wptr_offs/4]); + } else { + cik_srbm_select(rdev, ring->me, ring->pipe, ring->queue, 0); + wptr = RREG32(CP_HQD_PQ_WPTR); + cik_srbm_select(rdev, 0, 0, 0, 0); + } + wptr = (wptr & ring->ptr_reg_mask) >> ring->ptr_reg_shift; + + return wptr; +} + +static void cik_compute_ring_set_wptr(struct radeon_device *rdev, + struct radeon_ring *ring) +{ + u32 wptr = (ring->wptr << ring->ptr_reg_shift) & ring->ptr_reg_mask; + + rdev->wb.wb[ring->wptr_offs/4] = cpu_to_le32(wptr); + WDOORBELL32(ring->doorbell_offset, wptr); +} + /** * cik_cp_compute_enable - enable/disable the compute CP MEs * @@ -2176,7 +2222,8 @@ static int cik_cp_compute_load_microcode(struct radeon_device *rdev) */ static int cik_cp_compute_start(struct radeon_device *rdev) { - //todo + cik_cp_compute_enable(rdev, true); + return 0; }
@@ -2190,10 +2237,171 @@ static int cik_cp_compute_start(struct radeon_device *rdev) */ static void cik_cp_compute_fini(struct radeon_device *rdev) { + int i, idx, r; + cik_cp_compute_enable(rdev, false); - //todo + + for (i = 0; i < 2; i++) { + if (i == 0) + idx = CAYMAN_RING_TYPE_CP1_INDEX; + else + idx = CAYMAN_RING_TYPE_CP2_INDEX; + + if (rdev->ring[idx].mqd_obj) { + r = radeon_bo_reserve(rdev->ring[idx].mqd_obj, false); + if (unlikely(r != 0)) + dev_warn(rdev->dev, "(%d) reserve MQD bo failed\n", r); + + radeon_bo_unpin(rdev->ring[idx].mqd_obj); + radeon_bo_unreserve(rdev->ring[idx].mqd_obj); + + radeon_bo_unref(&rdev->ring[idx].mqd_obj); + rdev->ring[idx].mqd_obj = NULL; + } + } +} + +static void cik_mec_fini(struct radeon_device *rdev) +{ + int r; + + if (rdev->mec.hpd_eop_obj) { + r = radeon_bo_reserve(rdev->mec.hpd_eop_obj, false); + if (unlikely(r != 0)) + dev_warn(rdev->dev, "(%d) reserve HPD EOP bo failed\n", r); + radeon_bo_unpin(rdev->mec.hpd_eop_obj); + radeon_bo_unreserve(rdev->mec.hpd_eop_obj); + + radeon_bo_unref(&rdev->mec.hpd_eop_obj); + rdev->mec.hpd_eop_obj = NULL; + } +} + +#define MEC_HPD_SIZE 2048 + +static int cik_mec_init(struct radeon_device *rdev) +{ + int r; + u32 *hpd; + + /* + * KV: 2 MEC, 4 Pipes/MEC, 8 Queues/Pipe - 64 Queues total + * CI/KB: 1 MEC, 4 Pipes/MEC, 8 Queues/Pipe - 32 Queues total + */ + if (rdev->family == CHIP_KAVERI) + rdev->mec.num_mec = 2; + else + rdev->mec.num_mec = 1; + rdev->mec.num_pipe = 4; + rdev->mec.num_queue = rdev->mec.num_mec * rdev->mec.num_pipe * 8; + + if (rdev->mec.hpd_eop_obj == NULL) { + r = radeon_bo_create(rdev, + rdev->mec.num_mec *rdev->mec.num_pipe * MEC_HPD_SIZE * 2, + PAGE_SIZE, true, + RADEON_GEM_DOMAIN_GTT, NULL, + &rdev->mec.hpd_eop_obj); + if (r) { + dev_warn(rdev->dev, "(%d) create HDP EOP bo failed\n", r); + return r; + } + } + + r = radeon_bo_reserve(rdev->mec.hpd_eop_obj, false); + if (unlikely(r != 0)) { + cik_mec_fini(rdev); + return r; + } + r = radeon_bo_pin(rdev->mec.hpd_eop_obj, RADEON_GEM_DOMAIN_GTT, + &rdev->mec.hpd_eop_gpu_addr); + if (r) { + dev_warn(rdev->dev, "(%d) pin HDP EOP bo failed\n", r); + cik_mec_fini(rdev); + return r; + } + r = radeon_bo_kmap(rdev->mec.hpd_eop_obj, (void **)&hpd); + if (r) { + dev_warn(rdev->dev, "(%d) map HDP EOP bo failed\n", r); + cik_mec_fini(rdev); + return r; + } + + /* clear memory. Not sure if this is required or not */ + memset(hpd, 0, rdev->mec.num_mec *rdev->mec.num_pipe * MEC_HPD_SIZE * 2); + + radeon_bo_kunmap(rdev->mec.hpd_eop_obj); + radeon_bo_unreserve(rdev->mec.hpd_eop_obj); + + return 0; }
+struct hqd_registers +{ + u32 cp_mqd_base_addr; + u32 cp_mqd_base_addr_hi; + u32 cp_hqd_active; + u32 cp_hqd_vmid; + u32 cp_hqd_persistent_state; + u32 cp_hqd_pipe_priority; + u32 cp_hqd_queue_priority; + u32 cp_hqd_quantum; + u32 cp_hqd_pq_base; + u32 cp_hqd_pq_base_hi; + u32 cp_hqd_pq_rptr; + u32 cp_hqd_pq_rptr_report_addr; + u32 cp_hqd_pq_rptr_report_addr_hi; + u32 cp_hqd_pq_wptr_poll_addr; + u32 cp_hqd_pq_wptr_poll_addr_hi; + u32 cp_hqd_pq_doorbell_control; + u32 cp_hqd_pq_wptr; + u32 cp_hqd_pq_control; + u32 cp_hqd_ib_base_addr; + u32 cp_hqd_ib_base_addr_hi; + u32 cp_hqd_ib_rptr; + u32 cp_hqd_ib_control; + u32 cp_hqd_iq_timer; + u32 cp_hqd_iq_rptr; + u32 cp_hqd_dequeue_request; + u32 cp_hqd_dma_offload; + u32 cp_hqd_sema_cmd; + u32 cp_hqd_msg_type; + u32 cp_hqd_atomic0_preop_lo; + u32 cp_hqd_atomic0_preop_hi; + u32 cp_hqd_atomic1_preop_lo; + u32 cp_hqd_atomic1_preop_hi; + u32 cp_hqd_hq_scheduler0; + u32 cp_hqd_hq_scheduler1; + u32 cp_mqd_control; +}; + +struct bonaire_mqd +{ + u32 header; + u32 dispatch_initiator; + u32 dimensions[3]; + u32 start_idx[3]; + u32 num_threads[3]; + u32 pipeline_stat_enable; + u32 perf_counter_enable; + u32 pgm[2]; + u32 tba[2]; + u32 tma[2]; + u32 pgm_rsrc[2]; + u32 vmid; + u32 resource_limits; + u32 static_thread_mgmt01[2]; + u32 tmp_ring_size; + u32 static_thread_mgmt23[2]; + u32 restart[3]; + u32 thread_trace_enable; + u32 reserved1; + u32 user_data[16]; + u32 vgtcs_invoke_count[2]; + struct hqd_registers queue_state; + u32 dequeue_cntr; + u32 interrupt_queue[64]; +}; + /** * cik_cp_compute_resume - setup the compute queue registers * @@ -2205,24 +2413,247 @@ static void cik_cp_compute_fini(struct radeon_device *rdev) */ static int cik_cp_compute_resume(struct radeon_device *rdev) { - int r; + int r, i, idx; + u32 tmp; + bool use_doorbell = true; + u64 hqd_gpu_addr; + u64 mqd_gpu_addr; + u64 eop_gpu_addr; + u64 wb_gpu_addr; + u32 *buf; + struct bonaire_mqd *mqd;
- //todo r = cik_cp_compute_start(rdev); if (r) return r; + + /* fix up chicken bits */ + tmp = RREG32(CP_CPF_DEBUG); + tmp |= (1 << 23); + WREG32(CP_CPF_DEBUG, tmp); + + /* init the pipes */ + for (i = 0; i < (rdev->mec.num_pipe * rdev->mec.num_mec); i++) { + int me = (i < 4) ? 1 : 2; + int pipe = (i < 4) ? i : (i - 4); + + eop_gpu_addr = rdev->mec.hpd_eop_gpu_addr + (i * MEC_HPD_SIZE * 2); + + cik_srbm_select(rdev, me, pipe, 0, 0); + + /* write the EOP addr */ + WREG32(CP_HPD_EOP_BASE_ADDR, eop_gpu_addr >> 8); + WREG32(CP_HPD_EOP_BASE_ADDR_HI, upper_32_bits(eop_gpu_addr) >> 8); + + /* set the VMID assigned */ + WREG32(CP_HPD_EOP_VMID, 0); + + /* set the EOP size, register value is 2^(EOP_SIZE+1) dwords */ + tmp = RREG32(CP_HPD_EOP_CONTROL); + tmp &= ~EOP_SIZE_MASK; + tmp |= drm_order(MEC_HPD_SIZE / 8); + WREG32(CP_HPD_EOP_CONTROL, tmp); + } + cik_srbm_select(rdev, 0, 0, 0, 0); + + /* init the queues. Just two for now. */ + for (i = 0; i < 2; i++) { + if (i == 0) + idx = CAYMAN_RING_TYPE_CP1_INDEX; + else + idx = CAYMAN_RING_TYPE_CP2_INDEX; + + if (rdev->ring[idx].mqd_obj == NULL) { + r = radeon_bo_create(rdev, + sizeof(struct bonaire_mqd), + PAGE_SIZE, true, + RADEON_GEM_DOMAIN_GTT, NULL, + &rdev->ring[idx].mqd_obj); + if (r) { + dev_warn(rdev->dev, "(%d) create MQD bo failed\n", r); + return r; + } + } + + r = radeon_bo_reserve(rdev->ring[idx].mqd_obj, false); + if (unlikely(r != 0)) { + cik_cp_compute_fini(rdev); + return r; + } + r = radeon_bo_pin(rdev->ring[idx].mqd_obj, RADEON_GEM_DOMAIN_GTT, + &mqd_gpu_addr); + if (r) { + dev_warn(rdev->dev, "(%d) pin MQD bo failed\n", r); + cik_cp_compute_fini(rdev); + return r; + } + r = radeon_bo_kmap(rdev->ring[idx].mqd_obj, (void **)&buf); + if (r) { + dev_warn(rdev->dev, "(%d) map MQD bo failed\n", r); + cik_cp_compute_fini(rdev); + return r; + } + + /* doorbell offset */ + rdev->ring[idx].doorbell_offset = + (rdev->ring[idx].doorbell_page_num * PAGE_SIZE) + 0; + + /* init the mqd struct */ + memset(buf, 0, sizeof(struct bonaire_mqd)); + + mqd = (struct bonaire_mqd *)buf; + mqd->header = 0xC0310800; + mqd->static_thread_mgmt01[0] = 0xffffffff; + mqd->static_thread_mgmt01[1] = 0xffffffff; + mqd->static_thread_mgmt23[0] = 0xffffffff; + mqd->static_thread_mgmt23[1] = 0xffffffff; + + cik_srbm_select(rdev, rdev->ring[idx].me, + rdev->ring[idx].pipe, + rdev->ring[idx].queue, 0); + + /* disable wptr polling */ + tmp = RREG32(CP_PQ_WPTR_POLL_CNTL); + tmp &= ~WPTR_POLL_EN; + WREG32(CP_PQ_WPTR_POLL_CNTL, tmp); + + /* enable doorbell? */ + mqd->queue_state.cp_hqd_pq_doorbell_control = + RREG32(CP_HQD_PQ_DOORBELL_CONTROL); + if (use_doorbell) + mqd->queue_state.cp_hqd_pq_doorbell_control |= DOORBELL_EN; + else + mqd->queue_state.cp_hqd_pq_doorbell_control &= ~DOORBELL_EN; + WREG32(CP_HQD_PQ_DOORBELL_CONTROL, + mqd->queue_state.cp_hqd_pq_doorbell_control); + + /* disable the queue if it's active */ + mqd->queue_state.cp_hqd_dequeue_request = 0; + mqd->queue_state.cp_hqd_pq_rptr = 0; + mqd->queue_state.cp_hqd_pq_wptr= 0; + if (RREG32(CP_HQD_ACTIVE) & 1) { + WREG32(CP_HQD_DEQUEUE_REQUEST, 1); + for (i = 0; i < rdev->usec_timeout; i++) { + if (!(RREG32(CP_HQD_ACTIVE) & 1)) + break; + udelay(1); + } + WREG32(CP_HQD_DEQUEUE_REQUEST, mqd->queue_state.cp_hqd_dequeue_request); + WREG32(CP_HQD_PQ_RPTR, mqd->queue_state.cp_hqd_pq_rptr); + WREG32(CP_HQD_PQ_WPTR, mqd->queue_state.cp_hqd_pq_wptr); + } + + /* set the pointer to the MQD */ + mqd->queue_state.cp_mqd_base_addr = mqd_gpu_addr & 0xfffffffc; + mqd->queue_state.cp_mqd_base_addr_hi = upper_32_bits(mqd_gpu_addr); + WREG32(CP_MQD_BASE_ADDR, mqd->queue_state.cp_mqd_base_addr); + WREG32(CP_MQD_BASE_ADDR_HI, mqd->queue_state.cp_mqd_base_addr_hi); + /* set MQD vmid to 0 */ + mqd->queue_state.cp_mqd_control = RREG32(CP_MQD_CONTROL); + mqd->queue_state.cp_mqd_control &= ~MQD_VMID_MASK; + WREG32(CP_MQD_CONTROL, mqd->queue_state.cp_mqd_control); + + /* set the pointer to the HQD, this is similar CP_RB0_BASE/_HI */ + hqd_gpu_addr = rdev->ring[idx].gpu_addr >> 8; + mqd->queue_state.cp_hqd_pq_base = hqd_gpu_addr; + mqd->queue_state.cp_hqd_pq_base_hi = upper_32_bits(hqd_gpu_addr); + WREG32(CP_HQD_PQ_BASE, mqd->queue_state.cp_hqd_pq_base); + WREG32(CP_HQD_PQ_BASE_HI, mqd->queue_state.cp_hqd_pq_base_hi); + + /* set up the HQD, this is similar to CP_RB0_CNTL */ + mqd->queue_state.cp_hqd_pq_control = RREG32(CP_HQD_PQ_CONTROL); + mqd->queue_state.cp_hqd_pq_control &= + ~(QUEUE_SIZE_MASK | RPTR_BLOCK_SIZE_MASK); + + mqd->queue_state.cp_hqd_pq_control |= + drm_order(rdev->ring[idx].ring_size / 8); + mqd->queue_state.cp_hqd_pq_control |= + (drm_order(RADEON_GPU_PAGE_SIZE/8) << 8); +#ifdef __BIG_ENDIAN + mqd->queue_state.cp_hqd_pq_control |= BUF_SWAP_32BIT; +#endif + mqd->queue_state.cp_hqd_pq_control &= + ~(UNORD_DISPATCH | ROQ_PQ_IB_FLIP | PQ_VOLATILE); + mqd->queue_state.cp_hqd_pq_control |= + PRIV_STATE | KMD_QUEUE; /* assuming kernel queue control */ + WREG32(CP_HQD_PQ_CONTROL, mqd->queue_state.cp_hqd_pq_control); + + /* only used if CP_PQ_WPTR_POLL_CNTL.WPTR_POLL_EN=1 */ + if (i == 0) + wb_gpu_addr = rdev->wb.gpu_addr + CIK_WB_CP1_WPTR_OFFSET; + else + wb_gpu_addr = rdev->wb.gpu_addr + CIK_WB_CP2_WPTR_OFFSET; + mqd->queue_state.cp_hqd_pq_wptr_poll_addr = wb_gpu_addr & 0xfffffffc; + mqd->queue_state.cp_hqd_pq_wptr_poll_addr_hi = upper_32_bits(wb_gpu_addr) & 0xffff; + WREG32(CP_HQD_PQ_WPTR_POLL_ADDR, mqd->queue_state.cp_hqd_pq_wptr_poll_addr); + WREG32(CP_HQD_PQ_WPTR_POLL_ADDR_HI, + mqd->queue_state.cp_hqd_pq_wptr_poll_addr_hi); + + /* set the wb address wether it's enabled or not */ + if (i == 0) + wb_gpu_addr = rdev->wb.gpu_addr + RADEON_WB_CP1_RPTR_OFFSET; + else + wb_gpu_addr = rdev->wb.gpu_addr + RADEON_WB_CP2_RPTR_OFFSET; + mqd->queue_state.cp_hqd_pq_rptr_report_addr = wb_gpu_addr & 0xfffffffc; + mqd->queue_state.cp_hqd_pq_rptr_report_addr_hi = + upper_32_bits(wb_gpu_addr) & 0xffff; + WREG32(CP_HQD_PQ_RPTR_REPORT_ADDR, + mqd->queue_state.cp_hqd_pq_rptr_report_addr); + WREG32(CP_HQD_PQ_RPTR_REPORT_ADDR_HI, + mqd->queue_state.cp_hqd_pq_rptr_report_addr_hi); + + /* enable the doorbell if requested */ + if (use_doorbell) { + mqd->queue_state.cp_hqd_pq_doorbell_control = + RREG32(CP_HQD_PQ_DOORBELL_CONTROL); + mqd->queue_state.cp_hqd_pq_doorbell_control &= ~DOORBELL_OFFSET_MASK; + mqd->queue_state.cp_hqd_pq_doorbell_control |= + DOORBELL_OFFSET(rdev->ring[idx].doorbell_offset / 4); + mqd->queue_state.cp_hqd_pq_doorbell_control |= DOORBELL_EN; + mqd->queue_state.cp_hqd_pq_doorbell_control &= + ~(DOORBELL_SOURCE | DOORBELL_HIT); + + } else { + mqd->queue_state.cp_hqd_pq_doorbell_control = 0; + } + WREG32(CP_HQD_PQ_DOORBELL_CONTROL, + mqd->queue_state.cp_hqd_pq_doorbell_control); + + /* read and write pointers, similar to CP_RB0_WPTR/_RPTR */ + rdev->ring[idx].wptr = 0; + mqd->queue_state.cp_hqd_pq_wptr = rdev->ring[idx].wptr; + WREG32(CP_HQD_PQ_WPTR, mqd->queue_state.cp_hqd_pq_wptr); + rdev->ring[idx].rptr = RREG32(CP_HQD_PQ_RPTR); + mqd->queue_state.cp_hqd_pq_rptr = rdev->ring[idx].rptr; + + /* set the vmid for the queue */ + mqd->queue_state.cp_hqd_vmid = 0; + WREG32(CP_HQD_VMID, mqd->queue_state.cp_hqd_vmid); + + /* activate the queue */ + mqd->queue_state.cp_hqd_active = 1; + WREG32(CP_HQD_ACTIVE, mqd->queue_state.cp_hqd_active); + + cik_srbm_select(rdev, 0, 0, 0, 0); + + radeon_bo_kunmap(rdev->ring[idx].mqd_obj); + radeon_bo_unreserve(rdev->ring[idx].mqd_obj); + + rdev->ring[idx].ready = true; + r = radeon_ring_test(rdev, idx, &rdev->ring[idx]); + if (r) + rdev->ring[idx].ready = false; + } + return 0; }
-/* XXX temporary wrappers to handle both compute and gfx */ -/* XXX */ static void cik_cp_enable(struct radeon_device *rdev, bool enable) { cik_cp_gfx_enable(rdev, enable); cik_cp_compute_enable(rdev, enable); }
-/* XXX */ static int cik_cp_load_microcode(struct radeon_device *rdev) { int r; @@ -2237,14 +2668,12 @@ static int cik_cp_load_microcode(struct radeon_device *rdev) return 0; }
-/* XXX */ static void cik_cp_fini(struct radeon_device *rdev) { cik_cp_gfx_fini(rdev); cik_cp_compute_fini(rdev); }
-/* XXX */ static int cik_cp_resume(struct radeon_device *rdev) { int r; @@ -2865,6 +3294,22 @@ static void cik_print_gpu_status_regs(struct radeon_device *rdev) RREG32(SDMA0_STATUS_REG + SDMA0_REGISTER_OFFSET)); dev_info(rdev->dev, " SDMA1_STATUS_REG = 0x%08X\n", RREG32(SDMA0_STATUS_REG + SDMA1_REGISTER_OFFSET)); + dev_info(rdev->dev, " CP_STAT = 0x%08x\n", RREG32(CP_STAT)); + dev_info(rdev->dev, " CP_STALLED_STAT1 = 0x%08x\n", + RREG32(CP_STALLED_STAT1)); + dev_info(rdev->dev, " CP_STALLED_STAT2 = 0x%08x\n", + RREG32(CP_STALLED_STAT2)); + dev_info(rdev->dev, " CP_STALLED_STAT3 = 0x%08x\n", + RREG32(CP_STALLED_STAT3)); + dev_info(rdev->dev, " CP_CPF_BUSY_STAT = 0x%08x\n", + RREG32(CP_CPF_BUSY_STAT)); + dev_info(rdev->dev, " CP_CPF_STALLED_STAT1 = 0x%08x\n", + RREG32(CP_CPF_STALLED_STAT1)); + dev_info(rdev->dev, " CP_CPF_STATUS = 0x%08x\n", RREG32(CP_CPF_STATUS)); + dev_info(rdev->dev, " CP_CPC_BUSY_STAT = 0x%08x\n", RREG32(CP_CPC_BUSY_STAT)); + dev_info(rdev->dev, " CP_CPC_STALLED_STAT1 = 0x%08x\n", + RREG32(CP_CPC_STALLED_STAT1)); + dev_info(rdev->dev, " CP_CPC_STATUS = 0x%08x\n", RREG32(CP_CPC_STATUS)); }
/** @@ -4952,12 +5397,31 @@ static int cik_startup(struct radeon_device *rdev) if (r) return r;
+ /* allocate rlc buffers */ + r = cik_mec_init(rdev); + if (r) { + DRM_ERROR("Failed to init MEC BOs!\n"); + return r; + } + r = radeon_fence_driver_start_ring(rdev, RADEON_RING_TYPE_GFX_INDEX); if (r) { dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r); return r; }
+ r = radeon_fence_driver_start_ring(rdev, CAYMAN_RING_TYPE_CP1_INDEX); + if (r) { + dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r); + return r; + } + + r = radeon_fence_driver_start_ring(rdev, CAYMAN_RING_TYPE_CP2_INDEX); + if (r) { + dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r); + return r; + } + r = radeon_fence_driver_start_ring(rdev, R600_RING_TYPE_DMA_INDEX); if (r) { dev_err(rdev->dev, "failed initializing DMA fences (%d).\n", r); @@ -5002,6 +5466,36 @@ static int cik_startup(struct radeon_device *rdev) if (r) return r;
+ /* set up the compute queues */ + ring = &rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX]; + r = radeon_ring_init(rdev, ring, ring->ring_size, RADEON_WB_CP1_RPTR_OFFSET, + CP_HQD_PQ_RPTR, CP_HQD_PQ_WPTR, + 0, 0xfffff, RADEON_CP_PACKET2); + if (r) + return r; + ring->me = 1; /* first MEC */ + ring->pipe = 0; /* first pipe */ + ring->queue = 0; /* first queue */ + ring->wptr_offs = CIK_WB_CP1_WPTR_OFFSET; + ring->funcs.get_rptr = &cik_compute_ring_get_rptr; + ring->funcs.get_wptr = &cik_compute_ring_get_wptr; + ring->funcs.set_wptr = &cik_compute_ring_set_wptr; + + ring = &rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX]; + r = radeon_ring_init(rdev, ring, ring->ring_size, RADEON_WB_CP2_RPTR_OFFSET, + CP_HQD_PQ_RPTR, CP_HQD_PQ_WPTR, + 0, 0xffffffff, RADEON_CP_PACKET2); + if (r) + return r; + /* dGPU only have 1 MEC */ + ring->me = 1; /* first MEC */ + ring->pipe = 0; /* first pipe */ + ring->queue = 1; /* second queue */ + ring->wptr_offs = CIK_WB_CP2_WPTR_OFFSET; + ring->funcs.get_rptr = &cik_compute_ring_get_rptr; + ring->funcs.get_wptr = &cik_compute_ring_get_wptr; + ring->funcs.set_wptr = &cik_compute_ring_set_wptr; + ring = &rdev->ring[R600_RING_TYPE_DMA_INDEX]; r = radeon_ring_init(rdev, ring, ring->ring_size, R600_WB_DMA_RPTR_OFFSET, SDMA0_GFX_RB_RPTR + SDMA0_REGISTER_OFFSET, @@ -5176,6 +5670,20 @@ int cik_init(struct radeon_device *rdev) ring->ring_obj = NULL; r600_ring_init(rdev, ring, 1024 * 1024);
+ ring = &rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX]; + ring->ring_obj = NULL; + r600_ring_init(rdev, ring, 1024 * 1024); + r = radeon_doorbell_get(rdev, &ring->doorbell_page_num); + if (r) + return r; + + ring = &rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX]; + ring->ring_obj = NULL; + r600_ring_init(rdev, ring, 1024 * 1024); + r = radeon_doorbell_get(rdev, &ring->doorbell_page_num); + if (r) + return r; + ring = &rdev->ring[R600_RING_TYPE_DMA_INDEX]; ring->ring_obj = NULL; r600_ring_init(rdev, ring, 256 * 1024); @@ -5206,6 +5714,7 @@ int cik_init(struct radeon_device *rdev) cik_sdma_fini(rdev); cik_irq_fini(rdev); si_rlc_fini(rdev); + cik_mec_fini(rdev); radeon_wb_fini(rdev); radeon_ib_pool_fini(rdev); radeon_vm_manager_fini(rdev); @@ -5241,6 +5750,7 @@ void cik_fini(struct radeon_device *rdev) cik_sdma_fini(rdev); cik_irq_fini(rdev); si_rlc_fini(rdev); + cik_mec_fini(rdev); radeon_wb_fini(rdev); radeon_vm_manager_fini(rdev); radeon_ib_pool_fini(rdev); diff --git a/drivers/gpu/drm/radeon/cikd.h b/drivers/gpu/drm/radeon/cikd.h index 79be39e..63514b9 100644 --- a/drivers/gpu/drm/radeon/cikd.h +++ b/drivers/gpu/drm/radeon/cikd.h @@ -460,6 +460,13 @@ # define RDERR_INT_ENABLE (1 << 0) # define GUI_IDLE_INT_ENABLE (1 << 19)
+#define CP_CPC_STATUS 0x8210 +#define CP_CPC_BUSY_STAT 0x8214 +#define CP_CPC_STALLED_STAT1 0x8218 +#define CP_CPF_STATUS 0x821c +#define CP_CPF_BUSY_STAT 0x8220 +#define CP_CPF_STALLED_STAT1 0x8224 + #define CP_MEC_CNTL 0x8234 #define MEC_ME2_HALT (1 << 28) #define MEC_ME1_HALT (1 << 30) @@ -468,6 +475,12 @@ #define MEC_ME2_HALT (1 << 28) #define MEC_ME1_HALT (1 << 30)
+#define CP_STALLED_STAT3 0x8670 +#define CP_STALLED_STAT1 0x8674 +#define CP_STALLED_STAT2 0x8678 + +#define CP_STAT 0x8680 + #define CP_ME_CNTL 0x86D8 #define CP_CE_HALT (1 << 24) #define CP_PFP_HALT (1 << 26) @@ -701,6 +714,11 @@ # define CP_RINGID1_INT_STAT (1 << 30) # define CP_RINGID0_INT_STAT (1 << 31)
+#define CP_CPF_DEBUG 0xC200 + +#define CP_PQ_WPTR_POLL_CNTL 0xC20C +#define WPTR_POLL_EN (1 << 31) + #define CP_ME1_PIPE0_INT_CNTL 0xC214 #define CP_ME1_PIPE1_INT_CNTL 0xC218 #define CP_ME1_PIPE2_INT_CNTL 0xC21C @@ -773,6 +791,50 @@ #define RLC_GPM_SCRATCH_ADDR 0xC4B0 #define RLC_GPM_SCRATCH_DATA 0xC4B4
+#define CP_HPD_EOP_BASE_ADDR 0xC904 +#define CP_HPD_EOP_BASE_ADDR_HI 0xC908 +#define CP_HPD_EOP_VMID 0xC90C +#define CP_HPD_EOP_CONTROL 0xC910 +#define EOP_SIZE(x) ((x) << 0) +#define EOP_SIZE_MASK (0x3f << 0) +#define CP_MQD_BASE_ADDR 0xC914 +#define CP_MQD_BASE_ADDR_HI 0xC918 +#define CP_HQD_ACTIVE 0xC91C +#define CP_HQD_VMID 0xC920 + +#define CP_HQD_PQ_BASE 0xC934 +#define CP_HQD_PQ_BASE_HI 0xC938 +#define CP_HQD_PQ_RPTR 0xC93C +#define CP_HQD_PQ_RPTR_REPORT_ADDR 0xC940 +#define CP_HQD_PQ_RPTR_REPORT_ADDR_HI 0xC944 +#define CP_HQD_PQ_WPTR_POLL_ADDR 0xC948 +#define CP_HQD_PQ_WPTR_POLL_ADDR_HI 0xC94C +#define CP_HQD_PQ_DOORBELL_CONTROL 0xC950 +#define DOORBELL_OFFSET(x) ((x) << 2) +#define DOORBELL_OFFSET_MASK (0x1fffff << 2) +#define DOORBELL_SOURCE (1 << 28) +#define DOORBELL_SCHD_HIT (1 << 29) +#define DOORBELL_EN (1 << 30) +#define DOORBELL_HIT (1 << 31) +#define CP_HQD_PQ_WPTR 0xC954 +#define CP_HQD_PQ_CONTROL 0xC958 +#define QUEUE_SIZE(x) ((x) << 0) +#define QUEUE_SIZE_MASK (0x3f << 0) +#define RPTR_BLOCK_SIZE(x) ((x) << 8) +#define RPTR_BLOCK_SIZE_MASK (0x3f << 8) +#define PQ_VOLATILE (1 << 26) +#define NO_UPDATE_RPTR (1 << 27) +#define UNORD_DISPATCH (1 << 28) +#define ROQ_PQ_IB_FLIP (1 << 29) +#define PRIV_STATE (1 << 30) +#define KMD_QUEUE (1 << 31) + +#define CP_HQD_DEQUEUE_REQUEST 0xC974 + +#define CP_MQD_CONTROL 0xC99C +#define MQD_VMID(x) ((x) << 0) +#define MQD_VMID_MASK (0xf << 0) + #define PA_SC_RASTER_CONFIG 0x28350 # define RASTER_CONFIG_RB_MAP_0 0 # define RASTER_CONFIG_RB_MAP_1 1 diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index a2a3430..d40d506 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -714,6 +714,22 @@ struct radeon_ring { u32 (*get_wptr)(struct radeon_device *rdev, struct radeon_ring *ring); void (*set_wptr)(struct radeon_device *rdev, struct radeon_ring *ring); } funcs; + /* for CIK queues */ + u32 me; + u32 pipe; + u32 queue; + struct radeon_bo *mqd_obj; + u32 doorbell_page_num; + u32 doorbell_offset; + unsigned wptr_offs; +}; + +struct radeon_mec { + struct radeon_bo *hpd_eop_obj; + u64 hpd_eop_gpu_addr; + u32 num_pipe; + u32 num_mec; + u32 num_queue; };
/* @@ -971,6 +987,8 @@ struct radeon_wb { #define CAYMAN_WB_DMA1_RPTR_OFFSET 2304 #define R600_WB_UVD_RPTR_OFFSET 2560 #define R600_WB_EVENT_OFFSET 3072 +#define CIK_WB_CP1_WPTR_OFFSET 3328 +#define CIK_WB_CP2_WPTR_OFFSET 3584
/** * struct radeon_pm - power management datas @@ -1760,6 +1778,7 @@ struct radeon_device { int msi_enabled; /* msi enabled */ struct r600_ih ih; /* r6/700 interrupt ring */ struct si_rlc rlc; + struct radeon_mec mec; struct work_struct hotplug_work; struct work_struct audio_work; struct work_struct reset_work; diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c index cf71734..7e265a5 100644 --- a/drivers/gpu/drm/radeon/radeon_cs.c +++ b/drivers/gpu/drm/radeon/radeon_cs.c @@ -121,9 +121,7 @@ static int radeon_cs_get_ring(struct radeon_cs_parser *p, u32 ring, s32 priority p->ring = RADEON_RING_TYPE_GFX_INDEX; break; case RADEON_CS_RING_COMPUTE: - if (p->rdev->family >= CHIP_BONAIRE) - p->ring = RADEON_RING_TYPE_GFX_INDEX; - else if (p->rdev->family >= CHIP_TAHITI) { + if (p->rdev->family >= CHIP_TAHITI) { if (p->priority > 0) p->ring = CAYMAN_RING_TYPE_CP1_INDEX; else
On Wed, Jun 26, 2013 at 09:22:12AM -0400, alexdeucher@gmail.com wrote:
From: Alex Deucher alexander.deucher@amd.com
On CIK, the compute rings work slightly differently than on previous asics, however the basic concepts are the same.
The main differences:
- New MEC engines for compute queues
- Multiple queues per MEC:
- CI/KB: 1 MEC, 4 pipes per MEC, 8 queues per pipe = 32 queues
- KV: 2 MEC, 4 pipes per MEC, 8 queues per pipe = 64 queues
- Queues can be allocated and scheduled by another queue
- New doorbell aperture allows you to assign space in the aperture for the wptr which allows for userspace access to queues
v2: add wptr shadow, fix eop setup
Signed-off-by: Alex Deucher alexander.deucher@amd.com
Minor nitpick below otherwise
Reviewed-by: Jerome Glisse jglisse@redhat.com
drivers/gpu/drm/radeon/cik.c | 528 +++++++++++++++++++++++++++++++++++- drivers/gpu/drm/radeon/cikd.h | 62 +++++ drivers/gpu/drm/radeon/radeon.h | 19 ++ drivers/gpu/drm/radeon/radeon_cs.c | 4 +- 4 files changed, 601 insertions(+), 12 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 5c28fa5..9d2d6bb 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -1687,6 +1687,7 @@ int cik_ring_test(struct radeon_device *rdev, struct radeon_ring *ring) radeon_ring_write(ring, ((scratch - PACKET3_SET_UCONFIG_REG_START) >> 2)); radeon_ring_write(ring, 0xDEADBEEF); radeon_ring_unlock_commit(rdev, ring);
- for (i = 0; i < rdev->usec_timeout; i++) { tmp = RREG32(scratch); if (tmp == 0xDEADBEEF)
@@ -2112,6 +2113,51 @@ static int cik_cp_gfx_resume(struct radeon_device *rdev) return 0; }
+static u32 cik_compute_ring_get_rptr(struct radeon_device *rdev,
struct radeon_ring *ring)
+{
- u32 rptr;
- if (rdev->wb.enabled) {
rptr = le32_to_cpu(rdev->wb.wb[ring->rptr_offs/4]);
- } else {
cik_srbm_select(rdev, ring->me, ring->pipe, ring->queue, 0);
rptr = RREG32(CP_HQD_PQ_RPTR);
cik_srbm_select(rdev, 0, 0, 0, 0);
- }
- rptr = (rptr & ring->ptr_reg_mask) >> ring->ptr_reg_shift;
- return rptr;
+}
+static u32 cik_compute_ring_get_wptr(struct radeon_device *rdev,
struct radeon_ring *ring)
+{
- u32 wptr;
- if (rdev->wb.enabled) {
wptr = le32_to_cpu(rdev->wb.wb[ring->wptr_offs/4]);
- } else {
cik_srbm_select(rdev, ring->me, ring->pipe, ring->queue, 0);
wptr = RREG32(CP_HQD_PQ_WPTR);
cik_srbm_select(rdev, 0, 0, 0, 0);
- }
- wptr = (wptr & ring->ptr_reg_mask) >> ring->ptr_reg_shift;
- return wptr;
+}
+static void cik_compute_ring_set_wptr(struct radeon_device *rdev,
struct radeon_ring *ring)
+{
- u32 wptr = (ring->wptr << ring->ptr_reg_shift) & ring->ptr_reg_mask;
- rdev->wb.wb[ring->wptr_offs/4] = cpu_to_le32(wptr);
- WDOORBELL32(ring->doorbell_offset, wptr);
+}
/**
- cik_cp_compute_enable - enable/disable the compute CP MEs
@@ -2176,7 +2222,8 @@ static int cik_cp_compute_load_microcode(struct radeon_device *rdev) */ static int cik_cp_compute_start(struct radeon_device *rdev) {
- //todo
- cik_cp_compute_enable(rdev, true);
- return 0;
}
@@ -2190,10 +2237,171 @@ static int cik_cp_compute_start(struct radeon_device *rdev) */ static void cik_cp_compute_fini(struct radeon_device *rdev) {
- int i, idx, r;
- cik_cp_compute_enable(rdev, false);
- //todo
- for (i = 0; i < 2; i++) {
if (i == 0)
idx = CAYMAN_RING_TYPE_CP1_INDEX;
else
idx = CAYMAN_RING_TYPE_CP2_INDEX;
if (rdev->ring[idx].mqd_obj) {
r = radeon_bo_reserve(rdev->ring[idx].mqd_obj, false);
if (unlikely(r != 0))
dev_warn(rdev->dev, "(%d) reserve MQD bo failed\n", r);
radeon_bo_unpin(rdev->ring[idx].mqd_obj);
radeon_bo_unreserve(rdev->ring[idx].mqd_obj);
radeon_bo_unref(&rdev->ring[idx].mqd_obj);
rdev->ring[idx].mqd_obj = NULL;
}
- }
+}
+static void cik_mec_fini(struct radeon_device *rdev) +{
- int r;
- if (rdev->mec.hpd_eop_obj) {
r = radeon_bo_reserve(rdev->mec.hpd_eop_obj, false);
if (unlikely(r != 0))
dev_warn(rdev->dev, "(%d) reserve HPD EOP bo failed\n", r);
radeon_bo_unpin(rdev->mec.hpd_eop_obj);
radeon_bo_unreserve(rdev->mec.hpd_eop_obj);
radeon_bo_unref(&rdev->mec.hpd_eop_obj);
rdev->mec.hpd_eop_obj = NULL;
- }
+}
+#define MEC_HPD_SIZE 2048
+static int cik_mec_init(struct radeon_device *rdev) +{
- int r;
- u32 *hpd;
- /*
* KV: 2 MEC, 4 Pipes/MEC, 8 Queues/Pipe - 64 Queues total
* CI/KB: 1 MEC, 4 Pipes/MEC, 8 Queues/Pipe - 32 Queues total
*/
- if (rdev->family == CHIP_KAVERI)
rdev->mec.num_mec = 2;
- else
rdev->mec.num_mec = 1;
- rdev->mec.num_pipe = 4;
- rdev->mec.num_queue = rdev->mec.num_mec * rdev->mec.num_pipe * 8;
- if (rdev->mec.hpd_eop_obj == NULL) {
r = radeon_bo_create(rdev,
rdev->mec.num_mec *rdev->mec.num_pipe * MEC_HPD_SIZE * 2,
PAGE_SIZE, true,
RADEON_GEM_DOMAIN_GTT, NULL,
&rdev->mec.hpd_eop_obj);
if (r) {
dev_warn(rdev->dev, "(%d) create HDP EOP bo failed\n", r);
return r;
}
- }
- r = radeon_bo_reserve(rdev->mec.hpd_eop_obj, false);
- if (unlikely(r != 0)) {
cik_mec_fini(rdev);
return r;
- }
- r = radeon_bo_pin(rdev->mec.hpd_eop_obj, RADEON_GEM_DOMAIN_GTT,
&rdev->mec.hpd_eop_gpu_addr);
- if (r) {
dev_warn(rdev->dev, "(%d) pin HDP EOP bo failed\n", r);
cik_mec_fini(rdev);
return r;
- }
- r = radeon_bo_kmap(rdev->mec.hpd_eop_obj, (void **)&hpd);
- if (r) {
dev_warn(rdev->dev, "(%d) map HDP EOP bo failed\n", r);
cik_mec_fini(rdev);
return r;
- }
- /* clear memory. Not sure if this is required or not */
- memset(hpd, 0, rdev->mec.num_mec *rdev->mec.num_pipe * MEC_HPD_SIZE * 2);
- radeon_bo_kunmap(rdev->mec.hpd_eop_obj);
- radeon_bo_unreserve(rdev->mec.hpd_eop_obj);
- return 0;
}
+struct hqd_registers +{
- u32 cp_mqd_base_addr;
- u32 cp_mqd_base_addr_hi;
- u32 cp_hqd_active;
- u32 cp_hqd_vmid;
- u32 cp_hqd_persistent_state;
- u32 cp_hqd_pipe_priority;
- u32 cp_hqd_queue_priority;
- u32 cp_hqd_quantum;
- u32 cp_hqd_pq_base;
- u32 cp_hqd_pq_base_hi;
- u32 cp_hqd_pq_rptr;
- u32 cp_hqd_pq_rptr_report_addr;
- u32 cp_hqd_pq_rptr_report_addr_hi;
- u32 cp_hqd_pq_wptr_poll_addr;
- u32 cp_hqd_pq_wptr_poll_addr_hi;
- u32 cp_hqd_pq_doorbell_control;
- u32 cp_hqd_pq_wptr;
- u32 cp_hqd_pq_control;
- u32 cp_hqd_ib_base_addr;
- u32 cp_hqd_ib_base_addr_hi;
- u32 cp_hqd_ib_rptr;
- u32 cp_hqd_ib_control;
- u32 cp_hqd_iq_timer;
- u32 cp_hqd_iq_rptr;
- u32 cp_hqd_dequeue_request;
- u32 cp_hqd_dma_offload;
- u32 cp_hqd_sema_cmd;
- u32 cp_hqd_msg_type;
- u32 cp_hqd_atomic0_preop_lo;
- u32 cp_hqd_atomic0_preop_hi;
- u32 cp_hqd_atomic1_preop_lo;
- u32 cp_hqd_atomic1_preop_hi;
- u32 cp_hqd_hq_scheduler0;
- u32 cp_hqd_hq_scheduler1;
- u32 cp_mqd_control;
+};
+struct bonaire_mqd +{
- u32 header;
- u32 dispatch_initiator;
- u32 dimensions[3];
- u32 start_idx[3];
- u32 num_threads[3];
- u32 pipeline_stat_enable;
- u32 perf_counter_enable;
- u32 pgm[2];
- u32 tba[2];
- u32 tma[2];
- u32 pgm_rsrc[2];
- u32 vmid;
- u32 resource_limits;
- u32 static_thread_mgmt01[2];
- u32 tmp_ring_size;
- u32 static_thread_mgmt23[2];
- u32 restart[3];
- u32 thread_trace_enable;
- u32 reserved1;
- u32 user_data[16];
- u32 vgtcs_invoke_count[2];
- struct hqd_registers queue_state;
- u32 dequeue_cntr;
- u32 interrupt_queue[64];
+};
/**
- cik_cp_compute_resume - setup the compute queue registers
@@ -2205,24 +2413,247 @@ static void cik_cp_compute_fini(struct radeon_device *rdev) */ static int cik_cp_compute_resume(struct radeon_device *rdev) {
- int r;
- int r, i, idx;
- u32 tmp;
- bool use_doorbell = true;
- u64 hqd_gpu_addr;
- u64 mqd_gpu_addr;
- u64 eop_gpu_addr;
- u64 wb_gpu_addr;
- u32 *buf;
- struct bonaire_mqd *mqd;
- //todo r = cik_cp_compute_start(rdev); if (r) return r;
- /* fix up chicken bits */
- tmp = RREG32(CP_CPF_DEBUG);
- tmp |= (1 << 23);
What is this chicken bit ? Is it tasty ?
- WREG32(CP_CPF_DEBUG, tmp);
- /* init the pipes */
- for (i = 0; i < (rdev->mec.num_pipe * rdev->mec.num_mec); i++) {
int me = (i < 4) ? 1 : 2;
int pipe = (i < 4) ? i : (i - 4);
eop_gpu_addr = rdev->mec.hpd_eop_gpu_addr + (i * MEC_HPD_SIZE * 2);
cik_srbm_select(rdev, me, pipe, 0, 0);
/* write the EOP addr */
WREG32(CP_HPD_EOP_BASE_ADDR, eop_gpu_addr >> 8);
WREG32(CP_HPD_EOP_BASE_ADDR_HI, upper_32_bits(eop_gpu_addr) >> 8);
/* set the VMID assigned */
WREG32(CP_HPD_EOP_VMID, 0);
/* set the EOP size, register value is 2^(EOP_SIZE+1) dwords */
tmp = RREG32(CP_HPD_EOP_CONTROL);
tmp &= ~EOP_SIZE_MASK;
tmp |= drm_order(MEC_HPD_SIZE / 8);
WREG32(CP_HPD_EOP_CONTROL, tmp);
- }
- cik_srbm_select(rdev, 0, 0, 0, 0);
- /* init the queues. Just two for now. */
- for (i = 0; i < 2; i++) {
if (i == 0)
idx = CAYMAN_RING_TYPE_CP1_INDEX;
else
idx = CAYMAN_RING_TYPE_CP2_INDEX;
if (rdev->ring[idx].mqd_obj == NULL) {
r = radeon_bo_create(rdev,
sizeof(struct bonaire_mqd),
PAGE_SIZE, true,
RADEON_GEM_DOMAIN_GTT, NULL,
&rdev->ring[idx].mqd_obj);
if (r) {
dev_warn(rdev->dev, "(%d) create MQD bo failed\n", r);
return r;
}
}
r = radeon_bo_reserve(rdev->ring[idx].mqd_obj, false);
if (unlikely(r != 0)) {
cik_cp_compute_fini(rdev);
return r;
}
r = radeon_bo_pin(rdev->ring[idx].mqd_obj, RADEON_GEM_DOMAIN_GTT,
&mqd_gpu_addr);
if (r) {
dev_warn(rdev->dev, "(%d) pin MQD bo failed\n", r);
cik_cp_compute_fini(rdev);
return r;
}
r = radeon_bo_kmap(rdev->ring[idx].mqd_obj, (void **)&buf);
if (r) {
dev_warn(rdev->dev, "(%d) map MQD bo failed\n", r);
cik_cp_compute_fini(rdev);
return r;
}
/* doorbell offset */
rdev->ring[idx].doorbell_offset =
(rdev->ring[idx].doorbell_page_num * PAGE_SIZE) + 0;
/* init the mqd struct */
memset(buf, 0, sizeof(struct bonaire_mqd));
mqd = (struct bonaire_mqd *)buf;
mqd->header = 0xC0310800;
mqd->static_thread_mgmt01[0] = 0xffffffff;
mqd->static_thread_mgmt01[1] = 0xffffffff;
mqd->static_thread_mgmt23[0] = 0xffffffff;
mqd->static_thread_mgmt23[1] = 0xffffffff;
cik_srbm_select(rdev, rdev->ring[idx].me,
rdev->ring[idx].pipe,
rdev->ring[idx].queue, 0);
/* disable wptr polling */
tmp = RREG32(CP_PQ_WPTR_POLL_CNTL);
tmp &= ~WPTR_POLL_EN;
WREG32(CP_PQ_WPTR_POLL_CNTL, tmp);
/* enable doorbell? */
mqd->queue_state.cp_hqd_pq_doorbell_control =
RREG32(CP_HQD_PQ_DOORBELL_CONTROL);
if (use_doorbell)
mqd->queue_state.cp_hqd_pq_doorbell_control |= DOORBELL_EN;
else
mqd->queue_state.cp_hqd_pq_doorbell_control &= ~DOORBELL_EN;
WREG32(CP_HQD_PQ_DOORBELL_CONTROL,
mqd->queue_state.cp_hqd_pq_doorbell_control);
/* disable the queue if it's active */
mqd->queue_state.cp_hqd_dequeue_request = 0;
mqd->queue_state.cp_hqd_pq_rptr = 0;
mqd->queue_state.cp_hqd_pq_wptr= 0;
if (RREG32(CP_HQD_ACTIVE) & 1) {
WREG32(CP_HQD_DEQUEUE_REQUEST, 1);
for (i = 0; i < rdev->usec_timeout; i++) {
if (!(RREG32(CP_HQD_ACTIVE) & 1))
break;
udelay(1);
}
WREG32(CP_HQD_DEQUEUE_REQUEST, mqd->queue_state.cp_hqd_dequeue_request);
WREG32(CP_HQD_PQ_RPTR, mqd->queue_state.cp_hqd_pq_rptr);
WREG32(CP_HQD_PQ_WPTR, mqd->queue_state.cp_hqd_pq_wptr);
}
/* set the pointer to the MQD */
mqd->queue_state.cp_mqd_base_addr = mqd_gpu_addr & 0xfffffffc;
mqd->queue_state.cp_mqd_base_addr_hi = upper_32_bits(mqd_gpu_addr);
WREG32(CP_MQD_BASE_ADDR, mqd->queue_state.cp_mqd_base_addr);
WREG32(CP_MQD_BASE_ADDR_HI, mqd->queue_state.cp_mqd_base_addr_hi);
/* set MQD vmid to 0 */
mqd->queue_state.cp_mqd_control = RREG32(CP_MQD_CONTROL);
mqd->queue_state.cp_mqd_control &= ~MQD_VMID_MASK;
WREG32(CP_MQD_CONTROL, mqd->queue_state.cp_mqd_control);
/* set the pointer to the HQD, this is similar CP_RB0_BASE/_HI */
hqd_gpu_addr = rdev->ring[idx].gpu_addr >> 8;
mqd->queue_state.cp_hqd_pq_base = hqd_gpu_addr;
mqd->queue_state.cp_hqd_pq_base_hi = upper_32_bits(hqd_gpu_addr);
WREG32(CP_HQD_PQ_BASE, mqd->queue_state.cp_hqd_pq_base);
WREG32(CP_HQD_PQ_BASE_HI, mqd->queue_state.cp_hqd_pq_base_hi);
/* set up the HQD, this is similar to CP_RB0_CNTL */
mqd->queue_state.cp_hqd_pq_control = RREG32(CP_HQD_PQ_CONTROL);
mqd->queue_state.cp_hqd_pq_control &=
~(QUEUE_SIZE_MASK | RPTR_BLOCK_SIZE_MASK);
mqd->queue_state.cp_hqd_pq_control |=
drm_order(rdev->ring[idx].ring_size / 8);
mqd->queue_state.cp_hqd_pq_control |=
(drm_order(RADEON_GPU_PAGE_SIZE/8) << 8);
+#ifdef __BIG_ENDIAN
mqd->queue_state.cp_hqd_pq_control |= BUF_SWAP_32BIT;
+#endif
mqd->queue_state.cp_hqd_pq_control &=
~(UNORD_DISPATCH | ROQ_PQ_IB_FLIP | PQ_VOLATILE);
mqd->queue_state.cp_hqd_pq_control |=
PRIV_STATE | KMD_QUEUE; /* assuming kernel queue control */
WREG32(CP_HQD_PQ_CONTROL, mqd->queue_state.cp_hqd_pq_control);
/* only used if CP_PQ_WPTR_POLL_CNTL.WPTR_POLL_EN=1 */
if (i == 0)
wb_gpu_addr = rdev->wb.gpu_addr + CIK_WB_CP1_WPTR_OFFSET;
else
wb_gpu_addr = rdev->wb.gpu_addr + CIK_WB_CP2_WPTR_OFFSET;
mqd->queue_state.cp_hqd_pq_wptr_poll_addr = wb_gpu_addr & 0xfffffffc;
mqd->queue_state.cp_hqd_pq_wptr_poll_addr_hi = upper_32_bits(wb_gpu_addr) & 0xffff;
WREG32(CP_HQD_PQ_WPTR_POLL_ADDR, mqd->queue_state.cp_hqd_pq_wptr_poll_addr);
WREG32(CP_HQD_PQ_WPTR_POLL_ADDR_HI,
mqd->queue_state.cp_hqd_pq_wptr_poll_addr_hi);
/* set the wb address wether it's enabled or not */
if (i == 0)
wb_gpu_addr = rdev->wb.gpu_addr + RADEON_WB_CP1_RPTR_OFFSET;
else
wb_gpu_addr = rdev->wb.gpu_addr + RADEON_WB_CP2_RPTR_OFFSET;
mqd->queue_state.cp_hqd_pq_rptr_report_addr = wb_gpu_addr & 0xfffffffc;
mqd->queue_state.cp_hqd_pq_rptr_report_addr_hi =
upper_32_bits(wb_gpu_addr) & 0xffff;
WREG32(CP_HQD_PQ_RPTR_REPORT_ADDR,
mqd->queue_state.cp_hqd_pq_rptr_report_addr);
WREG32(CP_HQD_PQ_RPTR_REPORT_ADDR_HI,
mqd->queue_state.cp_hqd_pq_rptr_report_addr_hi);
/* enable the doorbell if requested */
if (use_doorbell) {
mqd->queue_state.cp_hqd_pq_doorbell_control =
RREG32(CP_HQD_PQ_DOORBELL_CONTROL);
mqd->queue_state.cp_hqd_pq_doorbell_control &= ~DOORBELL_OFFSET_MASK;
mqd->queue_state.cp_hqd_pq_doorbell_control |=
DOORBELL_OFFSET(rdev->ring[idx].doorbell_offset / 4);
mqd->queue_state.cp_hqd_pq_doorbell_control |= DOORBELL_EN;
mqd->queue_state.cp_hqd_pq_doorbell_control &=
~(DOORBELL_SOURCE | DOORBELL_HIT);
} else {
mqd->queue_state.cp_hqd_pq_doorbell_control = 0;
}
WREG32(CP_HQD_PQ_DOORBELL_CONTROL,
mqd->queue_state.cp_hqd_pq_doorbell_control);
/* read and write pointers, similar to CP_RB0_WPTR/_RPTR */
rdev->ring[idx].wptr = 0;
mqd->queue_state.cp_hqd_pq_wptr = rdev->ring[idx].wptr;
WREG32(CP_HQD_PQ_WPTR, mqd->queue_state.cp_hqd_pq_wptr);
rdev->ring[idx].rptr = RREG32(CP_HQD_PQ_RPTR);
mqd->queue_state.cp_hqd_pq_rptr = rdev->ring[idx].rptr;
/* set the vmid for the queue */
mqd->queue_state.cp_hqd_vmid = 0;
WREG32(CP_HQD_VMID, mqd->queue_state.cp_hqd_vmid);
/* activate the queue */
mqd->queue_state.cp_hqd_active = 1;
WREG32(CP_HQD_ACTIVE, mqd->queue_state.cp_hqd_active);
cik_srbm_select(rdev, 0, 0, 0, 0);
radeon_bo_kunmap(rdev->ring[idx].mqd_obj);
radeon_bo_unreserve(rdev->ring[idx].mqd_obj);
rdev->ring[idx].ready = true;
r = radeon_ring_test(rdev, idx, &rdev->ring[idx]);
if (r)
rdev->ring[idx].ready = false;
- }
- return 0;
}
-/* XXX temporary wrappers to handle both compute and gfx */ -/* XXX */ static void cik_cp_enable(struct radeon_device *rdev, bool enable) { cik_cp_gfx_enable(rdev, enable); cik_cp_compute_enable(rdev, enable); }
-/* XXX */ static int cik_cp_load_microcode(struct radeon_device *rdev) { int r; @@ -2237,14 +2668,12 @@ static int cik_cp_load_microcode(struct radeon_device *rdev) return 0; }
-/* XXX */ static void cik_cp_fini(struct radeon_device *rdev) { cik_cp_gfx_fini(rdev); cik_cp_compute_fini(rdev); }
-/* XXX */ static int cik_cp_resume(struct radeon_device *rdev) { int r; @@ -2865,6 +3294,22 @@ static void cik_print_gpu_status_regs(struct radeon_device *rdev) RREG32(SDMA0_STATUS_REG + SDMA0_REGISTER_OFFSET)); dev_info(rdev->dev, " SDMA1_STATUS_REG = 0x%08X\n", RREG32(SDMA0_STATUS_REG + SDMA1_REGISTER_OFFSET));
- dev_info(rdev->dev, " CP_STAT = 0x%08x\n", RREG32(CP_STAT));
- dev_info(rdev->dev, " CP_STALLED_STAT1 = 0x%08x\n",
RREG32(CP_STALLED_STAT1));
- dev_info(rdev->dev, " CP_STALLED_STAT2 = 0x%08x\n",
RREG32(CP_STALLED_STAT2));
- dev_info(rdev->dev, " CP_STALLED_STAT3 = 0x%08x\n",
RREG32(CP_STALLED_STAT3));
- dev_info(rdev->dev, " CP_CPF_BUSY_STAT = 0x%08x\n",
RREG32(CP_CPF_BUSY_STAT));
- dev_info(rdev->dev, " CP_CPF_STALLED_STAT1 = 0x%08x\n",
RREG32(CP_CPF_STALLED_STAT1));
- dev_info(rdev->dev, " CP_CPF_STATUS = 0x%08x\n", RREG32(CP_CPF_STATUS));
- dev_info(rdev->dev, " CP_CPC_BUSY_STAT = 0x%08x\n", RREG32(CP_CPC_BUSY_STAT));
- dev_info(rdev->dev, " CP_CPC_STALLED_STAT1 = 0x%08x\n",
RREG32(CP_CPC_STALLED_STAT1));
- dev_info(rdev->dev, " CP_CPC_STATUS = 0x%08x\n", RREG32(CP_CPC_STATUS));
}
/** @@ -4952,12 +5397,31 @@ static int cik_startup(struct radeon_device *rdev) if (r) return r;
- /* allocate rlc buffers */
Init mec not allocate rlc ...
r = cik_mec_init(rdev);
if (r) {
DRM_ERROR("Failed to init MEC BOs!\n");
return r;
}
r = radeon_fence_driver_start_ring(rdev, RADEON_RING_TYPE_GFX_INDEX); if (r) { dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r); return r; }
r = radeon_fence_driver_start_ring(rdev, CAYMAN_RING_TYPE_CP1_INDEX);
if (r) {
dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r);
return r;
}
r = radeon_fence_driver_start_ring(rdev, CAYMAN_RING_TYPE_CP2_INDEX);
if (r) {
dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r);
return r;
}
r = radeon_fence_driver_start_ring(rdev, R600_RING_TYPE_DMA_INDEX); if (r) { dev_err(rdev->dev, "failed initializing DMA fences (%d).\n", r);
@@ -5002,6 +5466,36 @@ static int cik_startup(struct radeon_device *rdev) if (r) return r;
- /* set up the compute queues */
- ring = &rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX];
- r = radeon_ring_init(rdev, ring, ring->ring_size, RADEON_WB_CP1_RPTR_OFFSET,
CP_HQD_PQ_RPTR, CP_HQD_PQ_WPTR,
0, 0xfffff, RADEON_CP_PACKET2);
- if (r)
return r;
- ring->me = 1; /* first MEC */
- ring->pipe = 0; /* first pipe */
- ring->queue = 0; /* first queue */
- ring->wptr_offs = CIK_WB_CP1_WPTR_OFFSET;
- ring->funcs.get_rptr = &cik_compute_ring_get_rptr;
- ring->funcs.get_wptr = &cik_compute_ring_get_wptr;
- ring->funcs.set_wptr = &cik_compute_ring_set_wptr;
- ring = &rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX];
- r = radeon_ring_init(rdev, ring, ring->ring_size, RADEON_WB_CP2_RPTR_OFFSET,
CP_HQD_PQ_RPTR, CP_HQD_PQ_WPTR,
0, 0xffffffff, RADEON_CP_PACKET2);
- if (r)
return r;
- /* dGPU only have 1 MEC */
- ring->me = 1; /* first MEC */
- ring->pipe = 0; /* first pipe */
- ring->queue = 1; /* second queue */
- ring->wptr_offs = CIK_WB_CP2_WPTR_OFFSET;
- ring->funcs.get_rptr = &cik_compute_ring_get_rptr;
- ring->funcs.get_wptr = &cik_compute_ring_get_wptr;
- ring->funcs.set_wptr = &cik_compute_ring_set_wptr;
- ring = &rdev->ring[R600_RING_TYPE_DMA_INDEX]; r = radeon_ring_init(rdev, ring, ring->ring_size, R600_WB_DMA_RPTR_OFFSET, SDMA0_GFX_RB_RPTR + SDMA0_REGISTER_OFFSET,
@@ -5176,6 +5670,20 @@ int cik_init(struct radeon_device *rdev) ring->ring_obj = NULL; r600_ring_init(rdev, ring, 1024 * 1024);
- ring = &rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX];
- ring->ring_obj = NULL;
- r600_ring_init(rdev, ring, 1024 * 1024);
- r = radeon_doorbell_get(rdev, &ring->doorbell_page_num);
- if (r)
return r;
- ring = &rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX];
- ring->ring_obj = NULL;
- r600_ring_init(rdev, ring, 1024 * 1024);
- r = radeon_doorbell_get(rdev, &ring->doorbell_page_num);
- if (r)
return r;
- ring = &rdev->ring[R600_RING_TYPE_DMA_INDEX]; ring->ring_obj = NULL; r600_ring_init(rdev, ring, 256 * 1024);
@@ -5206,6 +5714,7 @@ int cik_init(struct radeon_device *rdev) cik_sdma_fini(rdev); cik_irq_fini(rdev); si_rlc_fini(rdev);
radeon_wb_fini(rdev); radeon_ib_pool_fini(rdev); radeon_vm_manager_fini(rdev);cik_mec_fini(rdev);
@@ -5241,6 +5750,7 @@ void cik_fini(struct radeon_device *rdev) cik_sdma_fini(rdev); cik_irq_fini(rdev); si_rlc_fini(rdev);
- cik_mec_fini(rdev); radeon_wb_fini(rdev); radeon_vm_manager_fini(rdev); radeon_ib_pool_fini(rdev);
diff --git a/drivers/gpu/drm/radeon/cikd.h b/drivers/gpu/drm/radeon/cikd.h index 79be39e..63514b9 100644 --- a/drivers/gpu/drm/radeon/cikd.h +++ b/drivers/gpu/drm/radeon/cikd.h @@ -460,6 +460,13 @@ # define RDERR_INT_ENABLE (1 << 0) # define GUI_IDLE_INT_ENABLE (1 << 19)
+#define CP_CPC_STATUS 0x8210 +#define CP_CPC_BUSY_STAT 0x8214 +#define CP_CPC_STALLED_STAT1 0x8218 +#define CP_CPF_STATUS 0x821c +#define CP_CPF_BUSY_STAT 0x8220 +#define CP_CPF_STALLED_STAT1 0x8224
#define CP_MEC_CNTL 0x8234 #define MEC_ME2_HALT (1 << 28) #define MEC_ME1_HALT (1 << 30) @@ -468,6 +475,12 @@ #define MEC_ME2_HALT (1 << 28) #define MEC_ME1_HALT (1 << 30)
+#define CP_STALLED_STAT3 0x8670 +#define CP_STALLED_STAT1 0x8674 +#define CP_STALLED_STAT2 0x8678
+#define CP_STAT 0x8680
#define CP_ME_CNTL 0x86D8 #define CP_CE_HALT (1 << 24) #define CP_PFP_HALT (1 << 26) @@ -701,6 +714,11 @@ # define CP_RINGID1_INT_STAT (1 << 30) # define CP_RINGID0_INT_STAT (1 << 31)
+#define CP_CPF_DEBUG 0xC200
+#define CP_PQ_WPTR_POLL_CNTL 0xC20C +#define WPTR_POLL_EN (1 << 31)
#define CP_ME1_PIPE0_INT_CNTL 0xC214 #define CP_ME1_PIPE1_INT_CNTL 0xC218 #define CP_ME1_PIPE2_INT_CNTL 0xC21C @@ -773,6 +791,50 @@ #define RLC_GPM_SCRATCH_ADDR 0xC4B0 #define RLC_GPM_SCRATCH_DATA 0xC4B4
+#define CP_HPD_EOP_BASE_ADDR 0xC904 +#define CP_HPD_EOP_BASE_ADDR_HI 0xC908 +#define CP_HPD_EOP_VMID 0xC90C +#define CP_HPD_EOP_CONTROL 0xC910 +#define EOP_SIZE(x) ((x) << 0) +#define EOP_SIZE_MASK (0x3f << 0) +#define CP_MQD_BASE_ADDR 0xC914 +#define CP_MQD_BASE_ADDR_HI 0xC918 +#define CP_HQD_ACTIVE 0xC91C +#define CP_HQD_VMID 0xC920
+#define CP_HQD_PQ_BASE 0xC934 +#define CP_HQD_PQ_BASE_HI 0xC938 +#define CP_HQD_PQ_RPTR 0xC93C +#define CP_HQD_PQ_RPTR_REPORT_ADDR 0xC940 +#define CP_HQD_PQ_RPTR_REPORT_ADDR_HI 0xC944 +#define CP_HQD_PQ_WPTR_POLL_ADDR 0xC948 +#define CP_HQD_PQ_WPTR_POLL_ADDR_HI 0xC94C +#define CP_HQD_PQ_DOORBELL_CONTROL 0xC950 +#define DOORBELL_OFFSET(x) ((x) << 2) +#define DOORBELL_OFFSET_MASK (0x1fffff << 2) +#define DOORBELL_SOURCE (1 << 28) +#define DOORBELL_SCHD_HIT (1 << 29) +#define DOORBELL_EN (1 << 30) +#define DOORBELL_HIT (1 << 31) +#define CP_HQD_PQ_WPTR 0xC954 +#define CP_HQD_PQ_CONTROL 0xC958 +#define QUEUE_SIZE(x) ((x) << 0) +#define QUEUE_SIZE_MASK (0x3f << 0) +#define RPTR_BLOCK_SIZE(x) ((x) << 8) +#define RPTR_BLOCK_SIZE_MASK (0x3f << 8) +#define PQ_VOLATILE (1 << 26) +#define NO_UPDATE_RPTR (1 << 27) +#define UNORD_DISPATCH (1 << 28) +#define ROQ_PQ_IB_FLIP (1 << 29) +#define PRIV_STATE (1 << 30) +#define KMD_QUEUE (1 << 31)
+#define CP_HQD_DEQUEUE_REQUEST 0xC974
+#define CP_MQD_CONTROL 0xC99C +#define MQD_VMID(x) ((x) << 0) +#define MQD_VMID_MASK (0xf << 0)
#define PA_SC_RASTER_CONFIG 0x28350 # define RASTER_CONFIG_RB_MAP_0 0 # define RASTER_CONFIG_RB_MAP_1 1 diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index a2a3430..d40d506 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -714,6 +714,22 @@ struct radeon_ring { u32 (*get_wptr)(struct radeon_device *rdev, struct radeon_ring *ring); void (*set_wptr)(struct radeon_device *rdev, struct radeon_ring *ring); } funcs;
- /* for CIK queues */
- u32 me;
- u32 pipe;
- u32 queue;
- struct radeon_bo *mqd_obj;
- u32 doorbell_page_num;
- u32 doorbell_offset;
- unsigned wptr_offs;
+};
+struct radeon_mec {
- struct radeon_bo *hpd_eop_obj;
- u64 hpd_eop_gpu_addr;
- u32 num_pipe;
- u32 num_mec;
- u32 num_queue;
};
/* @@ -971,6 +987,8 @@ struct radeon_wb { #define CAYMAN_WB_DMA1_RPTR_OFFSET 2304 #define R600_WB_UVD_RPTR_OFFSET 2560 #define R600_WB_EVENT_OFFSET 3072 +#define CIK_WB_CP1_WPTR_OFFSET 3328 +#define CIK_WB_CP2_WPTR_OFFSET 3584
/**
- struct radeon_pm - power management datas
@@ -1760,6 +1778,7 @@ struct radeon_device { int msi_enabled; /* msi enabled */ struct r600_ih ih; /* r6/700 interrupt ring */ struct si_rlc rlc;
- struct radeon_mec mec; struct work_struct hotplug_work; struct work_struct audio_work; struct work_struct reset_work;
diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c index cf71734..7e265a5 100644 --- a/drivers/gpu/drm/radeon/radeon_cs.c +++ b/drivers/gpu/drm/radeon/radeon_cs.c @@ -121,9 +121,7 @@ static int radeon_cs_get_ring(struct radeon_cs_parser *p, u32 ring, s32 priority p->ring = RADEON_RING_TYPE_GFX_INDEX; break; case RADEON_CS_RING_COMPUTE:
if (p->rdev->family >= CHIP_BONAIRE)
p->ring = RADEON_RING_TYPE_GFX_INDEX;
else if (p->rdev->family >= CHIP_TAHITI) {
if (p->rdev->family >= CHIP_TAHITI) { if (p->priority > 0) p->ring = CAYMAN_RING_TYPE_CP1_INDEX; else
-- 1.7.7.5
dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
From: Alex Deucher alexander.deucher@amd.com
Type 2 packets are deprecated on CIK MEC and we should use type 3 nop packets. Setting the count field to the max value (0x3fff) indicates that only one dword should be skipped like a type 2 packet.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/cik.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 9d2d6bb..08dc4c2 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -5470,7 +5470,7 @@ static int cik_startup(struct radeon_device *rdev) ring = &rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX]; r = radeon_ring_init(rdev, ring, ring->ring_size, RADEON_WB_CP1_RPTR_OFFSET, CP_HQD_PQ_RPTR, CP_HQD_PQ_WPTR, - 0, 0xfffff, RADEON_CP_PACKET2); + 0, 0xfffff, PACKET3(PACKET3_NOP, 0x3FFF)); if (r) return r; ring->me = 1; /* first MEC */ @@ -5484,7 +5484,7 @@ static int cik_startup(struct radeon_device *rdev) ring = &rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX]; r = radeon_ring_init(rdev, ring, ring->ring_size, RADEON_WB_CP2_RPTR_OFFSET, CP_HQD_PQ_RPTR, CP_HQD_PQ_WPTR, - 0, 0xffffffff, RADEON_CP_PACKET2); + 0, 0xffffffff, PACKET3(PACKET3_NOP, 0x3FFF)); if (r) return r; /* dGPU only have 1 MEC */
On Wed, Jun 26, 2013 at 09:22:13AM -0400, alexdeucher@gmail.com wrote:
From: Alex Deucher alexander.deucher@amd.com
Type 2 packets are deprecated on CIK MEC and we should use type 3 nop packets. Setting the count field to the max value (0x3fff) indicates that only one dword should be skipped like a type 2 packet.
Signed-off-by: Alex Deucher alexander.deucher@amd.com
What about adding same comment in the code and not only in git commit message.
Reviewed-by: Jerome Glisse jglisse@redhat.com
drivers/gpu/drm/radeon/cik.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 9d2d6bb..08dc4c2 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -5470,7 +5470,7 @@ static int cik_startup(struct radeon_device *rdev) ring = &rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX]; r = radeon_ring_init(rdev, ring, ring->ring_size, RADEON_WB_CP1_RPTR_OFFSET, CP_HQD_PQ_RPTR, CP_HQD_PQ_WPTR,
0, 0xfffff, RADEON_CP_PACKET2);
if (r) return r; ring->me = 1; /* first MEC */0, 0xfffff, PACKET3(PACKET3_NOP, 0x3FFF));
@@ -5484,7 +5484,7 @@ static int cik_startup(struct radeon_device *rdev) ring = &rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX]; r = radeon_ring_init(rdev, ring, ring->ring_size, RADEON_WB_CP2_RPTR_OFFSET, CP_HQD_PQ_RPTR, CP_HQD_PQ_WPTR,
0, 0xffffffff, RADEON_CP_PACKET2);
if (r) return r; /* dGPU only have 1 MEC */0, 0xffffffff, PACKET3(PACKET3_NOP, 0x3FFF));
-- 1.7.7.5
dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
From: Alex Deucher alexander.deucher@amd.com
The compute rings use RELEASE_MEM rather then EOP packets for writing fences and there is no SYNC_PFP_ME packet on the compute rings.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/cik.c | 53 +++++++++++++++++++++++++++++++++++++---- 1 files changed, 47 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 08dc4c2..596bfed 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -1706,7 +1706,7 @@ int cik_ring_test(struct radeon_device *rdev, struct radeon_ring *ring) }
/** - * cik_fence_ring_emit - emit a fence on the gfx ring + * cik_fence_gfx_ring_emit - emit a fence on the gfx ring * * @rdev: radeon_device pointer * @fence: radeon fence object @@ -1714,8 +1714,8 @@ int cik_ring_test(struct radeon_device *rdev, struct radeon_ring *ring) * Emits a fence sequnce number on the gfx ring and flushes * GPU caches. */ -void cik_fence_ring_emit(struct radeon_device *rdev, - struct radeon_fence *fence) +void cik_fence_gfx_ring_emit(struct radeon_device *rdev, + struct radeon_fence *fence) { struct radeon_ring *ring = &rdev->ring[fence->ring]; u64 addr = rdev->fence_drv[fence->ring].gpu_addr; @@ -1742,6 +1742,44 @@ void cik_fence_ring_emit(struct radeon_device *rdev, radeon_ring_write(ring, 0); }
+/** + * cik_fence_compute_ring_emit - emit a fence on the compute ring + * + * @rdev: radeon_device pointer + * @fence: radeon fence object + * + * Emits a fence sequnce number on the compute ring and flushes + * GPU caches. + */ +void cik_fence_compute_ring_emit(struct radeon_device *rdev, + struct radeon_fence *fence) +{ + struct radeon_ring *ring = &rdev->ring[fence->ring]; + u64 addr = rdev->fence_drv[fence->ring].gpu_addr; + + /* RELEASE_MEM - flush caches, send int */ + radeon_ring_write(ring, PACKET3(PACKET3_RELEASE_MEM, 5)); + radeon_ring_write(ring, (EOP_TCL1_ACTION_EN | + EOP_TC_ACTION_EN | + EVENT_TYPE(CACHE_FLUSH_AND_INV_TS_EVENT) | + EVENT_INDEX(5))); + radeon_ring_write(ring, DATA_SEL(1) | INT_SEL(2)); + radeon_ring_write(ring, addr & 0xfffffffc); + radeon_ring_write(ring, upper_32_bits(addr)); + radeon_ring_write(ring, fence->seq); + radeon_ring_write(ring, 0); + /* HDP flush */ + /* We should be using the new WAIT_REG_MEM special op packet here + * but it causes the CP to hang + */ + radeon_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); + radeon_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) | + WRITE_DATA_DST_SEL(0))); + radeon_ring_write(ring, HDP_MEM_COHERENCY_FLUSH_CNTL >> 2); + radeon_ring_write(ring, 0); + radeon_ring_write(ring, 0); +} + void cik_semaphore_ring_emit(struct radeon_device *rdev, struct radeon_ring *ring, struct radeon_semaphore *semaphore, @@ -4051,9 +4089,12 @@ void cik_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm) radeon_ring_write(ring, 0); radeon_ring_write(ring, 1 << vm->id);
- /* sync PFP to ME, otherwise we might get invalid PFP reads */ - radeon_ring_write(ring, PACKET3(PACKET3_PFP_SYNC_ME, 0)); - radeon_ring_write(ring, 0x0); + /* compute doesn't have PFP */ + if (ridx == RADEON_RING_TYPE_GFX_INDEX) { + /* sync PFP to ME, otherwise we might get invalid PFP reads */ + radeon_ring_write(ring, PACKET3(PACKET3_PFP_SYNC_ME, 0)); + radeon_ring_write(ring, 0x0); + } }
/**
From: Alex Deucher alexander.deucher@amd.com
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/cik.c | 121 ++++++++++++++++++++++++++++++++++++++++-- 1 files changed, 116 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 596bfed..5923ef7 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -4631,6 +4631,8 @@ int cik_irq_set(struct radeon_device *rdev) { u32 cp_int_cntl = CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE | PRIV_INSTR_INT_ENABLE | PRIV_REG_INT_ENABLE; + u32 cp_m1p0, cp_m1p1, cp_m1p2, cp_m1p3; + u32 cp_m2p0, cp_m2p1, cp_m2p2, cp_m2p3; u32 crtc1 = 0, crtc2 = 0, crtc3 = 0, crtc4 = 0, crtc5 = 0, crtc6 = 0; u32 hpd1, hpd2, hpd3, hpd4, hpd5, hpd6; u32 grbm_int_cntl = 0; @@ -4658,13 +4660,106 @@ int cik_irq_set(struct radeon_device *rdev) dma_cntl = RREG32(SDMA0_CNTL + SDMA0_REGISTER_OFFSET) & ~TRAP_ENABLE; dma_cntl1 = RREG32(SDMA0_CNTL + SDMA1_REGISTER_OFFSET) & ~TRAP_ENABLE;
+ cp_m1p0 = RREG32(CP_ME1_PIPE0_INT_CNTL) & ~TIME_STAMP_INT_ENABLE; + cp_m1p1 = RREG32(CP_ME1_PIPE1_INT_CNTL) & ~TIME_STAMP_INT_ENABLE; + cp_m1p2 = RREG32(CP_ME1_PIPE2_INT_CNTL) & ~TIME_STAMP_INT_ENABLE; + cp_m1p3 = RREG32(CP_ME1_PIPE3_INT_CNTL) & ~TIME_STAMP_INT_ENABLE; + cp_m2p0 = RREG32(CP_ME2_PIPE0_INT_CNTL) & ~TIME_STAMP_INT_ENABLE; + cp_m2p1 = RREG32(CP_ME2_PIPE1_INT_CNTL) & ~TIME_STAMP_INT_ENABLE; + cp_m2p2 = RREG32(CP_ME2_PIPE2_INT_CNTL) & ~TIME_STAMP_INT_ENABLE; + cp_m2p3 = RREG32(CP_ME2_PIPE3_INT_CNTL) & ~TIME_STAMP_INT_ENABLE; + /* enable CP interrupts on all rings */ if (atomic_read(&rdev->irq.ring_int[RADEON_RING_TYPE_GFX_INDEX])) { DRM_DEBUG("cik_irq_set: sw int gfx\n"); cp_int_cntl |= TIME_STAMP_INT_ENABLE; } - /* TODO: compute queues! */ - /* CP_ME[1-2]_PIPE[0-3]_INT_CNTL */ + if (atomic_read(&rdev->irq.ring_int[CAYMAN_RING_TYPE_CP1_INDEX])) { + struct radeon_ring *ring = &rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX]; + DRM_DEBUG("si_irq_set: sw int cp1\n"); + if (ring->me == 1) { + switch (ring->pipe) { + case 0: + cp_m1p0 |= TIME_STAMP_INT_ENABLE; + break; + case 1: + cp_m1p1 |= TIME_STAMP_INT_ENABLE; + break; + case 2: + cp_m1p2 |= TIME_STAMP_INT_ENABLE; + break; + case 3: + cp_m1p2 |= TIME_STAMP_INT_ENABLE; + break; + default: + DRM_DEBUG("si_irq_set: sw int cp1 invalid pipe %d\n", ring->pipe); + break; + } + } else if (ring->me == 2) { + switch (ring->pipe) { + case 0: + cp_m2p0 |= TIME_STAMP_INT_ENABLE; + break; + case 1: + cp_m2p1 |= TIME_STAMP_INT_ENABLE; + break; + case 2: + cp_m2p2 |= TIME_STAMP_INT_ENABLE; + break; + case 3: + cp_m2p2 |= TIME_STAMP_INT_ENABLE; + break; + default: + DRM_DEBUG("si_irq_set: sw int cp1 invalid pipe %d\n", ring->pipe); + break; + } + } else { + DRM_DEBUG("si_irq_set: sw int cp1 invalid me %d\n", ring->me); + } + } + if (atomic_read(&rdev->irq.ring_int[CAYMAN_RING_TYPE_CP2_INDEX])) { + struct radeon_ring *ring = &rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX]; + DRM_DEBUG("si_irq_set: sw int cp2\n"); + if (ring->me == 1) { + switch (ring->pipe) { + case 0: + cp_m1p0 |= TIME_STAMP_INT_ENABLE; + break; + case 1: + cp_m1p1 |= TIME_STAMP_INT_ENABLE; + break; + case 2: + cp_m1p2 |= TIME_STAMP_INT_ENABLE; + break; + case 3: + cp_m1p2 |= TIME_STAMP_INT_ENABLE; + break; + default: + DRM_DEBUG("si_irq_set: sw int cp2 invalid pipe %d\n", ring->pipe); + break; + } + } else if (ring->me == 2) { + switch (ring->pipe) { + case 0: + cp_m2p0 |= TIME_STAMP_INT_ENABLE; + break; + case 1: + cp_m2p1 |= TIME_STAMP_INT_ENABLE; + break; + case 2: + cp_m2p2 |= TIME_STAMP_INT_ENABLE; + break; + case 3: + cp_m2p2 |= TIME_STAMP_INT_ENABLE; + break; + default: + DRM_DEBUG("si_irq_set: sw int cp2 invalid pipe %d\n", ring->pipe); + break; + } + } else { + DRM_DEBUG("si_irq_set: sw int cp2 invalid me %d\n", ring->me); + } + }
if (atomic_read(&rdev->irq.ring_int[R600_RING_TYPE_DMA_INDEX])) { DRM_DEBUG("cik_irq_set: sw int dma\n"); @@ -4736,6 +4831,15 @@ int cik_irq_set(struct radeon_device *rdev) WREG32(SDMA0_CNTL + SDMA0_REGISTER_OFFSET, dma_cntl); WREG32(SDMA0_CNTL + SDMA1_REGISTER_OFFSET, dma_cntl1);
+ WREG32(CP_ME1_PIPE0_INT_CNTL, cp_m1p0); + WREG32(CP_ME1_PIPE1_INT_CNTL, cp_m1p1); + WREG32(CP_ME1_PIPE2_INT_CNTL, cp_m1p2); + WREG32(CP_ME1_PIPE3_INT_CNTL, cp_m1p3); + WREG32(CP_ME2_PIPE0_INT_CNTL, cp_m2p0); + WREG32(CP_ME2_PIPE1_INT_CNTL, cp_m2p1); + WREG32(CP_ME2_PIPE2_INT_CNTL, cp_m2p2); + WREG32(CP_ME2_PIPE3_INT_CNTL, cp_m2p3); + WREG32(GRBM_INT_CNTL, grbm_int_cntl);
WREG32(LB_INTERRUPT_MASK + EVERGREEN_CRTC0_REGISTER_OFFSET, crtc1); @@ -4957,6 +5061,8 @@ static inline u32 cik_get_ih_wptr(struct radeon_device *rdev) */ int cik_irq_process(struct radeon_device *rdev) { + struct radeon_ring *cp1_ring = &rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX]; + struct radeon_ring *cp2_ring = &rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX]; u32 wptr; u32 rptr; u32 src_id, src_data, ring_id; @@ -5222,10 +5328,11 @@ restart_ih: radeon_fence_process(rdev, RADEON_RING_TYPE_GFX_INDEX); break; case 1: - /* XXX compute */ - break; case 2: - /* XXX compute */ + if ((cp1_ring->me == me_id) & (cp1_ring->pipe == pipe_id)) + radeon_fence_process(rdev, CAYMAN_RING_TYPE_CP1_INDEX); + if ((cp2_ring->me == me_id) & (cp2_ring->pipe == pipe_id)) + radeon_fence_process(rdev, CAYMAN_RING_TYPE_CP2_INDEX); break; } break; @@ -5244,9 +5351,11 @@ restart_ih: break; case 1: /* XXX compute */ + queue_reset = true; break; case 2: /* XXX compute */ + queue_reset = true; break; } break; @@ -5265,9 +5374,11 @@ restart_ih: break; case 1: /* XXX compute */ + queue_reset = true; break; case 2: /* XXX compute */ + queue_reset = true; break; } break;
From: Alex Deucher alexander.deucher@amd.com
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/cik.c | 438 ++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 438 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 5923ef7..e899c44 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -99,6 +99,439 @@ void cik_pciep_wreg(struct radeon_device *rdev, u32 reg, u32 v) (void)RREG32(PCIE_DATA); }
+static const u32 bonaire_golden_spm_registers[] = +{ + 0x30800, 0xe0ffffff, 0xe0000000 +}; + +static const u32 bonaire_golden_common_registers[] = +{ + 0xc770, 0xffffffff, 0x00000800, + 0xc774, 0xffffffff, 0x00000800, + 0xc798, 0xffffffff, 0x00007fbf, + 0xc79c, 0xffffffff, 0x00007faf +}; + +static const u32 bonaire_golden_registers[] = +{ + 0x3354, 0x00000333, 0x00000333, + 0x3350, 0x000c0fc0, 0x00040200, + 0x9a10, 0x00010000, 0x00058208, + 0x3c000, 0xffff1fff, 0x00140000, + 0x3c200, 0xfdfc0fff, 0x00000100, + 0x3c234, 0x40000000, 0x40000200, + 0x9830, 0xffffffff, 0x00000000, + 0x9834, 0xf00fffff, 0x00000400, + 0x9838, 0x0002021c, 0x00020200, + 0xc78, 0x00000080, 0x00000000, + 0x5bb0, 0x000000f0, 0x00000070, + 0x5bc0, 0xf0311fff, 0x80300000, + 0x98f8, 0x73773777, 0x12010001, + 0x350c, 0x00810000, 0x408af000, + 0x7030, 0x31000111, 0x00000011, + 0x2f48, 0x73773777, 0x12010001, + 0x220c, 0x00007fb6, 0x0021a1b1, + 0x2210, 0x00007fb6, 0x002021b1, + 0x2180, 0x00007fb6, 0x00002191, + 0x2218, 0x00007fb6, 0x002121b1, + 0x221c, 0x00007fb6, 0x002021b1, + 0x21dc, 0x00007fb6, 0x00002191, + 0x21e0, 0x00007fb6, 0x00002191, + 0x3628, 0x0000003f, 0x0000000a, + 0x362c, 0x0000003f, 0x0000000a, + 0x2ae4, 0x00073ffe, 0x000022a2, + 0x240c, 0x000007ff, 0x00000000, + 0x8a14, 0xf000003f, 0x00000007, + 0x8bf0, 0x00002001, 0x00000001, + 0x8b24, 0xffffffff, 0x00ffffff, + 0x30a04, 0x0000ff0f, 0x00000000, + 0x28a4c, 0x07ffffff, 0x06000000, + 0x4d8, 0x00000fff, 0x00000100, + 0x3e78, 0x00000001, 0x00000002, + 0x9100, 0x03000000, 0x0362c688, + 0x8c00, 0x000000ff, 0x00000001, + 0xe40, 0x00001fff, 0x00001fff, + 0x9060, 0x0000007f, 0x00000020, + 0x9508, 0x00010000, 0x00010000, + 0xac14, 0x000003ff, 0x000000f3, + 0xac0c, 0xffffffff, 0x00001032 +}; + +static const u32 bonaire_mgcg_cgcg_init[] = +{ + 0xc420, 0xffffffff, 0xfffffffc, + 0x30800, 0xffffffff, 0xe0000000, + 0x3c2a0, 0xffffffff, 0x00000100, + 0x3c208, 0xffffffff, 0x00000100, + 0x3c2c0, 0xffffffff, 0xc0000100, + 0x3c2c8, 0xffffffff, 0xc0000100, + 0x3c2c4, 0xffffffff, 0xc0000100, + 0x55e4, 0xffffffff, 0x00600100, + 0x3c280, 0xffffffff, 0x00000100, + 0x3c214, 0xffffffff, 0x06000100, + 0x3c220, 0xffffffff, 0x00000100, + 0x3c218, 0xffffffff, 0x06000100, + 0x3c204, 0xffffffff, 0x00000100, + 0x3c2e0, 0xffffffff, 0x00000100, + 0x3c224, 0xffffffff, 0x00000100, + 0x3c200, 0xffffffff, 0x00000100, + 0x3c230, 0xffffffff, 0x00000100, + 0x3c234, 0xffffffff, 0x00000100, + 0x3c250, 0xffffffff, 0x00000100, + 0x3c254, 0xffffffff, 0x00000100, + 0x3c258, 0xffffffff, 0x00000100, + 0x3c25c, 0xffffffff, 0x00000100, + 0x3c260, 0xffffffff, 0x00000100, + 0x3c27c, 0xffffffff, 0x00000100, + 0x3c278, 0xffffffff, 0x00000100, + 0x3c210, 0xffffffff, 0x06000100, + 0x3c290, 0xffffffff, 0x00000100, + 0x3c274, 0xffffffff, 0x00000100, + 0x3c2b4, 0xffffffff, 0x00000100, + 0x3c2b0, 0xffffffff, 0x00000100, + 0x3c270, 0xffffffff, 0x00000100, + 0x30800, 0xffffffff, 0xe0000000, + 0x3c020, 0xffffffff, 0x00010000, + 0x3c024, 0xffffffff, 0x00030002, + 0x3c028, 0xffffffff, 0x00040007, + 0x3c02c, 0xffffffff, 0x00060005, + 0x3c030, 0xffffffff, 0x00090008, + 0x3c034, 0xffffffff, 0x00010000, + 0x3c038, 0xffffffff, 0x00030002, + 0x3c03c, 0xffffffff, 0x00040007, + 0x3c040, 0xffffffff, 0x00060005, + 0x3c044, 0xffffffff, 0x00090008, + 0x3c048, 0xffffffff, 0x00010000, + 0x3c04c, 0xffffffff, 0x00030002, + 0x3c050, 0xffffffff, 0x00040007, + 0x3c054, 0xffffffff, 0x00060005, + 0x3c058, 0xffffffff, 0x00090008, + 0x3c05c, 0xffffffff, 0x00010000, + 0x3c060, 0xffffffff, 0x00030002, + 0x3c064, 0xffffffff, 0x00040007, + 0x3c068, 0xffffffff, 0x00060005, + 0x3c06c, 0xffffffff, 0x00090008, + 0x3c070, 0xffffffff, 0x00010000, + 0x3c074, 0xffffffff, 0x00030002, + 0x3c078, 0xffffffff, 0x00040007, + 0x3c07c, 0xffffffff, 0x00060005, + 0x3c080, 0xffffffff, 0x00090008, + 0x3c084, 0xffffffff, 0x00010000, + 0x3c088, 0xffffffff, 0x00030002, + 0x3c08c, 0xffffffff, 0x00040007, + 0x3c090, 0xffffffff, 0x00060005, + 0x3c094, 0xffffffff, 0x00090008, + 0x3c098, 0xffffffff, 0x00010000, + 0x3c09c, 0xffffffff, 0x00030002, + 0x3c0a0, 0xffffffff, 0x00040007, + 0x3c0a4, 0xffffffff, 0x00060005, + 0x3c0a8, 0xffffffff, 0x00090008, + 0x3c000, 0xffffffff, 0x96e00200, + 0x8708, 0xffffffff, 0x00900100, + 0xc424, 0xffffffff, 0x0020003f, + 0x38, 0xffffffff, 0x0140001c, + 0x3c, 0x000f0000, 0x000f0000, + 0x220, 0xffffffff, 0xC060000C, + 0x224, 0xc0000fff, 0x00000100, + 0xf90, 0xffffffff, 0x00000100, + 0xf98, 0x00000101, 0x00000000, + 0x20a8, 0xffffffff, 0x00000104, + 0x55e4, 0xff000fff, 0x00000100, + 0x30cc, 0xc0000fff, 0x00000104, + 0xc1e4, 0x00000001, 0x00000001, + 0xd00c, 0xff000ff0, 0x00000100, + 0xd80c, 0xff000ff0, 0x00000100 +}; + +static const u32 spectre_golden_spm_registers[] = +{ + 0x30800, 0xe0ffffff, 0xe0000000 +}; + +static const u32 spectre_golden_common_registers[] = +{ + 0xc770, 0xffffffff, 0x00000800, + 0xc774, 0xffffffff, 0x00000800, + 0xc798, 0xffffffff, 0x00007fbf, + 0xc79c, 0xffffffff, 0x00007faf +}; + +static const u32 spectre_golden_registers[] = +{ + 0x3c000, 0xffff1fff, 0x96940200, + 0x3c00c, 0xffff0001, 0xff000000, + 0x3c200, 0xfffc0fff, 0x00000100, + 0x6ed8, 0x00010101, 0x00010000, + 0x9834, 0xf00fffff, 0x00000400, + 0x9838, 0xfffffffc, 0x00020200, + 0x5bb0, 0x000000f0, 0x00000070, + 0x5bc0, 0xf0311fff, 0x80300000, + 0x98f8, 0x73773777, 0x12010001, + 0x9b7c, 0x00ff0000, 0x00fc0000, + 0x2f48, 0x73773777, 0x12010001, + 0x8a14, 0xf000003f, 0x00000007, + 0x8b24, 0xffffffff, 0x00ffffff, + 0x28350, 0x3f3f3fff, 0x00000082, + 0x28355, 0x0000003f, 0x00000000, + 0x3e78, 0x00000001, 0x00000002, + 0x913c, 0xffff03df, 0x00000004, + 0xc768, 0x00000008, 0x00000008, + 0x8c00, 0x000008ff, 0x00000800, + 0x9508, 0x00010000, 0x00010000, + 0xac0c, 0xffffffff, 0x54763210, + 0x214f8, 0x01ff01ff, 0x00000002, + 0x21498, 0x007ff800, 0x00200000, + 0x2015c, 0xffffffff, 0x00000f40, + 0x30934, 0xffffffff, 0x00000001 +}; + +static const u32 spectre_mgcg_cgcg_init[] = +{ + 0xc420, 0xffffffff, 0xfffffffc, + 0x30800, 0xffffffff, 0xe0000000, + 0x3c2a0, 0xffffffff, 0x00000100, + 0x3c208, 0xffffffff, 0x00000100, + 0x3c2c0, 0xffffffff, 0x00000100, + 0x3c2c8, 0xffffffff, 0x00000100, + 0x3c2c4, 0xffffffff, 0x00000100, + 0x55e4, 0xffffffff, 0x00600100, + 0x3c280, 0xffffffff, 0x00000100, + 0x3c214, 0xffffffff, 0x06000100, + 0x3c220, 0xffffffff, 0x00000100, + 0x3c218, 0xffffffff, 0x06000100, + 0x3c204, 0xffffffff, 0x00000100, + 0x3c2e0, 0xffffffff, 0x00000100, + 0x3c224, 0xffffffff, 0x00000100, + 0x3c200, 0xffffffff, 0x00000100, + 0x3c230, 0xffffffff, 0x00000100, + 0x3c234, 0xffffffff, 0x00000100, + 0x3c250, 0xffffffff, 0x00000100, + 0x3c254, 0xffffffff, 0x00000100, + 0x3c258, 0xffffffff, 0x00000100, + 0x3c25c, 0xffffffff, 0x00000100, + 0x3c260, 0xffffffff, 0x00000100, + 0x3c27c, 0xffffffff, 0x00000100, + 0x3c278, 0xffffffff, 0x00000100, + 0x3c210, 0xffffffff, 0x06000100, + 0x3c290, 0xffffffff, 0x00000100, + 0x3c274, 0xffffffff, 0x00000100, + 0x3c2b4, 0xffffffff, 0x00000100, + 0x3c2b0, 0xffffffff, 0x00000100, + 0x3c270, 0xffffffff, 0x00000100, + 0x30800, 0xffffffff, 0xe0000000, + 0x3c020, 0xffffffff, 0x00010000, + 0x3c024, 0xffffffff, 0x00030002, + 0x3c028, 0xffffffff, 0x00040007, + 0x3c02c, 0xffffffff, 0x00060005, + 0x3c030, 0xffffffff, 0x00090008, + 0x3c034, 0xffffffff, 0x00010000, + 0x3c038, 0xffffffff, 0x00030002, + 0x3c03c, 0xffffffff, 0x00040007, + 0x3c040, 0xffffffff, 0x00060005, + 0x3c044, 0xffffffff, 0x00090008, + 0x3c048, 0xffffffff, 0x00010000, + 0x3c04c, 0xffffffff, 0x00030002, + 0x3c050, 0xffffffff, 0x00040007, + 0x3c054, 0xffffffff, 0x00060005, + 0x3c058, 0xffffffff, 0x00090008, + 0x3c05c, 0xffffffff, 0x00010000, + 0x3c060, 0xffffffff, 0x00030002, + 0x3c064, 0xffffffff, 0x00040007, + 0x3c068, 0xffffffff, 0x00060005, + 0x3c06c, 0xffffffff, 0x00090008, + 0x3c070, 0xffffffff, 0x00010000, + 0x3c074, 0xffffffff, 0x00030002, + 0x3c078, 0xffffffff, 0x00040007, + 0x3c07c, 0xffffffff, 0x00060005, + 0x3c080, 0xffffffff, 0x00090008, + 0x3c084, 0xffffffff, 0x00010000, + 0x3c088, 0xffffffff, 0x00030002, + 0x3c08c, 0xffffffff, 0x00040007, + 0x3c090, 0xffffffff, 0x00060005, + 0x3c094, 0xffffffff, 0x00090008, + 0x3c098, 0xffffffff, 0x00010000, + 0x3c09c, 0xffffffff, 0x00030002, + 0x3c0a0, 0xffffffff, 0x00040007, + 0x3c0a4, 0xffffffff, 0x00060005, + 0x3c0a8, 0xffffffff, 0x00090008, + 0x3c0ac, 0xffffffff, 0x00010000, + 0x3c0b0, 0xffffffff, 0x00030002, + 0x3c0b4, 0xffffffff, 0x00040007, + 0x3c0b8, 0xffffffff, 0x00060005, + 0x3c0bc, 0xffffffff, 0x00090008, + 0x3c000, 0xffffffff, 0x96e00200, + 0x8708, 0xffffffff, 0x00900100, + 0xc424, 0xffffffff, 0x0020003f, + 0x38, 0xffffffff, 0x0140001c, + 0x3c, 0x000f0000, 0x000f0000, + 0x220, 0xffffffff, 0xC060000C, + 0x224, 0xc0000fff, 0x00000100, + 0xf90, 0xffffffff, 0x00000100, + 0xf98, 0x00000101, 0x00000000, + 0x20a8, 0xffffffff, 0x00000104, + 0x55e4, 0xff000fff, 0x00000100, + 0x30cc, 0xc0000fff, 0x00000104, + 0xc1e4, 0x00000001, 0x00000001, + 0xd00c, 0xff000ff0, 0x00000100, + 0xd80c, 0xff000ff0, 0x00000100 +}; + +static const u32 kalindi_golden_spm_registers[] = +{ + 0x30800, 0xe0ffffff, 0xe0000000 +}; + +static const u32 kalindi_golden_common_registers[] = +{ + 0xc770, 0xffffffff, 0x00000800, + 0xc774, 0xffffffff, 0x00000800, + 0xc798, 0xffffffff, 0x00007fbf, + 0xc79c, 0xffffffff, 0x00007faf +}; + +static const u32 kalindi_golden_registers[] = +{ + 0x3c000, 0xffffdfff, 0x6e944040, + 0x55e4, 0xff607fff, 0xfc000100, + 0x3c220, 0xff000fff, 0x00000100, + 0x3c224, 0xff000fff, 0x00000100, + 0x3c200, 0xfffc0fff, 0x00000100, + 0x6ed8, 0x00010101, 0x00010000, + 0x9830, 0xffffffff, 0x00000000, + 0x9834, 0xf00fffff, 0x00000400, + 0x5bb0, 0x000000f0, 0x00000070, + 0x5bc0, 0xf0311fff, 0x80300000, + 0x98f8, 0x73773777, 0x12010001, + 0x98fc, 0xffffffff, 0x00000010, + 0x9b7c, 0x00ff0000, 0x00fc0000, + 0x8030, 0x00001f0f, 0x0000100a, + 0x2f48, 0x73773777, 0x12010001, + 0x2408, 0x000fffff, 0x000c007f, + 0x8a14, 0xf000003f, 0x00000007, + 0x8b24, 0x3fff3fff, 0x00ffcfff, + 0x30a04, 0x0000ff0f, 0x00000000, + 0x28a4c, 0x07ffffff, 0x06000000, + 0x4d8, 0x00000fff, 0x00000100, + 0x3e78, 0x00000001, 0x00000002, + 0xc768, 0x00000008, 0x00000008, + 0x8c00, 0x000000ff, 0x00000003, + 0x214f8, 0x01ff01ff, 0x00000002, + 0x21498, 0x007ff800, 0x00200000, + 0x2015c, 0xffffffff, 0x00000f40, + 0x88c4, 0x001f3ae3, 0x00000082, + 0x88d4, 0x0000001f, 0x00000010, + 0x30934, 0xffffffff, 0x00000000 +}; + +static const u32 kalindi_mgcg_cgcg_init[] = +{ + 0xc420, 0xffffffff, 0xfffffffc, + 0x30800, 0xffffffff, 0xe0000000, + 0x3c2a0, 0xffffffff, 0x00000100, + 0x3c208, 0xffffffff, 0x00000100, + 0x3c2c0, 0xffffffff, 0x00000100, + 0x3c2c8, 0xffffffff, 0x00000100, + 0x3c2c4, 0xffffffff, 0x00000100, + 0x55e4, 0xffffffff, 0x00600100, + 0x3c280, 0xffffffff, 0x00000100, + 0x3c214, 0xffffffff, 0x06000100, + 0x3c220, 0xffffffff, 0x00000100, + 0x3c218, 0xffffffff, 0x06000100, + 0x3c204, 0xffffffff, 0x00000100, + 0x3c2e0, 0xffffffff, 0x00000100, + 0x3c224, 0xffffffff, 0x00000100, + 0x3c200, 0xffffffff, 0x00000100, + 0x3c230, 0xffffffff, 0x00000100, + 0x3c234, 0xffffffff, 0x00000100, + 0x3c250, 0xffffffff, 0x00000100, + 0x3c254, 0xffffffff, 0x00000100, + 0x3c258, 0xffffffff, 0x00000100, + 0x3c25c, 0xffffffff, 0x00000100, + 0x3c260, 0xffffffff, 0x00000100, + 0x3c27c, 0xffffffff, 0x00000100, + 0x3c278, 0xffffffff, 0x00000100, + 0x3c210, 0xffffffff, 0x06000100, + 0x3c290, 0xffffffff, 0x00000100, + 0x3c274, 0xffffffff, 0x00000100, + 0x3c2b4, 0xffffffff, 0x00000100, + 0x3c2b0, 0xffffffff, 0x00000100, + 0x3c270, 0xffffffff, 0x00000100, + 0x30800, 0xffffffff, 0xe0000000, + 0x3c020, 0xffffffff, 0x00010000, + 0x3c024, 0xffffffff, 0x00030002, + 0x3c028, 0xffffffff, 0x00040007, + 0x3c02c, 0xffffffff, 0x00060005, + 0x3c030, 0xffffffff, 0x00090008, + 0x3c034, 0xffffffff, 0x00010000, + 0x3c038, 0xffffffff, 0x00030002, + 0x3c03c, 0xffffffff, 0x00040007, + 0x3c040, 0xffffffff, 0x00060005, + 0x3c044, 0xffffffff, 0x00090008, + 0x3c000, 0xffffffff, 0x96e00200, + 0x8708, 0xffffffff, 0x00900100, + 0xc424, 0xffffffff, 0x0020003f, + 0x38, 0xffffffff, 0x0140001c, + 0x3c, 0x000f0000, 0x000f0000, + 0x220, 0xffffffff, 0xC060000C, + 0x224, 0xc0000fff, 0x00000100, + 0x20a8, 0xffffffff, 0x00000104, + 0x55e4, 0xff000fff, 0x00000100, + 0x30cc, 0xc0000fff, 0x00000104, + 0xc1e4, 0x00000001, 0x00000001, + 0xd00c, 0xff000ff0, 0x00000100, + 0xd80c, 0xff000ff0, 0x00000100 +}; + +static void cik_init_golden_registers(struct radeon_device *rdev) +{ + switch (rdev->family) { + case CHIP_BONAIRE: + radeon_program_register_sequence(rdev, + bonaire_mgcg_cgcg_init, + (const u32)ARRAY_SIZE(bonaire_mgcg_cgcg_init)); + radeon_program_register_sequence(rdev, + bonaire_golden_registers, + (const u32)ARRAY_SIZE(bonaire_golden_registers)); + radeon_program_register_sequence(rdev, + bonaire_golden_common_registers, + (const u32)ARRAY_SIZE(bonaire_golden_common_registers)); + radeon_program_register_sequence(rdev, + bonaire_golden_spm_registers, + (const u32)ARRAY_SIZE(bonaire_golden_spm_registers)); + break; + case CHIP_KABINI: + radeon_program_register_sequence(rdev, + kalindi_mgcg_cgcg_init, + (const u32)ARRAY_SIZE(kalindi_mgcg_cgcg_init)); + radeon_program_register_sequence(rdev, + kalindi_golden_registers, + (const u32)ARRAY_SIZE(kalindi_golden_registers)); + radeon_program_register_sequence(rdev, + kalindi_golden_common_registers, + (const u32)ARRAY_SIZE(kalindi_golden_common_registers)); + radeon_program_register_sequence(rdev, + kalindi_golden_spm_registers, + (const u32)ARRAY_SIZE(kalindi_golden_spm_registers)); + break; + case CHIP_KAVERI: + radeon_program_register_sequence(rdev, + spectre_mgcg_cgcg_init, + (const u32)ARRAY_SIZE(spectre_mgcg_cgcg_init)); + radeon_program_register_sequence(rdev, + spectre_golden_registers, + (const u32)ARRAY_SIZE(spectre_golden_registers)); + radeon_program_register_sequence(rdev, + spectre_golden_common_registers, + (const u32)ARRAY_SIZE(spectre_golden_common_registers)); + radeon_program_register_sequence(rdev, + spectre_golden_spm_registers, + (const u32)ARRAY_SIZE(spectre_golden_spm_registers)); + break; + default: + break; + } +} + /** * cik_get_xclk - get the xclk * @@ -5719,6 +6152,9 @@ int cik_resume(struct radeon_device *rdev) /* post card */ atom_asic_init(rdev->mode_info.atom_context);
+ /* init golden registers */ + cik_init_golden_registers(rdev); + rdev->accel_working = true; r = cik_startup(rdev); if (r) { @@ -5797,6 +6233,8 @@ int cik_init(struct radeon_device *rdev) DRM_INFO("GPU not posted. posting now...\n"); atom_asic_init(rdev->mode_info.atom_context); } + /* init golden registers */ + cik_init_golden_registers(rdev); /* Initialize scratch registers */ cik_scratch_init(rdev); /* Initialize surface registers */
From: Alex Deucher alexander.deucher@amd.com
v2: fix up for latest reset changes v3: use CP for pt updates for now v4: update for 2 level PTs v5: update for ib_parse removal v6: vm_flush api change v7: rebase v8: fix gfx ring function pointers v9: fix vm_set_page function params v10: update for compute changes v11: cleanup for release
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/radeon_asic.c | 287 ++++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/radeon_asic.h | 47 ++++++ 2 files changed, 334 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index 717b537..d60adb3 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1923,6 +1923,280 @@ static struct radeon_asic si_asic = { }, };
+static struct radeon_asic ci_asic = { + .init = &cik_init, + .fini = &cik_fini, + .suspend = &cik_suspend, + .resume = &cik_resume, + .asic_reset = &cik_asic_reset, + .vga_set_state = &r600_vga_set_state, + .ioctl_wait_idle = NULL, + .gui_idle = &r600_gui_idle, + .mc_wait_for_idle = &evergreen_mc_wait_for_idle, + .get_xclk = &cik_get_xclk, + .get_gpu_clock_counter = &cik_get_gpu_clock_counter, + .gart = { + .tlb_flush = &cik_pcie_gart_tlb_flush, + .set_page = &rs600_gart_set_page, + }, + .vm = { + .init = &cik_vm_init, + .fini = &cik_vm_fini, + .pt_ring_index = R600_RING_TYPE_DMA_INDEX, + .set_page = &cik_vm_set_page, + }, + .ring = { + [RADEON_RING_TYPE_GFX_INDEX] = { + .ib_execute = &cik_ring_ib_execute, + .ib_parse = &cik_ib_parse, + .emit_fence = &cik_fence_gfx_ring_emit, + .emit_semaphore = &cik_semaphore_ring_emit, + .cs_parse = NULL, + .ring_test = &cik_ring_test, + .ib_test = &cik_ib_test, + .is_lockup = &cik_gfx_is_lockup, + .vm_flush = &cik_vm_flush, + }, + [CAYMAN_RING_TYPE_CP1_INDEX] = { + .ib_execute = &cik_ring_ib_execute, + .ib_parse = &cik_ib_parse, + .emit_fence = &cik_fence_compute_ring_emit, + .emit_semaphore = &cik_semaphore_ring_emit, + .cs_parse = NULL, + .ring_test = &cik_ring_test, + .ib_test = &cik_ib_test, + .is_lockup = &cik_gfx_is_lockup, + .vm_flush = &cik_vm_flush, + }, + [CAYMAN_RING_TYPE_CP2_INDEX] = { + .ib_execute = &cik_ring_ib_execute, + .ib_parse = &cik_ib_parse, + .emit_fence = &cik_fence_compute_ring_emit, + .emit_semaphore = &cik_semaphore_ring_emit, + .cs_parse = NULL, + .ring_test = &cik_ring_test, + .ib_test = &cik_ib_test, + .is_lockup = &cik_gfx_is_lockup, + .vm_flush = &cik_vm_flush, + }, + [R600_RING_TYPE_DMA_INDEX] = { + .ib_execute = &cik_sdma_ring_ib_execute, + .ib_parse = &cik_ib_parse, + .emit_fence = &cik_sdma_fence_ring_emit, + .emit_semaphore = &cik_sdma_semaphore_ring_emit, + .cs_parse = NULL, + .ring_test = &cik_sdma_ring_test, + .ib_test = &cik_sdma_ib_test, + .is_lockup = &cik_sdma_is_lockup, + .vm_flush = &cik_dma_vm_flush, + }, + [CAYMAN_RING_TYPE_DMA1_INDEX] = { + .ib_execute = &cik_sdma_ring_ib_execute, + .ib_parse = &cik_ib_parse, + .emit_fence = &cik_sdma_fence_ring_emit, + .emit_semaphore = &cik_sdma_semaphore_ring_emit, + .cs_parse = NULL, + .ring_test = &cik_sdma_ring_test, + .ib_test = &cik_sdma_ib_test, + .is_lockup = &cik_sdma_is_lockup, + .vm_flush = &cik_dma_vm_flush, + }, + [R600_RING_TYPE_UVD_INDEX] = { + .ib_execute = &r600_uvd_ib_execute, + .emit_fence = &r600_uvd_fence_emit, + .emit_semaphore = &cayman_uvd_semaphore_emit, + .cs_parse = &radeon_uvd_cs_parse, + .ring_test = &r600_uvd_ring_test, + .ib_test = &r600_uvd_ib_test, + .is_lockup = &radeon_ring_test_lockup, + } + }, + .irq = { + .set = &cik_irq_set, + .process = &cik_irq_process, + }, + .display = { + .bandwidth_update = &dce8_bandwidth_update, + .get_vblank_counter = &evergreen_get_vblank_counter, + .wait_for_vblank = &dce4_wait_for_vblank, + }, + .copy = { + .blit = NULL, + .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .dma = &cik_copy_dma, + .dma_ring_index = R600_RING_TYPE_DMA_INDEX, + .copy = &cik_copy_dma, + .copy_ring_index = R600_RING_TYPE_DMA_INDEX, + }, + .surface = { + .set_reg = r600_set_surface_reg, + .clear_reg = r600_clear_surface_reg, + }, + .hpd = { + .init = &evergreen_hpd_init, + .fini = &evergreen_hpd_fini, + .sense = &evergreen_hpd_sense, + .set_polarity = &evergreen_hpd_set_polarity, + }, + .pm = { + .misc = &evergreen_pm_misc, + .prepare = &evergreen_pm_prepare, + .finish = &evergreen_pm_finish, + .init_profile = &sumo_pm_init_profile, + .get_dynpm_state = &r600_pm_get_dynpm_state, + .get_engine_clock = &radeon_atom_get_engine_clock, + .set_engine_clock = &radeon_atom_set_engine_clock, + .get_memory_clock = &radeon_atom_get_memory_clock, + .set_memory_clock = &radeon_atom_set_memory_clock, + .get_pcie_lanes = NULL, + .set_pcie_lanes = NULL, + .set_clock_gating = NULL, + .set_uvd_clocks = &cik_set_uvd_clocks, + }, + .pflip = { + .pre_page_flip = &evergreen_pre_page_flip, + .page_flip = &evergreen_page_flip, + .post_page_flip = &evergreen_post_page_flip, + }, +}; + +static struct radeon_asic kv_asic = { + .init = &cik_init, + .fini = &cik_fini, + .suspend = &cik_suspend, + .resume = &cik_resume, + .asic_reset = &cik_asic_reset, + .vga_set_state = &r600_vga_set_state, + .ioctl_wait_idle = NULL, + .gui_idle = &r600_gui_idle, + .mc_wait_for_idle = &evergreen_mc_wait_for_idle, + .get_xclk = &cik_get_xclk, + .get_gpu_clock_counter = &cik_get_gpu_clock_counter, + .gart = { + .tlb_flush = &cik_pcie_gart_tlb_flush, + .set_page = &rs600_gart_set_page, + }, + .vm = { + .init = &cik_vm_init, + .fini = &cik_vm_fini, + .pt_ring_index = R600_RING_TYPE_DMA_INDEX, + .set_page = &cik_vm_set_page, + }, + .ring = { + [RADEON_RING_TYPE_GFX_INDEX] = { + .ib_execute = &cik_ring_ib_execute, + .ib_parse = &cik_ib_parse, + .emit_fence = &cik_fence_gfx_ring_emit, + .emit_semaphore = &cik_semaphore_ring_emit, + .cs_parse = NULL, + .ring_test = &cik_ring_test, + .ib_test = &cik_ib_test, + .is_lockup = &cik_gfx_is_lockup, + .vm_flush = &cik_vm_flush, + }, + [CAYMAN_RING_TYPE_CP1_INDEX] = { + .ib_execute = &cik_ring_ib_execute, + .ib_parse = &cik_ib_parse, + .emit_fence = &cik_fence_compute_ring_emit, + .emit_semaphore = &cik_semaphore_ring_emit, + .cs_parse = NULL, + .ring_test = &cik_ring_test, + .ib_test = &cik_ib_test, + .is_lockup = &cik_gfx_is_lockup, + .vm_flush = &cik_vm_flush, + }, + [CAYMAN_RING_TYPE_CP2_INDEX] = { + .ib_execute = &cik_ring_ib_execute, + .ib_parse = &cik_ib_parse, + .emit_fence = &cik_fence_compute_ring_emit, + .emit_semaphore = &cik_semaphore_ring_emit, + .cs_parse = NULL, + .ring_test = &cik_ring_test, + .ib_test = &cik_ib_test, + .is_lockup = &cik_gfx_is_lockup, + .vm_flush = &cik_vm_flush, + }, + [R600_RING_TYPE_DMA_INDEX] = { + .ib_execute = &cik_sdma_ring_ib_execute, + .ib_parse = &cik_ib_parse, + .emit_fence = &cik_sdma_fence_ring_emit, + .emit_semaphore = &cik_sdma_semaphore_ring_emit, + .cs_parse = NULL, + .ring_test = &cik_sdma_ring_test, + .ib_test = &cik_sdma_ib_test, + .is_lockup = &cik_sdma_is_lockup, + .vm_flush = &cik_dma_vm_flush, + }, + [CAYMAN_RING_TYPE_DMA1_INDEX] = { + .ib_execute = &cik_sdma_ring_ib_execute, + .ib_parse = &cik_ib_parse, + .emit_fence = &cik_sdma_fence_ring_emit, + .emit_semaphore = &cik_sdma_semaphore_ring_emit, + .cs_parse = NULL, + .ring_test = &cik_sdma_ring_test, + .ib_test = &cik_sdma_ib_test, + .is_lockup = &cik_sdma_is_lockup, + .vm_flush = &cik_dma_vm_flush, + }, + [R600_RING_TYPE_UVD_INDEX] = { + .ib_execute = &r600_uvd_ib_execute, + .emit_fence = &r600_uvd_fence_emit, + .emit_semaphore = &cayman_uvd_semaphore_emit, + .cs_parse = &radeon_uvd_cs_parse, + .ring_test = &r600_uvd_ring_test, + .ib_test = &r600_uvd_ib_test, + .is_lockup = &radeon_ring_test_lockup, + } + }, + .irq = { + .set = &cik_irq_set, + .process = &cik_irq_process, + }, + .display = { + .bandwidth_update = &dce8_bandwidth_update, + .get_vblank_counter = &evergreen_get_vblank_counter, + .wait_for_vblank = &dce4_wait_for_vblank, + }, + .copy = { + .blit = NULL, + .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .dma = &cik_copy_dma, + .dma_ring_index = R600_RING_TYPE_DMA_INDEX, + .copy = &cik_copy_dma, + .copy_ring_index = R600_RING_TYPE_DMA_INDEX, + }, + .surface = { + .set_reg = r600_set_surface_reg, + .clear_reg = r600_clear_surface_reg, + }, + .hpd = { + .init = &evergreen_hpd_init, + .fini = &evergreen_hpd_fini, + .sense = &evergreen_hpd_sense, + .set_polarity = &evergreen_hpd_set_polarity, + }, + .pm = { + .misc = &evergreen_pm_misc, + .prepare = &evergreen_pm_prepare, + .finish = &evergreen_pm_finish, + .init_profile = &sumo_pm_init_profile, + .get_dynpm_state = &r600_pm_get_dynpm_state, + .get_engine_clock = &radeon_atom_get_engine_clock, + .set_engine_clock = &radeon_atom_set_engine_clock, + .get_memory_clock = &radeon_atom_get_memory_clock, + .set_memory_clock = &radeon_atom_set_memory_clock, + .get_pcie_lanes = NULL, + .set_pcie_lanes = NULL, + .set_clock_gating = NULL, + .set_uvd_clocks = &cik_set_uvd_clocks, + }, + .pflip = { + .pre_page_flip = &evergreen_pre_page_flip, + .page_flip = &evergreen_page_flip, + .post_page_flip = &evergreen_post_page_flip, + }, +}; + /** * radeon_asic_init - register asic specific callbacks * @@ -2086,6 +2360,19 @@ int radeon_asic_init(struct radeon_device *rdev) else rdev->has_uvd = true; break; + case CHIP_BONAIRE: + rdev->asic = &ci_asic; + rdev->num_crtc = 6; + break; + case CHIP_KAVERI: + case CHIP_KABINI: + rdev->asic = &kv_asic; + /* set num crtcs */ + if (rdev->family == CHIP_KAVERI) + rdev->num_crtc = 4; + else + rdev->num_crtc = 2; + break; default: /* FIXME: not supported yet */ return -EINVAL; diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 3405742..4c2f51f 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -553,6 +553,9 @@ u32 si_get_xclk(struct radeon_device *rdev); uint64_t si_get_gpu_clock_counter(struct radeon_device *rdev); int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk);
+/* DCE8 - CIK */ +void dce8_bandwidth_update(struct radeon_device *rdev); + /* * cik */ @@ -562,5 +565,49 @@ uint32_t cik_pciep_rreg(struct radeon_device *rdev, uint32_t reg); void cik_pciep_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v); int cik_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk); int cik_uvd_resume(struct radeon_device *rdev); +void cik_sdma_fence_ring_emit(struct radeon_device *rdev, + struct radeon_fence *fence); +void cik_sdma_semaphore_ring_emit(struct radeon_device *rdev, + struct radeon_ring *ring, + struct radeon_semaphore *semaphore, + bool emit_wait); +void cik_sdma_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib); +int cik_copy_dma(struct radeon_device *rdev, + uint64_t src_offset, uint64_t dst_offset, + unsigned num_gpu_pages, + struct radeon_fence **fence); +int cik_sdma_ring_test(struct radeon_device *rdev, struct radeon_ring *ring); +int cik_sdma_ib_test(struct radeon_device *rdev, struct radeon_ring *ring); +bool cik_sdma_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring); +void cik_fence_gfx_ring_emit(struct radeon_device *rdev, + struct radeon_fence *fence); +void cik_fence_compute_ring_emit(struct radeon_device *rdev, + struct radeon_fence *fence); +void cik_semaphore_ring_emit(struct radeon_device *rdev, + struct radeon_ring *cp, + struct radeon_semaphore *semaphore, + bool emit_wait); +void cik_pcie_gart_tlb_flush(struct radeon_device *rdev); +int cik_init(struct radeon_device *rdev); +void cik_fini(struct radeon_device *rdev); +int cik_suspend(struct radeon_device *rdev); +int cik_resume(struct radeon_device *rdev); +bool cik_gfx_is_lockup(struct radeon_device *rdev, struct radeon_ring *cp); +int cik_asic_reset(struct radeon_device *rdev); +void cik_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib); +int cik_ring_test(struct radeon_device *rdev, struct radeon_ring *ring); +int cik_ib_test(struct radeon_device *rdev, struct radeon_ring *ring); +int cik_irq_set(struct radeon_device *rdev); +int cik_irq_process(struct radeon_device *rdev); +int cik_vm_init(struct radeon_device *rdev); +void cik_vm_fini(struct radeon_device *rdev); +void cik_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm); +void cik_vm_set_page(struct radeon_device *rdev, + struct radeon_ib *ib, + uint64_t pe, + uint64_t addr, unsigned count, + uint32_t incr, uint32_t flags); +void cik_dma_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm); +int cik_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib);
#endif
From: Alex Deucher alexander.deucher@amd.com
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/cik.c | 4 ++++ drivers/gpu/drm/radeon/radeon.h | 1 + drivers/gpu/drm/radeon/radeon_drv.c | 3 ++- drivers/gpu/drm/radeon/radeon_kms.c | 14 +++++++------- 4 files changed, 14 insertions(+), 8 deletions(-)
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index e899c44..2cf3521 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -1059,6 +1059,7 @@ static void cik_tiling_mode_table_init(struct radeon_device *rdev) gb_tile_moden = 0; break; } + rdev->config.cik.tile_mode_array[reg_offset] = gb_tile_moden; WREG32(GB_TILE_MODE0 + (reg_offset * 4), gb_tile_moden); } for (reg_offset = 0; reg_offset < num_secondary_tile_mode_states; reg_offset++) { @@ -1277,6 +1278,7 @@ static void cik_tiling_mode_table_init(struct radeon_device *rdev) gb_tile_moden = 0; break; } + rdev->config.cik.tile_mode_array[reg_offset] = gb_tile_moden; WREG32(GB_TILE_MODE0 + (reg_offset * 4), gb_tile_moden); } } else if (num_rbs < 4) { @@ -1402,6 +1404,7 @@ static void cik_tiling_mode_table_init(struct radeon_device *rdev) gb_tile_moden = 0; break; } + rdev->config.cik.tile_mode_array[reg_offset] = gb_tile_moden; WREG32(GB_TILE_MODE0 + (reg_offset * 4), gb_tile_moden); } } @@ -1619,6 +1622,7 @@ static void cik_tiling_mode_table_init(struct radeon_device *rdev) gb_tile_moden = 0; break; } + rdev->config.cik.tile_mode_array[reg_offset] = gb_tile_moden; WREG32(GB_TILE_MODE0 + (reg_offset * 4), gb_tile_moden); } for (reg_offset = 0; reg_offset < num_secondary_tile_mode_states; reg_offset++) { diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index d40d506..83f62aa 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1588,6 +1588,7 @@ struct cik_asic { unsigned multi_gpu_tile_size;
unsigned tile_config; + uint32_t tile_mode_array[32]; };
union radeon_asic_config { diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 094e7e5..02709e4 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -74,9 +74,10 @@ * 2.31.0 - Add fastfb support for rs690 * 2.32.0 - new info request for rings working * 2.33.0 - Add SI tiling mode array query + * 2.34.0 - Add CIK tiling mode array query */ #define KMS_DRIVER_MAJOR 2 -#define KMS_DRIVER_MINOR 33 +#define KMS_DRIVER_MINOR 34 #define KMS_DRIVER_PATCHLEVEL 0 int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags); int radeon_driver_unload_kms(struct drm_device *dev); diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index c650228..49ff3d1 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -423,15 +423,15 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) break; case RADEON_INFO_SI_TILE_MODE_ARRAY: if (rdev->family >= CHIP_BONAIRE) { - DRM_DEBUG_KMS("tile mode array is not implemented yet\n"); + value = rdev->config.cik.tile_mode_array; + value_size = sizeof(uint32_t)*32; + } else if (rdev->family >= CHIP_TAHITI) { + value = rdev->config.si.tile_mode_array; + value_size = sizeof(uint32_t)*32; + } else { + DRM_DEBUG_KMS("tile mode array is si+ only!\n"); return -EINVAL; } - if (rdev->family < CHIP_TAHITI) { - DRM_DEBUG_KMS("tile mode array is si only!\n"); - return -EINVAL; - } - value = rdev->config.si.tile_mode_array; - value_size = sizeof(uint32_t)*32; break; default: DRM_DEBUG_KMS("Invalid request %d\n", info->request);
From: Alex Deucher alexander.deucher@amd.com
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- include/drm/drm_pciids.h | 8 ++++++++ 1 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/include/drm/drm_pciids.h b/include/drm/drm_pciids.h index bb1bc48..23f89df 100644 --- a/include/drm/drm_pciids.h +++ b/include/drm/drm_pciids.h @@ -152,6 +152,14 @@ {0x1002, 0x6621, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6623, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6631, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x6640, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x6641, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x6649, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x6650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x6651, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x6658, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x665c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x665d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6660, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6663, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6664, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAINAN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
From: Alex Deucher alexander.deucher@amd.com
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- include/drm/drm_pciids.h | 16 ++++++++++++++++ 1 files changed, 16 insertions(+), 0 deletions(-)
diff --git a/include/drm/drm_pciids.h b/include/drm/drm_pciids.h index 23f89df..34efaf6 100644 --- a/include/drm/drm_pciids.h +++ b/include/drm/drm_pciids.h @@ -588,6 +588,22 @@ {0x1002, 0x9808, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ {0x1002, 0x9809, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ {0x1002, 0x980A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PALM|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x9830, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x9831, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x9832, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x9833, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x9834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x9835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x9836, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x9837, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x9838, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x9839, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x983a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x983b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x983c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x983d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x983e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ + {0x1002, 0x983f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ {0x1002, 0x9900, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ {0x1002, 0x9901, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \ {0x1002, 0x9903, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_ARUBA|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP|RADEON_IS_IGP}, \
From: Alex Deucher alexander.deucher@amd.com
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/evergreen.c | 6 ++---- drivers/gpu/drm/radeon/r600_reg.h | 3 +++ drivers/gpu/drm/radeon/radeon.h | 17 +++++++++++++++++ 3 files changed, 22 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 0f89ce3..9009dd4 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -3120,10 +3120,8 @@ static void evergreen_gpu_init(struct radeon_device *rdev) u32 efuse_straps_4; u32 efuse_straps_3;
- WREG32(RCU_IND_INDEX, 0x204); - efuse_straps_4 = RREG32(RCU_IND_DATA); - WREG32(RCU_IND_INDEX, 0x203); - efuse_straps_3 = RREG32(RCU_IND_DATA); + efuse_straps_4 = RREG32_RCU(0x204); + efuse_straps_3 = RREG32_RCU(0x203); tmp = (((efuse_straps_4 & 0xf) << 4) | ((efuse_straps_3 & 0xf0000000) >> 28)); } else { diff --git a/drivers/gpu/drm/radeon/r600_reg.h b/drivers/gpu/drm/radeon/r600_reg.h index 909219b..58c86cc 100644 --- a/drivers/gpu/drm/radeon/r600_reg.h +++ b/drivers/gpu/drm/radeon/r600_reg.h @@ -31,6 +31,9 @@ #define R600_PCIE_PORT_INDEX 0x0038 #define R600_PCIE_PORT_DATA 0x003c
+#define R600_RCU_INDEX 0x0100 +#define R600_RCU_DATA 0x0104 + #define R600_MC_VM_FB_LOCATION 0x2180 #define R600_MC_FB_BASE_MASK 0x0000FFFF #define R600_MC_FB_BASE_SHIFT 0 diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 83f62aa..0814aa7 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1851,6 +1851,8 @@ void cik_mm_wdoorbell(struct radeon_device *rdev, u32 offset, u32 v); #define WREG32_PCIE_PORT(reg, v) rdev->pciep_wreg(rdev, (reg), (v)) #define RREG32_SMC(reg) tn_smc_rreg(rdev, (reg)) #define WREG32_SMC(reg, v) tn_smc_wreg(rdev, (reg), (v)) +#define RREG32_RCU(reg) r600_rcu_rreg(rdev, (reg)) +#define WREG32_RCU(reg, v) r600_rcu_wreg(rdev, (reg), (v)) #define WREG32_P(reg, val, mask) \ do { \ uint32_t tmp_ = RREG32(reg); \ @@ -1907,6 +1909,21 @@ static inline void tn_smc_wreg(struct radeon_device *rdev, u32 reg, u32 v) WREG32(TN_SMC_IND_DATA_0, (v)); }
+static inline u32 r600_rcu_rreg(struct radeon_device *rdev, u32 reg) +{ + u32 r; + + WREG32(R600_RCU_INDEX, ((reg) & 0x1fff)); + r = RREG32(R600_RCU_DATA); + return r; +} + +static inline void r600_rcu_wreg(struct radeon_device *rdev, u32 reg, u32 v) +{ + WREG32(R600_RCU_INDEX, ((reg) & 0x1fff)); + WREG32(R600_RCU_DATA, (v)); +} + void r100_pll_errata_after_index(struct radeon_device *rdev);
From: Alex Deucher alexander.deucher@amd.com
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/evergreen_reg.h | 3 +++ drivers/gpu/drm/radeon/radeon.h | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/evergreen_reg.h b/drivers/gpu/drm/radeon/evergreen_reg.h index 50948ac..76630c6b 100644 --- a/drivers/gpu/drm/radeon/evergreen_reg.h +++ b/drivers/gpu/drm/radeon/evergreen_reg.h @@ -44,6 +44,9 @@ #define EVERGREEN_AUDIO_PLL1_DIV 0x5b4 #define EVERGREEN_AUDIO_PLL1_UNK 0x5bc
+#define EVERGREEN_CG_IND_ADDR 0x8f8 +#define EVERGREEN_CG_IND_DATA 0x8fc + #define EVERGREEN_AUDIO_ENABLE 0x5e78 #define EVERGREEN_AUDIO_VENDOR_ID 0x5ec0
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 0814aa7..e73c972 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1853,6 +1853,8 @@ void cik_mm_wdoorbell(struct radeon_device *rdev, u32 offset, u32 v); #define WREG32_SMC(reg, v) tn_smc_wreg(rdev, (reg), (v)) #define RREG32_RCU(reg) r600_rcu_rreg(rdev, (reg)) #define WREG32_RCU(reg, v) r600_rcu_wreg(rdev, (reg), (v)) +#define RREG32_CG(reg) eg_cg_rreg(rdev, (reg)) +#define WREG32_CG(reg, v) eg_cg_wreg(rdev, (reg), (v)) #define WREG32_P(reg, val, mask) \ do { \ uint32_t tmp_ = RREG32(reg); \ @@ -1924,6 +1926,21 @@ static inline void r600_rcu_wreg(struct radeon_device *rdev, u32 reg, u32 v) WREG32(R600_RCU_DATA, (v)); }
+static inline u32 eg_cg_rreg(struct radeon_device *rdev, u32 reg) +{ + u32 r; + + WREG32(EVERGREEN_CG_IND_ADDR, ((reg) & 0xffff)); + r = RREG32(EVERGREEN_CG_IND_DATA); + return r; +} + +static inline void eg_cg_wreg(struct radeon_device *rdev, u32 reg, u32 v) +{ + WREG32(EVERGREEN_CG_IND_ADDR, ((reg) & 0xffff)); + WREG32(EVERGREEN_CG_IND_DATA, (v)); +} + void r100_pll_errata_after_index(struct radeon_device *rdev);
From: Alex Deucher alexander.deucher@amd.com
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/radeon.h | 7 ++----- drivers/gpu/drm/radeon/radeon_asic.c | 8 ++++++++ drivers/gpu/drm/radeon/radeon_asic.h | 5 +++++ drivers/gpu/drm/radeon/radeon_pm.c | 26 ++++---------------------- 4 files changed, 19 insertions(+), 27 deletions(-)
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index e73c972..40053c8 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -220,11 +220,6 @@ int radeon_atom_get_clock_dividers(struct radeon_device *rdev, struct atom_clock_dividers *dividers); void radeon_atom_set_voltage(struct radeon_device *rdev, u16 voltage_level, u8 voltage_type); void rs690_pm_info(struct radeon_device *rdev); -extern int rv6xx_get_temp(struct radeon_device *rdev); -extern int rv770_get_temp(struct radeon_device *rdev); -extern int evergreen_get_temp(struct radeon_device *rdev); -extern int sumo_get_temp(struct radeon_device *rdev); -extern int si_get_temp(struct radeon_device *rdev); extern void evergreen_tiling_fields(unsigned tiling_flags, unsigned *bankw, unsigned *bankh, unsigned *mtaspect, unsigned *tile_split); @@ -1396,6 +1391,7 @@ struct radeon_asic { void (*set_pcie_lanes)(struct radeon_device *rdev, int lanes); void (*set_clock_gating)(struct radeon_device *rdev, int enable); int (*set_uvd_clocks)(struct radeon_device *rdev, u32 vclk, u32 dclk); + int (*get_temperature)(struct radeon_device *rdev); } pm; /* pageflipping */ struct { @@ -2065,6 +2061,7 @@ void radeon_ring_write(struct radeon_ring *ring, uint32_t v); #define radeon_set_pcie_lanes(rdev, l) (rdev)->asic->pm.set_pcie_lanes((rdev), (l)) #define radeon_set_clock_gating(rdev, e) (rdev)->asic->pm.set_clock_gating((rdev), (e)) #define radeon_set_uvd_clocks(rdev, v, d) (rdev)->asic->pm.set_uvd_clocks((rdev), (v), (d)) +#define radeon_get_temperature(rdev) (rdev)->asic->pm.get_temperature((rdev)) #define radeon_set_surface_reg(rdev, r, f, p, o, s) ((rdev)->asic->surface.set_reg((rdev), (r), (f), (p), (o), (s))) #define radeon_clear_surface_reg(rdev, r) ((rdev)->asic->surface.clear_reg((rdev), (r))) #define radeon_bandwidth_update(rdev) (rdev)->asic->display.bandwidth_update((rdev)) diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index d60adb3..f127ea2 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1016,6 +1016,7 @@ static struct radeon_asic r600_asic = { .get_pcie_lanes = &r600_get_pcie_lanes, .set_pcie_lanes = &r600_set_pcie_lanes, .set_clock_gating = NULL, + .get_temperature = &rv6xx_get_temp, }, .pflip = { .pre_page_flip = &rs600_pre_page_flip, @@ -1104,6 +1105,7 @@ static struct radeon_asic rs780_asic = { .get_pcie_lanes = NULL, .set_pcie_lanes = NULL, .set_clock_gating = NULL, + .get_temperature = &rv6xx_get_temp, }, .pflip = { .pre_page_flip = &rs600_pre_page_flip, @@ -1202,6 +1204,7 @@ static struct radeon_asic rv770_asic = { .set_pcie_lanes = &r600_set_pcie_lanes, .set_clock_gating = &radeon_atom_set_clock_gating, .set_uvd_clocks = &rv770_set_uvd_clocks, + .get_temperature = &rv770_get_temp, }, .pflip = { .pre_page_flip = &rs600_pre_page_flip, @@ -1300,6 +1303,7 @@ static struct radeon_asic evergreen_asic = { .set_pcie_lanes = &r600_set_pcie_lanes, .set_clock_gating = NULL, .set_uvd_clocks = &evergreen_set_uvd_clocks, + .get_temperature = &evergreen_get_temp, }, .pflip = { .pre_page_flip = &evergreen_pre_page_flip, @@ -1398,6 +1402,7 @@ static struct radeon_asic sumo_asic = { .set_pcie_lanes = NULL, .set_clock_gating = NULL, .set_uvd_clocks = &sumo_set_uvd_clocks, + .get_temperature = &sumo_get_temp, }, .pflip = { .pre_page_flip = &evergreen_pre_page_flip, @@ -1496,6 +1501,7 @@ static struct radeon_asic btc_asic = { .set_pcie_lanes = &r600_set_pcie_lanes, .set_clock_gating = NULL, .set_uvd_clocks = &evergreen_set_uvd_clocks, + .get_temperature = &evergreen_get_temp, }, .pflip = { .pre_page_flip = &evergreen_pre_page_flip, @@ -1637,6 +1643,7 @@ static struct radeon_asic cayman_asic = { .set_pcie_lanes = &r600_set_pcie_lanes, .set_clock_gating = NULL, .set_uvd_clocks = &evergreen_set_uvd_clocks, + .get_temperature = &evergreen_get_temp, }, .pflip = { .pre_page_flip = &evergreen_pre_page_flip, @@ -1915,6 +1922,7 @@ static struct radeon_asic si_asic = { .set_pcie_lanes = &r600_set_pcie_lanes, .set_clock_gating = NULL, .set_uvd_clocks = &si_set_uvd_clocks, + .get_temperature = &si_get_temp, }, .pflip = { .pre_page_flip = &evergreen_pre_page_flip, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 4c2f51f..f1dcb07 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -395,6 +395,7 @@ void r600_kms_blit_copy(struct radeon_device *rdev, int r600_mc_wait_for_idle(struct radeon_device *rdev); u32 r600_get_xclk(struct radeon_device *rdev); uint64_t r600_get_gpu_clock_counter(struct radeon_device *rdev); +int rv6xx_get_temp(struct radeon_device *rdev);
/* uvd */ int r600_uvd_init(struct radeon_device *rdev); @@ -428,6 +429,7 @@ int rv770_copy_dma(struct radeon_device *rdev, u32 rv770_get_xclk(struct radeon_device *rdev); int rv770_uvd_resume(struct radeon_device *rdev); int rv770_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk); +int rv770_get_temp(struct radeon_device *rdev);
/* * evergreen @@ -482,6 +484,8 @@ int evergreen_copy_dma(struct radeon_device *rdev, struct radeon_fence **fence); void evergreen_hdmi_enable(struct drm_encoder *encoder, bool enable); void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mode); +int evergreen_get_temp(struct radeon_device *rdev); +int sumo_get_temp(struct radeon_device *rdev);
/* * cayman @@ -552,6 +556,7 @@ void si_dma_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm) u32 si_get_xclk(struct radeon_device *rdev); uint64_t si_get_gpu_clock_counter(struct radeon_device *rdev); int si_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk); +int si_get_temp(struct radeon_device *rdev);
/* DCE8 - CIK */ void dce8_bandwidth_update(struct radeon_device *rdev); diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 788c64c..e8c1bea 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -434,27 +434,10 @@ static ssize_t radeon_hwmon_show_temp(struct device *dev, struct radeon_device *rdev = ddev->dev_private; int temp;
- switch (rdev->pm.int_thermal_type) { - case THERMAL_TYPE_RV6XX: - temp = rv6xx_get_temp(rdev); - break; - case THERMAL_TYPE_RV770: - temp = rv770_get_temp(rdev); - break; - case THERMAL_TYPE_EVERGREEN: - case THERMAL_TYPE_NI: - temp = evergreen_get_temp(rdev); - break; - case THERMAL_TYPE_SUMO: - temp = sumo_get_temp(rdev); - break; - case THERMAL_TYPE_SI: - temp = si_get_temp(rdev); - break; - default: + if (rdev->asic->pm.get_temperature) + temp = radeon_get_temperature(rdev); + else temp = 0; - break; - }
return snprintf(buf, PAGE_SIZE, "%d\n", temp); } @@ -492,8 +475,7 @@ static int radeon_hwmon_init(struct radeon_device *rdev) case THERMAL_TYPE_NI: case THERMAL_TYPE_SUMO: case THERMAL_TYPE_SI: - /* No support for TN yet */ - if (rdev->family == CHIP_ARUBA) + if (rdev->asic->pm.get_temperature == NULL) return err; rdev->pm.int_hwmon_dev = hwmon_device_register(rdev->dev); if (IS_ERR(rdev->pm.int_hwmon_dev)) {
From: Alex Deucher alexander.deucher@amd.com
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/ni.c | 8 ++++++++ drivers/gpu/drm/radeon/nid.h | 3 +++ drivers/gpu/drm/radeon/radeon_asic.c | 1 + drivers/gpu/drm/radeon/radeon_asic.h | 1 + 4 files changed, 13 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c index 8458330..f889461 100644 --- a/drivers/gpu/drm/radeon/ni.c +++ b/drivers/gpu/drm/radeon/ni.c @@ -692,6 +692,14 @@ out: return err; }
+int tn_get_temp(struct radeon_device *rdev) +{ + u32 temp = RREG32_SMC(TN_CURRENT_GNB_TEMP) & 0x7ff; + int actual_temp = (temp / 8) - 49; + + return actual_temp * 1000; +} + /* * Core functions */ diff --git a/drivers/gpu/drm/radeon/nid.h b/drivers/gpu/drm/radeon/nid.h index e226faf..7b8da52 100644 --- a/drivers/gpu/drm/radeon/nid.h +++ b/drivers/gpu/drm/radeon/nid.h @@ -489,6 +489,9 @@ # define CACHE_FLUSH_AND_INV_EVENT_TS (0x14 << 0) # define CACHE_FLUSH_AND_INV_EVENT (0x16 << 0)
+/* TN SMU registers */ +#define TN_CURRENT_GNB_TEMP 0x1F390 + /* * UVD */ diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index f127ea2..5736377 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1783,6 +1783,7 @@ static struct radeon_asic trinity_asic = { .set_pcie_lanes = NULL, .set_clock_gating = NULL, .set_uvd_clocks = &sumo_set_uvd_clocks, + .get_temperature = &tn_get_temp, }, .pflip = { .pre_page_flip = &evergreen_pre_page_flip, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index f1dcb07..8507cae 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -486,6 +486,7 @@ void evergreen_hdmi_enable(struct drm_encoder *encoder, bool enable); void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mode); int evergreen_get_temp(struct radeon_device *rdev); int sumo_get_temp(struct radeon_device *rdev); +int tn_get_temp(struct radeon_device *rdev);
/* * cayman
From: Alex Deucher alexander.deucher@amd.com
Avoids confusion and duplication.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/evergreen.c | 4 +-- drivers/gpu/drm/radeon/ni.c | 13 +-------- drivers/gpu/drm/radeon/r600.c | 25 +++++------------ drivers/gpu/drm/radeon/radeon_ucode.h | 47 +++++++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 33 deletions(-) create mode 100644 drivers/gpu/drm/radeon/radeon_ucode.h
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 9009dd4..6b559cb5 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -33,9 +33,7 @@ #include "avivod.h" #include "evergreen_reg.h" #include "evergreen_blit_shaders.h" - -#define EVERGREEN_PFP_UCODE_SIZE 1120 -#define EVERGREEN_PM4_UCODE_SIZE 1376 +#include "radeon_ucode.h"
static const u32 crtc_offsets[6] = { diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c index f889461..9284346 100644 --- a/drivers/gpu/drm/radeon/ni.c +++ b/drivers/gpu/drm/radeon/ni.c @@ -33,6 +33,7 @@ #include "atom.h" #include "ni_reg.h" #include "cayman_blit_shaders.h" +#include "radeon_ucode.h"
extern bool evergreen_is_display_hung(struct radeon_device *rdev); extern void evergreen_print_gpu_status_regs(struct radeon_device *rdev); @@ -47,18 +48,6 @@ extern void evergreen_pcie_gen2_enable(struct radeon_device *rdev); extern void si_rlc_fini(struct radeon_device *rdev); extern int si_rlc_init(struct radeon_device *rdev);
-#define EVERGREEN_PFP_UCODE_SIZE 1120 -#define EVERGREEN_PM4_UCODE_SIZE 1376 -#define EVERGREEN_RLC_UCODE_SIZE 768 -#define BTC_MC_UCODE_SIZE 6024 - -#define CAYMAN_PFP_UCODE_SIZE 2176 -#define CAYMAN_PM4_UCODE_SIZE 2176 -#define CAYMAN_RLC_UCODE_SIZE 1024 -#define CAYMAN_MC_UCODE_SIZE 6037 - -#define ARUBA_RLC_UCODE_SIZE 1536 - /* Firmware Names */ MODULE_FIRMWARE("radeon/BARTS_pfp.bin"); MODULE_FIRMWARE("radeon/BARTS_me.bin"); diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index 6948eb8..6089261 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -38,18 +38,7 @@ #include "r600d.h" #include "atom.h" #include "avivod.h" - -#define PFP_UCODE_SIZE 576 -#define PM4_UCODE_SIZE 1792 -#define RLC_UCODE_SIZE 768 -#define R700_PFP_UCODE_SIZE 848 -#define R700_PM4_UCODE_SIZE 1360 -#define R700_RLC_UCODE_SIZE 1024 -#define EVERGREEN_PFP_UCODE_SIZE 1120 -#define EVERGREEN_PM4_UCODE_SIZE 1376 -#define EVERGREEN_RLC_UCODE_SIZE 768 -#define CAYMAN_RLC_UCODE_SIZE 1024 -#define ARUBA_RLC_UCODE_SIZE 1536 +#include "radeon_ucode.h"
/* Firmware Names */ MODULE_FIRMWARE("radeon/R600_pfp.bin"); @@ -2246,9 +2235,9 @@ int r600_init_microcode(struct radeon_device *rdev) me_req_size = R700_PM4_UCODE_SIZE * 4; rlc_req_size = R700_RLC_UCODE_SIZE * 4; } else { - pfp_req_size = PFP_UCODE_SIZE * 4; - me_req_size = PM4_UCODE_SIZE * 12; - rlc_req_size = RLC_UCODE_SIZE * 4; + pfp_req_size = R600_PFP_UCODE_SIZE * 4; + me_req_size = R600_PM4_UCODE_SIZE * 12; + rlc_req_size = R600_RLC_UCODE_SIZE * 4; }
DRM_INFO("Loading %s Microcode\n", chip_name); @@ -2331,13 +2320,13 @@ static int r600_cp_load_microcode(struct radeon_device *rdev)
fw_data = (const __be32 *)rdev->me_fw->data; WREG32(CP_ME_RAM_WADDR, 0); - for (i = 0; i < PM4_UCODE_SIZE * 3; i++) + for (i = 0; i < R600_PM4_UCODE_SIZE * 3; i++) WREG32(CP_ME_RAM_DATA, be32_to_cpup(fw_data++));
fw_data = (const __be32 *)rdev->pfp_fw->data; WREG32(CP_PFP_UCODE_ADDR, 0); - for (i = 0; i < PFP_UCODE_SIZE; i++) + for (i = 0; i < R600_PFP_UCODE_SIZE; i++) WREG32(CP_PFP_UCODE_DATA, be32_to_cpup(fw_data++));
@@ -3839,7 +3828,7 @@ static int r600_rlc_init(struct radeon_device *rdev) WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++)); } } else { - for (i = 0; i < RLC_UCODE_SIZE; i++) { + for (i = 0; i < R600_RLC_UCODE_SIZE; i++) { WREG32(RLC_UCODE_ADDR, i); WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++)); } diff --git a/drivers/gpu/drm/radeon/radeon_ucode.h b/drivers/gpu/drm/radeon/radeon_ucode.h new file mode 100644 index 0000000..d2642b0 --- /dev/null +++ b/drivers/gpu/drm/radeon/radeon_ucode.h @@ -0,0 +1,47 @@ +/* + * Copyright 2012 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ +#ifndef __RADEON_UCODE_H__ +#define __RADEON_UCODE_H__ + +/* CP */ +#define R600_PFP_UCODE_SIZE 576 +#define R600_PM4_UCODE_SIZE 1792 +#define R700_PFP_UCODE_SIZE 848 +#define R700_PM4_UCODE_SIZE 1360 +#define EVERGREEN_PFP_UCODE_SIZE 1120 +#define EVERGREEN_PM4_UCODE_SIZE 1376 +#define CAYMAN_PFP_UCODE_SIZE 2176 +#define CAYMAN_PM4_UCODE_SIZE 2176 + +/* RLC */ +#define R600_RLC_UCODE_SIZE 768 +#define R700_RLC_UCODE_SIZE 1024 +#define EVERGREEN_RLC_UCODE_SIZE 768 +#define CAYMAN_RLC_UCODE_SIZE 1024 +#define ARUBA_RLC_UCODE_SIZE 1536 + +/* MC */ +#define BTC_MC_UCODE_SIZE 6024 +#define CAYMAN_MC_UCODE_SIZE 6037 + +#endif
From: Alex Deucher alexander.deucher@amd.com
This is required for certain advanced functionality.
v2: save/restore list takes dword offsets v3: rebase on gpu reset changes
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/clearstate_cayman.h | 1081 +++++++++++++++++++++++++ drivers/gpu/drm/radeon/clearstate_defs.h | 44 + drivers/gpu/drm/radeon/clearstate_evergreen.h | 1080 ++++++++++++++++++++++++ drivers/gpu/drm/radeon/evergreen.c | 339 ++++++++ drivers/gpu/drm/radeon/evergreend.h | 19 + drivers/gpu/drm/radeon/ni.c | 141 ++++- drivers/gpu/drm/radeon/r600.c | 43 +- drivers/gpu/drm/radeon/r600d.h | 4 - drivers/gpu/drm/radeon/radeon.h | 13 +- 9 files changed, 2721 insertions(+), 43 deletions(-) create mode 100644 drivers/gpu/drm/radeon/clearstate_cayman.h create mode 100644 drivers/gpu/drm/radeon/clearstate_defs.h create mode 100644 drivers/gpu/drm/radeon/clearstate_evergreen.h
diff --git a/drivers/gpu/drm/radeon/clearstate_cayman.h b/drivers/gpu/drm/radeon/clearstate_cayman.h new file mode 100644 index 0000000..c003394 --- /dev/null +++ b/drivers/gpu/drm/radeon/clearstate_cayman.h @@ -0,0 +1,1081 @@ +/* + * Copyright 2012 Advanced Micro Devices, 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. + * + */ + +static const u32 SECT_CONTEXT_def_1[] = +{ + 0x00000000, // DB_RENDER_CONTROL + 0x00000000, // DB_COUNT_CONTROL + 0x00000000, // DB_DEPTH_VIEW + 0x00000000, // DB_RENDER_OVERRIDE + 0x00000000, // DB_RENDER_OVERRIDE2 + 0x00000000, // DB_HTILE_DATA_BASE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0x00000000, // DB_STENCIL_CLEAR + 0x00000000, // DB_DEPTH_CLEAR + 0x00000000, // PA_SC_SCREEN_SCISSOR_TL + 0x40004000, // PA_SC_SCREEN_SCISSOR_BR + 0, // HOLE + 0x00000000, // DB_DEPTH_INFO + 0x00000000, // DB_Z_INFO + 0x00000000, // DB_STENCIL_INFO + 0x00000000, // DB_Z_READ_BASE + 0x00000000, // DB_STENCIL_READ_BASE + 0x00000000, // DB_Z_WRITE_BASE + 0x00000000, // DB_STENCIL_WRITE_BASE + 0x00000000, // DB_DEPTH_SIZE + 0x00000000, // DB_DEPTH_SLICE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_0 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_1 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_2 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_3 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_4 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_5 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_6 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_7 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_8 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_9 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_10 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_11 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_12 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_13 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_14 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_15 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_0 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_1 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_2 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_3 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_4 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_5 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_6 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_7 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_8 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_9 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_10 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_11 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_12 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_13 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_14 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_15 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_0 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_1 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_2 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_3 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_4 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_5 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_6 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_7 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_8 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_9 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_10 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_11 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_12 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_13 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_14 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_15 + 0x00000000, // PA_SC_WINDOW_OFFSET + 0x80000000, // PA_SC_WINDOW_SCISSOR_TL + 0x40004000, // PA_SC_WINDOW_SCISSOR_BR + 0x0000ffff, // PA_SC_CLIPRECT_RULE + 0x00000000, // PA_SC_CLIPRECT_0_TL + 0x40004000, // PA_SC_CLIPRECT_0_BR + 0x00000000, // PA_SC_CLIPRECT_1_TL + 0x40004000, // PA_SC_CLIPRECT_1_BR + 0x00000000, // PA_SC_CLIPRECT_2_TL + 0x40004000, // PA_SC_CLIPRECT_2_BR + 0x00000000, // PA_SC_CLIPRECT_3_TL + 0x40004000, // PA_SC_CLIPRECT_3_BR + 0xaa99aaaa, // PA_SC_EDGERULE + 0x00000000, // PA_SU_HARDWARE_SCREEN_OFFSET + 0xffffffff, // CB_TARGET_MASK + 0xffffffff, // CB_SHADER_MASK + 0x80000000, // PA_SC_GENERIC_SCISSOR_TL + 0x40004000, // PA_SC_GENERIC_SCISSOR_BR + 0x00000000, // COHER_DEST_BASE_0 + 0x00000000, // COHER_DEST_BASE_1 + 0x80000000, // PA_SC_VPORT_SCISSOR_0_TL + 0x40004000, // PA_SC_VPORT_SCISSOR_0_BR + 0x80000000, // PA_SC_VPORT_SCISSOR_1_TL + 0x40004000, // PA_SC_VPORT_SCISSOR_1_BR + 0x80000000, // PA_SC_VPORT_SCISSOR_2_TL + 0x40004000, // PA_SC_VPORT_SCISSOR_2_BR + 0x80000000, // PA_SC_VPORT_SCISSOR_3_TL + 0x40004000, // PA_SC_VPORT_SCISSOR_3_BR + 0x80000000, // PA_SC_VPORT_SCISSOR_4_TL + 0x40004000, // PA_SC_VPORT_SCISSOR_4_BR + 0x80000000, // PA_SC_VPORT_SCISSOR_5_TL + 0x40004000, // PA_SC_VPORT_SCISSOR_5_BR + 0x80000000, // PA_SC_VPORT_SCISSOR_6_TL + 0x40004000, // PA_SC_VPORT_SCISSOR_6_BR + 0x80000000, // PA_SC_VPORT_SCISSOR_7_TL + 0x40004000, // PA_SC_VPORT_SCISSOR_7_BR + 0x80000000, // PA_SC_VPORT_SCISSOR_8_TL + 0x40004000, // PA_SC_VPORT_SCISSOR_8_BR + 0x80000000, // PA_SC_VPORT_SCISSOR_9_TL + 0x40004000, // PA_SC_VPORT_SCISSOR_9_BR + 0x80000000, // PA_SC_VPORT_SCISSOR_10_TL + 0x40004000, // PA_SC_VPORT_SCISSOR_10_BR + 0x80000000, // PA_SC_VPORT_SCISSOR_11_TL + 0x40004000, // PA_SC_VPORT_SCISSOR_11_BR + 0x80000000, // PA_SC_VPORT_SCISSOR_12_TL + 0x40004000, // PA_SC_VPORT_SCISSOR_12_BR + 0x80000000, // PA_SC_VPORT_SCISSOR_13_TL + 0x40004000, // PA_SC_VPORT_SCISSOR_13_BR + 0x80000000, // PA_SC_VPORT_SCISSOR_14_TL + 0x40004000, // PA_SC_VPORT_SCISSOR_14_BR + 0x80000000, // PA_SC_VPORT_SCISSOR_15_TL + 0x40004000, // PA_SC_VPORT_SCISSOR_15_BR + 0x00000000, // PA_SC_VPORT_ZMIN_0 + 0x3f800000, // PA_SC_VPORT_ZMAX_0 + 0x00000000, // PA_SC_VPORT_ZMIN_1 + 0x3f800000, // PA_SC_VPORT_ZMAX_1 + 0x00000000, // PA_SC_VPORT_ZMIN_2 + 0x3f800000, // PA_SC_VPORT_ZMAX_2 + 0x00000000, // PA_SC_VPORT_ZMIN_3 + 0x3f800000, // PA_SC_VPORT_ZMAX_3 + 0x00000000, // PA_SC_VPORT_ZMIN_4 + 0x3f800000, // PA_SC_VPORT_ZMAX_4 + 0x00000000, // PA_SC_VPORT_ZMIN_5 + 0x3f800000, // PA_SC_VPORT_ZMAX_5 + 0x00000000, // PA_SC_VPORT_ZMIN_6 + 0x3f800000, // PA_SC_VPORT_ZMAX_6 + 0x00000000, // PA_SC_VPORT_ZMIN_7 + 0x3f800000, // PA_SC_VPORT_ZMAX_7 + 0x00000000, // PA_SC_VPORT_ZMIN_8 + 0x3f800000, // PA_SC_VPORT_ZMAX_8 + 0x00000000, // PA_SC_VPORT_ZMIN_9 + 0x3f800000, // PA_SC_VPORT_ZMAX_9 + 0x00000000, // PA_SC_VPORT_ZMIN_10 + 0x3f800000, // PA_SC_VPORT_ZMAX_10 + 0x00000000, // PA_SC_VPORT_ZMIN_11 + 0x3f800000, // PA_SC_VPORT_ZMAX_11 + 0x00000000, // PA_SC_VPORT_ZMIN_12 + 0x3f800000, // PA_SC_VPORT_ZMAX_12 + 0x00000000, // PA_SC_VPORT_ZMIN_13 + 0x3f800000, // PA_SC_VPORT_ZMAX_13 + 0x00000000, // PA_SC_VPORT_ZMIN_14 + 0x3f800000, // PA_SC_VPORT_ZMAX_14 + 0x00000000, // PA_SC_VPORT_ZMIN_15 + 0x3f800000, // PA_SC_VPORT_ZMAX_15 + 0x00000000, // SX_MISC + 0x00000000, // SX_SURFACE_SYNC + 0x00000000, // SX_SCATTER_EXPORT_BASE + 0x00000000, // SX_SCATTER_EXPORT_SIZE + 0x00000000, // CP_PERFMON_CNTX_CNTL + 0x00000000, // CP_RINGID + 0x00000000, // CP_VMID + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0x00000000, // SQ_VTX_SEMANTIC_0 + 0x00000000, // SQ_VTX_SEMANTIC_1 + 0x00000000, // SQ_VTX_SEMANTIC_2 + 0x00000000, // SQ_VTX_SEMANTIC_3 + 0x00000000, // SQ_VTX_SEMANTIC_4 + 0x00000000, // SQ_VTX_SEMANTIC_5 + 0x00000000, // SQ_VTX_SEMANTIC_6 + 0x00000000, // SQ_VTX_SEMANTIC_7 + 0x00000000, // SQ_VTX_SEMANTIC_8 + 0x00000000, // SQ_VTX_SEMANTIC_9 + 0x00000000, // SQ_VTX_SEMANTIC_10 + 0x00000000, // SQ_VTX_SEMANTIC_11 + 0x00000000, // SQ_VTX_SEMANTIC_12 + 0x00000000, // SQ_VTX_SEMANTIC_13 + 0x00000000, // SQ_VTX_SEMANTIC_14 + 0x00000000, // SQ_VTX_SEMANTIC_15 + 0x00000000, // SQ_VTX_SEMANTIC_16 + 0x00000000, // SQ_VTX_SEMANTIC_17 + 0x00000000, // SQ_VTX_SEMANTIC_18 + 0x00000000, // SQ_VTX_SEMANTIC_19 + 0x00000000, // SQ_VTX_SEMANTIC_20 + 0x00000000, // SQ_VTX_SEMANTIC_21 + 0x00000000, // SQ_VTX_SEMANTIC_22 + 0x00000000, // SQ_VTX_SEMANTIC_23 + 0x00000000, // SQ_VTX_SEMANTIC_24 + 0x00000000, // SQ_VTX_SEMANTIC_25 + 0x00000000, // SQ_VTX_SEMANTIC_26 + 0x00000000, // SQ_VTX_SEMANTIC_27 + 0x00000000, // SQ_VTX_SEMANTIC_28 + 0x00000000, // SQ_VTX_SEMANTIC_29 + 0x00000000, // SQ_VTX_SEMANTIC_30 + 0x00000000, // SQ_VTX_SEMANTIC_31 + 0xffffffff, // VGT_MAX_VTX_INDX + 0x00000000, // VGT_MIN_VTX_INDX + 0x00000000, // VGT_INDX_OFFSET + 0x00000000, // VGT_MULTI_PRIM_IB_RESET_INDX + 0x00000000, // SX_ALPHA_TEST_CONTROL + 0x00000000, // CB_BLEND_RED + 0x00000000, // CB_BLEND_GREEN + 0x00000000, // CB_BLEND_BLUE + 0x00000000, // CB_BLEND_ALPHA + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0x00000000, // DB_STENCILREFMASK + 0x00000000, // DB_STENCILREFMASK_BF + 0x00000000, // SX_ALPHA_REF + 0x00000000, // PA_CL_VPORT_XSCALE + 0x00000000, // PA_CL_VPORT_XOFFSET + 0x00000000, // PA_CL_VPORT_YSCALE + 0x00000000, // PA_CL_VPORT_YOFFSET + 0x00000000, // PA_CL_VPORT_ZSCALE + 0x00000000, // PA_CL_VPORT_ZOFFSET + 0x00000000, // PA_CL_VPORT_XSCALE_1 + 0x00000000, // PA_CL_VPORT_XOFFSET_1 + 0x00000000, // PA_CL_VPORT_YSCALE_1 + 0x00000000, // PA_CL_VPORT_YOFFSET_1 + 0x00000000, // PA_CL_VPORT_ZSCALE_1 + 0x00000000, // PA_CL_VPORT_ZOFFSET_1 + 0x00000000, // PA_CL_VPORT_XSCALE_2 + 0x00000000, // PA_CL_VPORT_XOFFSET_2 + 0x00000000, // PA_CL_VPORT_YSCALE_2 + 0x00000000, // PA_CL_VPORT_YOFFSET_2 + 0x00000000, // PA_CL_VPORT_ZSCALE_2 + 0x00000000, // PA_CL_VPORT_ZOFFSET_2 + 0x00000000, // PA_CL_VPORT_XSCALE_3 + 0x00000000, // PA_CL_VPORT_XOFFSET_3 + 0x00000000, // PA_CL_VPORT_YSCALE_3 + 0x00000000, // PA_CL_VPORT_YOFFSET_3 + 0x00000000, // PA_CL_VPORT_ZSCALE_3 + 0x00000000, // PA_CL_VPORT_ZOFFSET_3 + 0x00000000, // PA_CL_VPORT_XSCALE_4 + 0x00000000, // PA_CL_VPORT_XOFFSET_4 + 0x00000000, // PA_CL_VPORT_YSCALE_4 + 0x00000000, // PA_CL_VPORT_YOFFSET_4 + 0x00000000, // PA_CL_VPORT_ZSCALE_4 + 0x00000000, // PA_CL_VPORT_ZOFFSET_4 + 0x00000000, // PA_CL_VPORT_XSCALE_5 + 0x00000000, // PA_CL_VPORT_XOFFSET_5 + 0x00000000, // PA_CL_VPORT_YSCALE_5 + 0x00000000, // PA_CL_VPORT_YOFFSET_5 + 0x00000000, // PA_CL_VPORT_ZSCALE_5 + 0x00000000, // PA_CL_VPORT_ZOFFSET_5 + 0x00000000, // PA_CL_VPORT_XSCALE_6 + 0x00000000, // PA_CL_VPORT_XOFFSET_6 + 0x00000000, // PA_CL_VPORT_YSCALE_6 + 0x00000000, // PA_CL_VPORT_YOFFSET_6 + 0x00000000, // PA_CL_VPORT_ZSCALE_6 + 0x00000000, // PA_CL_VPORT_ZOFFSET_6 + 0x00000000, // PA_CL_VPORT_XSCALE_7 + 0x00000000, // PA_CL_VPORT_XOFFSET_7 + 0x00000000, // PA_CL_VPORT_YSCALE_7 + 0x00000000, // PA_CL_VPORT_YOFFSET_7 + 0x00000000, // PA_CL_VPORT_ZSCALE_7 + 0x00000000, // PA_CL_VPORT_ZOFFSET_7 + 0x00000000, // PA_CL_VPORT_XSCALE_8 + 0x00000000, // PA_CL_VPORT_XOFFSET_8 + 0x00000000, // PA_CL_VPORT_YSCALE_8 + 0x00000000, // PA_CL_VPORT_YOFFSET_8 + 0x00000000, // PA_CL_VPORT_ZSCALE_8 + 0x00000000, // PA_CL_VPORT_ZOFFSET_8 + 0x00000000, // PA_CL_VPORT_XSCALE_9 + 0x00000000, // PA_CL_VPORT_XOFFSET_9 + 0x00000000, // PA_CL_VPORT_YSCALE_9 + 0x00000000, // PA_CL_VPORT_YOFFSET_9 + 0x00000000, // PA_CL_VPORT_ZSCALE_9 + 0x00000000, // PA_CL_VPORT_ZOFFSET_9 + 0x00000000, // PA_CL_VPORT_XSCALE_10 + 0x00000000, // PA_CL_VPORT_XOFFSET_10 + 0x00000000, // PA_CL_VPORT_YSCALE_10 + 0x00000000, // PA_CL_VPORT_YOFFSET_10 + 0x00000000, // PA_CL_VPORT_ZSCALE_10 + 0x00000000, // PA_CL_VPORT_ZOFFSET_10 + 0x00000000, // PA_CL_VPORT_XSCALE_11 + 0x00000000, // PA_CL_VPORT_XOFFSET_11 + 0x00000000, // PA_CL_VPORT_YSCALE_11 + 0x00000000, // PA_CL_VPORT_YOFFSET_11 + 0x00000000, // PA_CL_VPORT_ZSCALE_11 + 0x00000000, // PA_CL_VPORT_ZOFFSET_11 + 0x00000000, // PA_CL_VPORT_XSCALE_12 + 0x00000000, // PA_CL_VPORT_XOFFSET_12 + 0x00000000, // PA_CL_VPORT_YSCALE_12 + 0x00000000, // PA_CL_VPORT_YOFFSET_12 + 0x00000000, // PA_CL_VPORT_ZSCALE_12 + 0x00000000, // PA_CL_VPORT_ZOFFSET_12 + 0x00000000, // PA_CL_VPORT_XSCALE_13 + 0x00000000, // PA_CL_VPORT_XOFFSET_13 + 0x00000000, // PA_CL_VPORT_YSCALE_13 + 0x00000000, // PA_CL_VPORT_YOFFSET_13 + 0x00000000, // PA_CL_VPORT_ZSCALE_13 + 0x00000000, // PA_CL_VPORT_ZOFFSET_13 + 0x00000000, // PA_CL_VPORT_XSCALE_14 + 0x00000000, // PA_CL_VPORT_XOFFSET_14 + 0x00000000, // PA_CL_VPORT_YSCALE_14 + 0x00000000, // PA_CL_VPORT_YOFFSET_14 + 0x00000000, // PA_CL_VPORT_ZSCALE_14 + 0x00000000, // PA_CL_VPORT_ZOFFSET_14 + 0x00000000, // PA_CL_VPORT_XSCALE_15 + 0x00000000, // PA_CL_VPORT_XOFFSET_15 + 0x00000000, // PA_CL_VPORT_YSCALE_15 + 0x00000000, // PA_CL_VPORT_YOFFSET_15 + 0x00000000, // PA_CL_VPORT_ZSCALE_15 + 0x00000000, // PA_CL_VPORT_ZOFFSET_15 + 0x00000000, // PA_CL_UCP_0_X + 0x00000000, // PA_CL_UCP_0_Y + 0x00000000, // PA_CL_UCP_0_Z + 0x00000000, // PA_CL_UCP_0_W + 0x00000000, // PA_CL_UCP_1_X + 0x00000000, // PA_CL_UCP_1_Y + 0x00000000, // PA_CL_UCP_1_Z + 0x00000000, // PA_CL_UCP_1_W + 0x00000000, // PA_CL_UCP_2_X + 0x00000000, // PA_CL_UCP_2_Y + 0x00000000, // PA_CL_UCP_2_Z + 0x00000000, // PA_CL_UCP_2_W + 0x00000000, // PA_CL_UCP_3_X + 0x00000000, // PA_CL_UCP_3_Y + 0x00000000, // PA_CL_UCP_3_Z + 0x00000000, // PA_CL_UCP_3_W + 0x00000000, // PA_CL_UCP_4_X + 0x00000000, // PA_CL_UCP_4_Y + 0x00000000, // PA_CL_UCP_4_Z + 0x00000000, // PA_CL_UCP_4_W + 0x00000000, // PA_CL_UCP_5_X + 0x00000000, // PA_CL_UCP_5_Y + 0x00000000, // PA_CL_UCP_5_Z + 0x00000000, // PA_CL_UCP_5_W + 0x00000000, // SPI_VS_OUT_ID_0 + 0x00000000, // SPI_VS_OUT_ID_1 + 0x00000000, // SPI_VS_OUT_ID_2 + 0x00000000, // SPI_VS_OUT_ID_3 + 0x00000000, // SPI_VS_OUT_ID_4 + 0x00000000, // SPI_VS_OUT_ID_5 + 0x00000000, // SPI_VS_OUT_ID_6 + 0x00000000, // SPI_VS_OUT_ID_7 + 0x00000000, // SPI_VS_OUT_ID_8 + 0x00000000, // SPI_VS_OUT_ID_9 + 0x00000000, // SPI_PS_INPUT_CNTL_0 + 0x00000000, // SPI_PS_INPUT_CNTL_1 + 0x00000000, // SPI_PS_INPUT_CNTL_2 + 0x00000000, // SPI_PS_INPUT_CNTL_3 + 0x00000000, // SPI_PS_INPUT_CNTL_4 + 0x00000000, // SPI_PS_INPUT_CNTL_5 + 0x00000000, // SPI_PS_INPUT_CNTL_6 + 0x00000000, // SPI_PS_INPUT_CNTL_7 + 0x00000000, // SPI_PS_INPUT_CNTL_8 + 0x00000000, // SPI_PS_INPUT_CNTL_9 + 0x00000000, // SPI_PS_INPUT_CNTL_10 + 0x00000000, // SPI_PS_INPUT_CNTL_11 + 0x00000000, // SPI_PS_INPUT_CNTL_12 + 0x00000000, // SPI_PS_INPUT_CNTL_13 + 0x00000000, // SPI_PS_INPUT_CNTL_14 + 0x00000000, // SPI_PS_INPUT_CNTL_15 + 0x00000000, // SPI_PS_INPUT_CNTL_16 + 0x00000000, // SPI_PS_INPUT_CNTL_17 + 0x00000000, // SPI_PS_INPUT_CNTL_18 + 0x00000000, // SPI_PS_INPUT_CNTL_19 + 0x00000000, // SPI_PS_INPUT_CNTL_20 + 0x00000000, // SPI_PS_INPUT_CNTL_21 + 0x00000000, // SPI_PS_INPUT_CNTL_22 + 0x00000000, // SPI_PS_INPUT_CNTL_23 + 0x00000000, // SPI_PS_INPUT_CNTL_24 + 0x00000000, // SPI_PS_INPUT_CNTL_25 + 0x00000000, // SPI_PS_INPUT_CNTL_26 + 0x00000000, // SPI_PS_INPUT_CNTL_27 + 0x00000000, // SPI_PS_INPUT_CNTL_28 + 0x00000000, // SPI_PS_INPUT_CNTL_29 + 0x00000000, // SPI_PS_INPUT_CNTL_30 + 0x00000000, // SPI_PS_INPUT_CNTL_31 + 0x00000000, // SPI_VS_OUT_CONFIG + 0x00000001, // SPI_THREAD_GROUPING + 0x00000002, // SPI_PS_IN_CONTROL_0 + 0x00000000, // SPI_PS_IN_CONTROL_1 + 0x00000000, // SPI_INTERP_CONTROL_0 + 0x00000000, // SPI_INPUT_Z + 0x00000000, // SPI_FOG_CNTL + 0x00000000, // SPI_BARYC_CNTL + 0x00000000, // SPI_PS_IN_CONTROL_2 + 0x00000000, // SPI_COMPUTE_INPUT_CNTL + 0x00000000, // SPI_COMPUTE_NUM_THREAD_X + 0x00000000, // SPI_COMPUTE_NUM_THREAD_Y + 0x00000000, // SPI_COMPUTE_NUM_THREAD_Z + 0x00000000, // SPI_GPR_MGMT + 0x00000000, // SPI_LDS_MGMT + 0x00000000, // SPI_STACK_MGMT + 0x00000000, // SPI_WAVE_MGMT_1 + 0x00000000, // SPI_WAVE_MGMT_2 + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0x00000000, // GDS_ADDR_BASE + 0x00003fff, // GDS_ADDR_SIZE + 0, // HOLE + 0, // HOLE + 0x00000000, // GDS_ORDERED_COUNT + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0x00000000, // GDS_APPEND_CONSUME_UAV0 + 0x00000000, // GDS_APPEND_CONSUME_UAV1 + 0x00000000, // GDS_APPEND_CONSUME_UAV2 + 0x00000000, // GDS_APPEND_CONSUME_UAV3 + 0x00000000, // GDS_APPEND_CONSUME_UAV4 + 0x00000000, // GDS_APPEND_CONSUME_UAV5 + 0x00000000, // GDS_APPEND_CONSUME_UAV6 + 0x00000000, // GDS_APPEND_CONSUME_UAV7 + 0x00000000, // GDS_APPEND_CONSUME_UAV8 + 0x00000000, // GDS_APPEND_CONSUME_UAV9 + 0x00000000, // GDS_APPEND_CONSUME_UAV10 + 0x00000000, // GDS_APPEND_CONSUME_UAV11 + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0x00000000, // CB_BLEND0_CONTROL + 0x00000000, // CB_BLEND1_CONTROL + 0x00000000, // CB_BLEND2_CONTROL + 0x00000000, // CB_BLEND3_CONTROL + 0x00000000, // CB_BLEND4_CONTROL + 0x00000000, // CB_BLEND5_CONTROL + 0x00000000, // CB_BLEND6_CONTROL + 0x00000000, // CB_BLEND7_CONTROL +}; +static const u32 SECT_CONTEXT_def_2[] = +{ + 0x00000000, // PA_CL_POINT_X_RAD + 0x00000000, // PA_CL_POINT_Y_RAD + 0x00000000, // PA_CL_POINT_SIZE + 0x00000000, // PA_CL_POINT_CULL_RAD + 0x00000000, // VGT_DMA_BASE_HI + 0x00000000, // VGT_DMA_BASE +}; +static const u32 SECT_CONTEXT_def_3[] = +{ + 0x00000000, // DB_DEPTH_CONTROL + 0x00000000, // DB_EQAA + 0x00000000, // CB_COLOR_CONTROL + 0x00000200, // DB_SHADER_CONTROL + 0x00000000, // PA_CL_CLIP_CNTL + 0x00000000, // PA_SU_SC_MODE_CNTL + 0x00000000, // PA_CL_VTE_CNTL + 0x00000000, // PA_CL_VS_OUT_CNTL + 0x00000000, // PA_CL_NANINF_CNTL + 0x00000000, // PA_SU_LINE_STIPPLE_CNTL + 0x00000000, // PA_SU_LINE_STIPPLE_SCALE + 0x00000000, // PA_SU_PRIM_FILTER_CNTL + 0x00000000, // SQ_LSTMP_RING_ITEMSIZE + 0x00000000, // SQ_HSTMP_RING_ITEMSIZE + 0, // HOLE + 0, // HOLE + 0x00000000, // SQ_PGM_START_PS + 0x00000000, // SQ_PGM_RESOURCES_PS + 0x00000000, // SQ_PGM_RESOURCES_2_PS + 0x00000000, // SQ_PGM_EXPORTS_PS + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0x00000000, // SQ_PGM_START_VS + 0x00000000, // SQ_PGM_RESOURCES_VS + 0x00000000, // SQ_PGM_RESOURCES_2_VS + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0x00000000, // SQ_PGM_START_GS + 0x00000000, // SQ_PGM_RESOURCES_GS + 0x00000000, // SQ_PGM_RESOURCES_2_GS + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0x00000000, // SQ_PGM_START_ES + 0x00000000, // SQ_PGM_RESOURCES_ES + 0x00000000, // SQ_PGM_RESOURCES_2_ES + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0x00000000, // SQ_PGM_START_FS + 0x00000000, // SQ_PGM_RESOURCES_FS + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0x00000000, // SQ_PGM_START_HS + 0x00000000, // SQ_PGM_RESOURCES_HS + 0x00000000, // SQ_PGM_RESOURCES_2_HS + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0x00000000, // SQ_PGM_START_LS + 0x00000000, // SQ_PGM_RESOURCES_LS + 0x00000000, // SQ_PGM_RESOURCES_2_LS +}; +static const u32 SECT_CONTEXT_def_4[] = +{ + 0x00000000, // SQ_LDS_ALLOC + 0x00000000, // SQ_LDS_ALLOC_PS + 0x00000000, // SQ_VTX_SEMANTIC_CLEAR + 0, // HOLE + 0x00000000, // SQ_THREAD_TRACE_CTRL + 0, // HOLE + 0x00000000, // SQ_ESGS_RING_ITEMSIZE + 0x00000000, // SQ_GSVS_RING_ITEMSIZE + 0x00000000, // SQ_ESTMP_RING_ITEMSIZE + 0x00000000, // SQ_GSTMP_RING_ITEMSIZE + 0x00000000, // SQ_VSTMP_RING_ITEMSIZE + 0x00000000, // SQ_PSTMP_RING_ITEMSIZE + 0, // HOLE + 0x00000000, // SQ_GS_VERT_ITEMSIZE + 0x00000000, // SQ_GS_VERT_ITEMSIZE_1 + 0x00000000, // SQ_GS_VERT_ITEMSIZE_2 + 0x00000000, // SQ_GS_VERT_ITEMSIZE_3 + 0x00000000, // SQ_GSVS_RING_OFFSET_1 + 0x00000000, // SQ_GSVS_RING_OFFSET_2 + 0x00000000, // SQ_GSVS_RING_OFFSET_3 + 0x00000000, // SQ_GWS_RING_OFFSET + 0, // HOLE + 0x00000000, // SQ_ALU_CONST_CACHE_PS_0 + 0x00000000, // SQ_ALU_CONST_CACHE_PS_1 + 0x00000000, // SQ_ALU_CONST_CACHE_PS_2 + 0x00000000, // SQ_ALU_CONST_CACHE_PS_3 + 0x00000000, // SQ_ALU_CONST_CACHE_PS_4 + 0x00000000, // SQ_ALU_CONST_CACHE_PS_5 + 0x00000000, // SQ_ALU_CONST_CACHE_PS_6 + 0x00000000, // SQ_ALU_CONST_CACHE_PS_7 + 0x00000000, // SQ_ALU_CONST_CACHE_PS_8 + 0x00000000, // SQ_ALU_CONST_CACHE_PS_9 + 0x00000000, // SQ_ALU_CONST_CACHE_PS_10 + 0x00000000, // SQ_ALU_CONST_CACHE_PS_11 + 0x00000000, // SQ_ALU_CONST_CACHE_PS_12 + 0x00000000, // SQ_ALU_CONST_CACHE_PS_13 + 0x00000000, // SQ_ALU_CONST_CACHE_PS_14 + 0x00000000, // SQ_ALU_CONST_CACHE_PS_15 + 0x00000000, // SQ_ALU_CONST_CACHE_VS_0 + 0x00000000, // SQ_ALU_CONST_CACHE_VS_1 + 0x00000000, // SQ_ALU_CONST_CACHE_VS_2 + 0x00000000, // SQ_ALU_CONST_CACHE_VS_3 + 0x00000000, // SQ_ALU_CONST_CACHE_VS_4 + 0x00000000, // SQ_ALU_CONST_CACHE_VS_5 + 0x00000000, // SQ_ALU_CONST_CACHE_VS_6 + 0x00000000, // SQ_ALU_CONST_CACHE_VS_7 + 0x00000000, // SQ_ALU_CONST_CACHE_VS_8 + 0x00000000, // SQ_ALU_CONST_CACHE_VS_9 + 0x00000000, // SQ_ALU_CONST_CACHE_VS_10 + 0x00000000, // SQ_ALU_CONST_CACHE_VS_11 + 0x00000000, // SQ_ALU_CONST_CACHE_VS_12 + 0x00000000, // SQ_ALU_CONST_CACHE_VS_13 + 0x00000000, // SQ_ALU_CONST_CACHE_VS_14 + 0x00000000, // SQ_ALU_CONST_CACHE_VS_15 + 0x00000000, // SQ_ALU_CONST_CACHE_GS_0 + 0x00000000, // SQ_ALU_CONST_CACHE_GS_1 + 0x00000000, // SQ_ALU_CONST_CACHE_GS_2 + 0x00000000, // SQ_ALU_CONST_CACHE_GS_3 + 0x00000000, // SQ_ALU_CONST_CACHE_GS_4 + 0x00000000, // SQ_ALU_CONST_CACHE_GS_5 + 0x00000000, // SQ_ALU_CONST_CACHE_GS_6 + 0x00000000, // SQ_ALU_CONST_CACHE_GS_7 + 0x00000000, // SQ_ALU_CONST_CACHE_GS_8 + 0x00000000, // SQ_ALU_CONST_CACHE_GS_9 + 0x00000000, // SQ_ALU_CONST_CACHE_GS_10 + 0x00000000, // SQ_ALU_CONST_CACHE_GS_11 + 0x00000000, // SQ_ALU_CONST_CACHE_GS_12 + 0x00000000, // SQ_ALU_CONST_CACHE_GS_13 + 0x00000000, // SQ_ALU_CONST_CACHE_GS_14 + 0x00000000, // SQ_ALU_CONST_CACHE_GS_15 + 0x00000000, // PA_SU_POINT_SIZE + 0x00000000, // PA_SU_POINT_MINMAX + 0x00000000, // PA_SU_LINE_CNTL + 0x00000000, // PA_SC_LINE_STIPPLE + 0x00000000, // VGT_OUTPUT_PATH_CNTL + 0x00000000, // VGT_HOS_CNTL + 0x00000000, // VGT_HOS_MAX_TESS_LEVEL + 0x00000000, // VGT_HOS_MIN_TESS_LEVEL + 0x00000000, // VGT_HOS_REUSE_DEPTH + 0x00000000, // VGT_GROUP_PRIM_TYPE + 0x00000000, // VGT_GROUP_FIRST_DECR + 0x00000000, // VGT_GROUP_DECR + 0x00000000, // VGT_GROUP_VECT_0_CNTL + 0x00000000, // VGT_GROUP_VECT_1_CNTL + 0x00000000, // VGT_GROUP_VECT_0_FMT_CNTL + 0x00000000, // VGT_GROUP_VECT_1_FMT_CNTL + 0x00000000, // VGT_GS_MODE + 0, // HOLE + 0x00000000, // PA_SC_MODE_CNTL_0 + 0x00000000, // PA_SC_MODE_CNTL_1 + 0x00000000, // VGT_ENHANCE + 0x00000100, // VGT_GS_PER_ES + 0x00000080, // VGT_ES_PER_GS + 0x00000002, // VGT_GS_PER_VS + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0x00000000, // VGT_GS_OUT_PRIM_TYPE + 0x00000000, // IA_ENHANCE +}; +static const u32 SECT_CONTEXT_def_5[] = +{ + 0x00000000, // VGT_DMA_MAX_SIZE + 0x00000000, // VGT_DMA_INDEX_TYPE + 0, // HOLE + 0x00000000, // VGT_PRIMITIVEID_EN + 0x00000000, // VGT_DMA_NUM_INSTANCES +}; +static const u32 SECT_CONTEXT_def_6[] = +{ + 0x00000000, // VGT_MULTI_PRIM_IB_RESET_EN + 0, // HOLE + 0, // HOLE + 0x00000000, // VGT_INSTANCE_STEP_RATE_0 + 0x00000000, // VGT_INSTANCE_STEP_RATE_1 + 0x000000ff, // IA_MULTI_VGT_PARAM + 0, // HOLE + 0, // HOLE + 0x00000000, // VGT_REUSE_OFF + 0x00000000, // VGT_VTX_CNT_EN + 0x00000000, // DB_HTILE_SURFACE + 0x00000000, // DB_SRESULTS_COMPARE_STATE0 + 0x00000000, // DB_SRESULTS_COMPARE_STATE1 + 0x00000000, // DB_PRELOAD_CONTROL + 0, // HOLE + 0x00000000, // VGT_STRMOUT_BUFFER_SIZE_0 + 0x00000000, // VGT_STRMOUT_VTX_STRIDE_0 + 0x00000000, // VGT_STRMOUT_BUFFER_BASE_0 + 0x00000000, // VGT_STRMOUT_BUFFER_OFFSET_0 + 0x00000000, // VGT_STRMOUT_BUFFER_SIZE_1 + 0x00000000, // VGT_STRMOUT_VTX_STRIDE_1 + 0x00000000, // VGT_STRMOUT_BUFFER_BASE_1 + 0x00000000, // VGT_STRMOUT_BUFFER_OFFSET_1 + 0x00000000, // VGT_STRMOUT_BUFFER_SIZE_2 + 0x00000000, // VGT_STRMOUT_VTX_STRIDE_2 + 0x00000000, // VGT_STRMOUT_BUFFER_BASE_2 + 0x00000000, // VGT_STRMOUT_BUFFER_OFFSET_2 + 0x00000000, // VGT_STRMOUT_BUFFER_SIZE_3 + 0x00000000, // VGT_STRMOUT_VTX_STRIDE_3 + 0x00000000, // VGT_STRMOUT_BUFFER_BASE_3 + 0x00000000, // VGT_STRMOUT_BUFFER_OFFSET_3 + 0x00000000, // VGT_STRMOUT_BASE_OFFSET_0 + 0x00000000, // VGT_STRMOUT_BASE_OFFSET_1 + 0x00000000, // VGT_STRMOUT_BASE_OFFSET_2 + 0x00000000, // VGT_STRMOUT_BASE_OFFSET_3 + 0, // HOLE + 0, // HOLE + 0x00000000, // VGT_STRMOUT_DRAW_OPAQUE_OFFSET + 0x00000000, // VGT_STRMOUT_DRAW_OPAQUE_BUFFER_FILLED_SIZE + 0x00000000, // VGT_STRMOUT_DRAW_OPAQUE_VERTEX_STRIDE + 0, // HOLE + 0x00000000, // VGT_GS_MAX_VERT_OUT + 0, // HOLE + 0, // HOLE + 0x00000000, // VGT_STRMOUT_BASE_OFFSET_HI_0 + 0x00000000, // VGT_STRMOUT_BASE_OFFSET_HI_1 + 0x00000000, // VGT_STRMOUT_BASE_OFFSET_HI_2 + 0x00000000, // VGT_STRMOUT_BASE_OFFSET_HI_3 + 0x00000000, // VGT_SHADER_STAGES_EN + 0x00000000, // VGT_LS_HS_CONFIG + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0x00000000, // VGT_TF_PARAM + 0x00000000, // DB_ALPHA_TO_MASK +}; +static const u32 SECT_CONTEXT_def_7[] = +{ + 0x00000000, // PA_SU_POLY_OFFSET_DB_FMT_CNTL + 0x00000000, // PA_SU_POLY_OFFSET_CLAMP + 0x00000000, // PA_SU_POLY_OFFSET_FRONT_SCALE + 0x00000000, // PA_SU_POLY_OFFSET_FRONT_OFFSET + 0x00000000, // PA_SU_POLY_OFFSET_BACK_SCALE + 0x00000000, // PA_SU_POLY_OFFSET_BACK_OFFSET + 0x00000000, // VGT_GS_INSTANCE_CNT + 0x00000000, // VGT_STRMOUT_CONFIG + 0x00000000, // VGT_STRMOUT_BUFFER_CONFIG + 0x00000000, // CB_IMMED0_BASE + 0x00000000, // CB_IMMED1_BASE + 0x00000000, // CB_IMMED2_BASE + 0x00000000, // CB_IMMED3_BASE + 0x00000000, // CB_IMMED4_BASE + 0x00000000, // CB_IMMED5_BASE + 0x00000000, // CB_IMMED6_BASE + 0x00000000, // CB_IMMED7_BASE + 0x00000000, // CB_IMMED8_BASE + 0x00000000, // CB_IMMED9_BASE + 0x00000000, // CB_IMMED10_BASE + 0x00000000, // CB_IMMED11_BASE + 0, // HOLE + 0, // HOLE + 0x00000000, // PA_SC_CENTROID_PRIORITY_0 + 0x00000000, // PA_SC_CENTROID_PRIORITY_1 + 0x00001000, // PA_SC_LINE_CNTL + 0x00000000, // PA_SC_AA_CONFIG + 0x00000005, // PA_SU_VTX_CNTL + 0x3f800000, // PA_CL_GB_VERT_CLIP_ADJ + 0x3f800000, // PA_CL_GB_VERT_DISC_ADJ + 0x3f800000, // PA_CL_GB_HORZ_CLIP_ADJ + 0x3f800000, // PA_CL_GB_HORZ_DISC_ADJ + 0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y0_0 + 0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y0_1 + 0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y0_2 + 0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y0_3 + 0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y0_0 + 0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y0_1 + 0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y0_2 + 0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y0_3 + 0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y1_0 + 0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y1_1 + 0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y1_2 + 0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y1_3 + 0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y1_0 + 0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y1_1 + 0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y1_2 + 0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y1_3 + 0xffffffff, // PA_SC_AA_MASK_X0Y0_X1Y0 + 0xffffffff, // PA_SC_AA_MASK_X0Y1_X1Y1 + 0x00000000, // CB_CLRCMP_CONTROL + 0x00000000, // CB_CLRCMP_SRC + 0x00000000, // CB_CLRCMP_DST + 0x00000000, // CB_CLRCMP_MSK + 0, // HOLE + 0, // HOLE + 0x0000000e, // VGT_VERTEX_REUSE_BLOCK_CNTL + 0x00000010, // VGT_OUT_DEALLOC_CNTL + 0x00000000, // CB_COLOR0_BASE + 0x00000000, // CB_COLOR0_PITCH + 0x00000000, // CB_COLOR0_SLICE + 0x00000000, // CB_COLOR0_VIEW + 0x00000000, // CB_COLOR0_INFO + 0x00000000, // CB_COLOR0_ATTRIB + 0x00000000, // CB_COLOR0_DIM + 0x00000000, // CB_COLOR0_CMASK + 0x00000000, // CB_COLOR0_CMASK_SLICE + 0x00000000, // CB_COLOR0_FMASK + 0x00000000, // CB_COLOR0_FMASK_SLICE + 0x00000000, // CB_COLOR0_CLEAR_WORD0 + 0x00000000, // CB_COLOR0_CLEAR_WORD1 + 0x00000000, // CB_COLOR0_CLEAR_WORD2 + 0x00000000, // CB_COLOR0_CLEAR_WORD3 + 0x00000000, // CB_COLOR1_BASE + 0x00000000, // CB_COLOR1_PITCH + 0x00000000, // CB_COLOR1_SLICE + 0x00000000, // CB_COLOR1_VIEW + 0x00000000, // CB_COLOR1_INFO + 0x00000000, // CB_COLOR1_ATTRIB + 0x00000000, // CB_COLOR1_DIM + 0x00000000, // CB_COLOR1_CMASK + 0x00000000, // CB_COLOR1_CMASK_SLICE + 0x00000000, // CB_COLOR1_FMASK + 0x00000000, // CB_COLOR1_FMASK_SLICE + 0x00000000, // CB_COLOR1_CLEAR_WORD0 + 0x00000000, // CB_COLOR1_CLEAR_WORD1 + 0x00000000, // CB_COLOR1_CLEAR_WORD2 + 0x00000000, // CB_COLOR1_CLEAR_WORD3 + 0x00000000, // CB_COLOR2_BASE + 0x00000000, // CB_COLOR2_PITCH + 0x00000000, // CB_COLOR2_SLICE + 0x00000000, // CB_COLOR2_VIEW + 0x00000000, // CB_COLOR2_INFO + 0x00000000, // CB_COLOR2_ATTRIB + 0x00000000, // CB_COLOR2_DIM + 0x00000000, // CB_COLOR2_CMASK + 0x00000000, // CB_COLOR2_CMASK_SLICE + 0x00000000, // CB_COLOR2_FMASK + 0x00000000, // CB_COLOR2_FMASK_SLICE + 0x00000000, // CB_COLOR2_CLEAR_WORD0 + 0x00000000, // CB_COLOR2_CLEAR_WORD1 + 0x00000000, // CB_COLOR2_CLEAR_WORD2 + 0x00000000, // CB_COLOR2_CLEAR_WORD3 + 0x00000000, // CB_COLOR3_BASE + 0x00000000, // CB_COLOR3_PITCH + 0x00000000, // CB_COLOR3_SLICE + 0x00000000, // CB_COLOR3_VIEW + 0x00000000, // CB_COLOR3_INFO + 0x00000000, // CB_COLOR3_ATTRIB + 0x00000000, // CB_COLOR3_DIM + 0x00000000, // CB_COLOR3_CMASK + 0x00000000, // CB_COLOR3_CMASK_SLICE + 0x00000000, // CB_COLOR3_FMASK + 0x00000000, // CB_COLOR3_FMASK_SLICE + 0x00000000, // CB_COLOR3_CLEAR_WORD0 + 0x00000000, // CB_COLOR3_CLEAR_WORD1 + 0x00000000, // CB_COLOR3_CLEAR_WORD2 + 0x00000000, // CB_COLOR3_CLEAR_WORD3 + 0x00000000, // CB_COLOR4_BASE + 0x00000000, // CB_COLOR4_PITCH + 0x00000000, // CB_COLOR4_SLICE + 0x00000000, // CB_COLOR4_VIEW + 0x00000000, // CB_COLOR4_INFO + 0x00000000, // CB_COLOR4_ATTRIB + 0x00000000, // CB_COLOR4_DIM + 0x00000000, // CB_COLOR4_CMASK + 0x00000000, // CB_COLOR4_CMASK_SLICE + 0x00000000, // CB_COLOR4_FMASK + 0x00000000, // CB_COLOR4_FMASK_SLICE + 0x00000000, // CB_COLOR4_CLEAR_WORD0 + 0x00000000, // CB_COLOR4_CLEAR_WORD1 + 0x00000000, // CB_COLOR4_CLEAR_WORD2 + 0x00000000, // CB_COLOR4_CLEAR_WORD3 + 0x00000000, // CB_COLOR5_BASE + 0x00000000, // CB_COLOR5_PITCH + 0x00000000, // CB_COLOR5_SLICE + 0x00000000, // CB_COLOR5_VIEW + 0x00000000, // CB_COLOR5_INFO + 0x00000000, // CB_COLOR5_ATTRIB + 0x00000000, // CB_COLOR5_DIM + 0x00000000, // CB_COLOR5_CMASK + 0x00000000, // CB_COLOR5_CMASK_SLICE + 0x00000000, // CB_COLOR5_FMASK + 0x00000000, // CB_COLOR5_FMASK_SLICE + 0x00000000, // CB_COLOR5_CLEAR_WORD0 + 0x00000000, // CB_COLOR5_CLEAR_WORD1 + 0x00000000, // CB_COLOR5_CLEAR_WORD2 + 0x00000000, // CB_COLOR5_CLEAR_WORD3 + 0x00000000, // CB_COLOR6_BASE + 0x00000000, // CB_COLOR6_PITCH + 0x00000000, // CB_COLOR6_SLICE + 0x00000000, // CB_COLOR6_VIEW + 0x00000000, // CB_COLOR6_INFO + 0x00000000, // CB_COLOR6_ATTRIB + 0x00000000, // CB_COLOR6_DIM + 0x00000000, // CB_COLOR6_CMASK + 0x00000000, // CB_COLOR6_CMASK_SLICE + 0x00000000, // CB_COLOR6_FMASK + 0x00000000, // CB_COLOR6_FMASK_SLICE + 0x00000000, // CB_COLOR6_CLEAR_WORD0 + 0x00000000, // CB_COLOR6_CLEAR_WORD1 + 0x00000000, // CB_COLOR6_CLEAR_WORD2 + 0x00000000, // CB_COLOR6_CLEAR_WORD3 + 0x00000000, // CB_COLOR7_BASE + 0x00000000, // CB_COLOR7_PITCH + 0x00000000, // CB_COLOR7_SLICE + 0x00000000, // CB_COLOR7_VIEW + 0x00000000, // CB_COLOR7_INFO + 0x00000000, // CB_COLOR7_ATTRIB + 0x00000000, // CB_COLOR7_DIM + 0x00000000, // CB_COLOR7_CMASK + 0x00000000, // CB_COLOR7_CMASK_SLICE + 0x00000000, // CB_COLOR7_FMASK + 0x00000000, // CB_COLOR7_FMASK_SLICE + 0x00000000, // CB_COLOR7_CLEAR_WORD0 + 0x00000000, // CB_COLOR7_CLEAR_WORD1 + 0x00000000, // CB_COLOR7_CLEAR_WORD2 + 0x00000000, // CB_COLOR7_CLEAR_WORD3 + 0x00000000, // CB_COLOR8_BASE + 0x00000000, // CB_COLOR8_PITCH + 0x00000000, // CB_COLOR8_SLICE + 0x00000000, // CB_COLOR8_VIEW + 0x00000000, // CB_COLOR8_INFO + 0x00000000, // CB_COLOR8_ATTRIB + 0x00000000, // CB_COLOR8_DIM + 0x00000000, // CB_COLOR9_BASE + 0x00000000, // CB_COLOR9_PITCH + 0x00000000, // CB_COLOR9_SLICE + 0x00000000, // CB_COLOR9_VIEW + 0x00000000, // CB_COLOR9_INFO + 0x00000000, // CB_COLOR9_ATTRIB + 0x00000000, // CB_COLOR9_DIM + 0x00000000, // CB_COLOR10_BASE + 0x00000000, // CB_COLOR10_PITCH + 0x00000000, // CB_COLOR10_SLICE + 0x00000000, // CB_COLOR10_VIEW + 0x00000000, // CB_COLOR10_INFO + 0x00000000, // CB_COLOR10_ATTRIB + 0x00000000, // CB_COLOR10_DIM + 0x00000000, // CB_COLOR11_BASE + 0x00000000, // CB_COLOR11_PITCH + 0x00000000, // CB_COLOR11_SLICE + 0x00000000, // CB_COLOR11_VIEW + 0x00000000, // CB_COLOR11_INFO + 0x00000000, // CB_COLOR11_ATTRIB + 0x00000000, // CB_COLOR11_DIM + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0x00000000, // SQ_ALU_CONST_CACHE_HS_0 + 0x00000000, // SQ_ALU_CONST_CACHE_HS_1 + 0x00000000, // SQ_ALU_CONST_CACHE_HS_2 + 0x00000000, // SQ_ALU_CONST_CACHE_HS_3 + 0x00000000, // SQ_ALU_CONST_CACHE_HS_4 + 0x00000000, // SQ_ALU_CONST_CACHE_HS_5 + 0x00000000, // SQ_ALU_CONST_CACHE_HS_6 + 0x00000000, // SQ_ALU_CONST_CACHE_HS_7 + 0x00000000, // SQ_ALU_CONST_CACHE_HS_8 + 0x00000000, // SQ_ALU_CONST_CACHE_HS_9 + 0x00000000, // SQ_ALU_CONST_CACHE_HS_10 + 0x00000000, // SQ_ALU_CONST_CACHE_HS_11 + 0x00000000, // SQ_ALU_CONST_CACHE_HS_12 + 0x00000000, // SQ_ALU_CONST_CACHE_HS_13 + 0x00000000, // SQ_ALU_CONST_CACHE_HS_14 + 0x00000000, // SQ_ALU_CONST_CACHE_HS_15 + 0x00000000, // SQ_ALU_CONST_CACHE_LS_0 + 0x00000000, // SQ_ALU_CONST_CACHE_LS_1 + 0x00000000, // SQ_ALU_CONST_CACHE_LS_2 + 0x00000000, // SQ_ALU_CONST_CACHE_LS_3 + 0x00000000, // SQ_ALU_CONST_CACHE_LS_4 + 0x00000000, // SQ_ALU_CONST_CACHE_LS_5 + 0x00000000, // SQ_ALU_CONST_CACHE_LS_6 + 0x00000000, // SQ_ALU_CONST_CACHE_LS_7 + 0x00000000, // SQ_ALU_CONST_CACHE_LS_8 + 0x00000000, // SQ_ALU_CONST_CACHE_LS_9 + 0x00000000, // SQ_ALU_CONST_CACHE_LS_10 + 0x00000000, // SQ_ALU_CONST_CACHE_LS_11 + 0x00000000, // SQ_ALU_CONST_CACHE_LS_12 + 0x00000000, // SQ_ALU_CONST_CACHE_LS_13 + 0x00000000, // SQ_ALU_CONST_CACHE_LS_14 + 0x00000000, // SQ_ALU_CONST_CACHE_LS_15 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_0 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_1 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_2 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_3 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_4 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_5 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_6 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_7 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_8 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_9 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_10 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_11 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_12 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_13 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_14 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_15 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_0 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_1 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_2 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_3 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_4 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_5 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_6 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_7 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_8 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_9 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_10 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_11 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_12 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_13 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_14 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_15 +}; +static const struct cs_extent_def SECT_CONTEXT_defs[] = +{ + {SECT_CONTEXT_def_1, 0x0000a000, 488 }, + {SECT_CONTEXT_def_2, 0x0000a1f5, 6 }, + {SECT_CONTEXT_def_3, 0x0000a200, 55 }, + {SECT_CONTEXT_def_4, 0x0000a23a, 99 }, + {SECT_CONTEXT_def_5, 0x0000a29e, 5 }, + {SECT_CONTEXT_def_6, 0x0000a2a5, 56 }, + {SECT_CONTEXT_def_7, 0x0000a2de, 290 }, + { 0, 0, 0 } +}; +static const u32 SECT_CLEAR_def_1[] = +{ + 0xffffffff, // SQ_TEX_SAMPLER_CLEAR + 0xffffffff, // SQ_TEX_RESOURCE_CLEAR + 0xffffffff, // SQ_LOOP_BOOL_CLEAR +}; +static const struct cs_extent_def SECT_CLEAR_defs[] = +{ + {SECT_CLEAR_def_1, 0x0000ffc0, 3 }, + { 0, 0, 0 } +}; +static const u32 SECT_CTRLCONST_def_1[] = +{ + 0x00000000, // SQ_VTX_BASE_VTX_LOC + 0x00000000, // SQ_VTX_START_INST_LOC +}; +static const struct cs_extent_def SECT_CTRLCONST_defs[] = +{ + {SECT_CTRLCONST_def_1, 0x0000f3fc, 2 }, + { 0, 0, 0 } +}; +struct cs_section_def cayman_cs_data[] = { + { SECT_CONTEXT_defs, SECT_CONTEXT }, + { SECT_CLEAR_defs, SECT_CLEAR }, + { SECT_CTRLCONST_defs, SECT_CTRLCONST }, + { 0, SECT_NONE } +}; diff --git a/drivers/gpu/drm/radeon/clearstate_defs.h b/drivers/gpu/drm/radeon/clearstate_defs.h new file mode 100644 index 0000000..3eda707 --- /dev/null +++ b/drivers/gpu/drm/radeon/clearstate_defs.h @@ -0,0 +1,44 @@ +/* + * Copyright 2012 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ +#ifndef CLEARSTATE_DEFS_H +#define CLEARSTATE_DEFS_H + +enum section_id { + SECT_NONE, + SECT_CONTEXT, + SECT_CLEAR, + SECT_CTRLCONST +}; + +struct cs_extent_def { + const unsigned int *extent; + const unsigned int reg_index; + const unsigned int reg_count; +}; + +struct cs_section_def { + const struct cs_extent_def *section; + const enum section_id id; +}; + +#endif diff --git a/drivers/gpu/drm/radeon/clearstate_evergreen.h b/drivers/gpu/drm/radeon/clearstate_evergreen.h new file mode 100644 index 0000000..4791d85 --- /dev/null +++ b/drivers/gpu/drm/radeon/clearstate_evergreen.h @@ -0,0 +1,1080 @@ +/* + * Copyright 2012 Advanced Micro Devices, 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. + * + */ + +static const u32 SECT_CONTEXT_def_1[] = +{ + 0x00000000, // DB_RENDER_CONTROL + 0x00000000, // DB_COUNT_CONTROL + 0x00000000, // DB_DEPTH_VIEW + 0x00000000, // DB_RENDER_OVERRIDE + 0x00000000, // DB_RENDER_OVERRIDE2 + 0x00000000, // DB_HTILE_DATA_BASE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0x00000000, // DB_STENCIL_CLEAR + 0x00000000, // DB_DEPTH_CLEAR + 0x00000000, // PA_SC_SCREEN_SCISSOR_TL + 0x40004000, // PA_SC_SCREEN_SCISSOR_BR + 0, // HOLE + 0, // HOLE + 0x00000000, // DB_Z_INFO + 0x00000000, // DB_STENCIL_INFO + 0x00000000, // DB_Z_READ_BASE + 0x00000000, // DB_STENCIL_READ_BASE + 0x00000000, // DB_Z_WRITE_BASE + 0x00000000, // DB_STENCIL_WRITE_BASE + 0x00000000, // DB_DEPTH_SIZE + 0x00000000, // DB_DEPTH_SLICE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_0 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_1 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_2 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_3 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_4 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_5 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_6 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_7 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_8 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_9 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_10 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_11 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_12 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_13 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_14 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_PS_15 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_0 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_1 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_2 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_3 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_4 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_5 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_6 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_7 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_8 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_9 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_10 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_11 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_12 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_13 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_14 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_VS_15 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_0 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_1 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_2 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_3 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_4 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_5 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_6 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_7 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_8 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_9 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_10 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_11 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_12 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_13 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_14 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_GS_15 + 0x00000000, // PA_SC_WINDOW_OFFSET + 0x80000000, // PA_SC_WINDOW_SCISSOR_TL + 0x40004000, // PA_SC_WINDOW_SCISSOR_BR + 0x0000ffff, // PA_SC_CLIPRECT_RULE + 0x00000000, // PA_SC_CLIPRECT_0_TL + 0x40004000, // PA_SC_CLIPRECT_0_BR + 0x00000000, // PA_SC_CLIPRECT_1_TL + 0x40004000, // PA_SC_CLIPRECT_1_BR + 0x00000000, // PA_SC_CLIPRECT_2_TL + 0x40004000, // PA_SC_CLIPRECT_2_BR + 0x00000000, // PA_SC_CLIPRECT_3_TL + 0x40004000, // PA_SC_CLIPRECT_3_BR + 0xaa99aaaa, // PA_SC_EDGERULE + 0x00000000, // PA_SU_HARDWARE_SCREEN_OFFSET + 0xffffffff, // CB_TARGET_MASK + 0xffffffff, // CB_SHADER_MASK + 0x80000000, // PA_SC_GENERIC_SCISSOR_TL + 0x40004000, // PA_SC_GENERIC_SCISSOR_BR + 0x00000000, // COHER_DEST_BASE_0 + 0x00000000, // COHER_DEST_BASE_1 + 0x80000000, // PA_SC_VPORT_SCISSOR_0_TL + 0x40004000, // PA_SC_VPORT_SCISSOR_0_BR + 0x80000000, // PA_SC_VPORT_SCISSOR_1_TL + 0x40004000, // PA_SC_VPORT_SCISSOR_1_BR + 0x80000000, // PA_SC_VPORT_SCISSOR_2_TL + 0x40004000, // PA_SC_VPORT_SCISSOR_2_BR + 0x80000000, // PA_SC_VPORT_SCISSOR_3_TL + 0x40004000, // PA_SC_VPORT_SCISSOR_3_BR + 0x80000000, // PA_SC_VPORT_SCISSOR_4_TL + 0x40004000, // PA_SC_VPORT_SCISSOR_4_BR + 0x80000000, // PA_SC_VPORT_SCISSOR_5_TL + 0x40004000, // PA_SC_VPORT_SCISSOR_5_BR + 0x80000000, // PA_SC_VPORT_SCISSOR_6_TL + 0x40004000, // PA_SC_VPORT_SCISSOR_6_BR + 0x80000000, // PA_SC_VPORT_SCISSOR_7_TL + 0x40004000, // PA_SC_VPORT_SCISSOR_7_BR + 0x80000000, // PA_SC_VPORT_SCISSOR_8_TL + 0x40004000, // PA_SC_VPORT_SCISSOR_8_BR + 0x80000000, // PA_SC_VPORT_SCISSOR_9_TL + 0x40004000, // PA_SC_VPORT_SCISSOR_9_BR + 0x80000000, // PA_SC_VPORT_SCISSOR_10_TL + 0x40004000, // PA_SC_VPORT_SCISSOR_10_BR + 0x80000000, // PA_SC_VPORT_SCISSOR_11_TL + 0x40004000, // PA_SC_VPORT_SCISSOR_11_BR + 0x80000000, // PA_SC_VPORT_SCISSOR_12_TL + 0x40004000, // PA_SC_VPORT_SCISSOR_12_BR + 0x80000000, // PA_SC_VPORT_SCISSOR_13_TL + 0x40004000, // PA_SC_VPORT_SCISSOR_13_BR + 0x80000000, // PA_SC_VPORT_SCISSOR_14_TL + 0x40004000, // PA_SC_VPORT_SCISSOR_14_BR + 0x80000000, // PA_SC_VPORT_SCISSOR_15_TL + 0x40004000, // PA_SC_VPORT_SCISSOR_15_BR + 0x00000000, // PA_SC_VPORT_ZMIN_0 + 0x3f800000, // PA_SC_VPORT_ZMAX_0 + 0x00000000, // PA_SC_VPORT_ZMIN_1 + 0x3f800000, // PA_SC_VPORT_ZMAX_1 + 0x00000000, // PA_SC_VPORT_ZMIN_2 + 0x3f800000, // PA_SC_VPORT_ZMAX_2 + 0x00000000, // PA_SC_VPORT_ZMIN_3 + 0x3f800000, // PA_SC_VPORT_ZMAX_3 + 0x00000000, // PA_SC_VPORT_ZMIN_4 + 0x3f800000, // PA_SC_VPORT_ZMAX_4 + 0x00000000, // PA_SC_VPORT_ZMIN_5 + 0x3f800000, // PA_SC_VPORT_ZMAX_5 + 0x00000000, // PA_SC_VPORT_ZMIN_6 + 0x3f800000, // PA_SC_VPORT_ZMAX_6 + 0x00000000, // PA_SC_VPORT_ZMIN_7 + 0x3f800000, // PA_SC_VPORT_ZMAX_7 + 0x00000000, // PA_SC_VPORT_ZMIN_8 + 0x3f800000, // PA_SC_VPORT_ZMAX_8 + 0x00000000, // PA_SC_VPORT_ZMIN_9 + 0x3f800000, // PA_SC_VPORT_ZMAX_9 + 0x00000000, // PA_SC_VPORT_ZMIN_10 + 0x3f800000, // PA_SC_VPORT_ZMAX_10 + 0x00000000, // PA_SC_VPORT_ZMIN_11 + 0x3f800000, // PA_SC_VPORT_ZMAX_11 + 0x00000000, // PA_SC_VPORT_ZMIN_12 + 0x3f800000, // PA_SC_VPORT_ZMAX_12 + 0x00000000, // PA_SC_VPORT_ZMIN_13 + 0x3f800000, // PA_SC_VPORT_ZMAX_13 + 0x00000000, // PA_SC_VPORT_ZMIN_14 + 0x3f800000, // PA_SC_VPORT_ZMAX_14 + 0x00000000, // PA_SC_VPORT_ZMIN_15 + 0x3f800000, // PA_SC_VPORT_ZMAX_15 + 0x00000000, // SX_MISC + 0x00000000, // SX_SURFACE_SYNC + 0x00000000, // CP_PERFMON_CNTX_CNTL + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0x00000000, // SQ_VTX_SEMANTIC_0 + 0x00000000, // SQ_VTX_SEMANTIC_1 + 0x00000000, // SQ_VTX_SEMANTIC_2 + 0x00000000, // SQ_VTX_SEMANTIC_3 + 0x00000000, // SQ_VTX_SEMANTIC_4 + 0x00000000, // SQ_VTX_SEMANTIC_5 + 0x00000000, // SQ_VTX_SEMANTIC_6 + 0x00000000, // SQ_VTX_SEMANTIC_7 + 0x00000000, // SQ_VTX_SEMANTIC_8 + 0x00000000, // SQ_VTX_SEMANTIC_9 + 0x00000000, // SQ_VTX_SEMANTIC_10 + 0x00000000, // SQ_VTX_SEMANTIC_11 + 0x00000000, // SQ_VTX_SEMANTIC_12 + 0x00000000, // SQ_VTX_SEMANTIC_13 + 0x00000000, // SQ_VTX_SEMANTIC_14 + 0x00000000, // SQ_VTX_SEMANTIC_15 + 0x00000000, // SQ_VTX_SEMANTIC_16 + 0x00000000, // SQ_VTX_SEMANTIC_17 + 0x00000000, // SQ_VTX_SEMANTIC_18 + 0x00000000, // SQ_VTX_SEMANTIC_19 + 0x00000000, // SQ_VTX_SEMANTIC_20 + 0x00000000, // SQ_VTX_SEMANTIC_21 + 0x00000000, // SQ_VTX_SEMANTIC_22 + 0x00000000, // SQ_VTX_SEMANTIC_23 + 0x00000000, // SQ_VTX_SEMANTIC_24 + 0x00000000, // SQ_VTX_SEMANTIC_25 + 0x00000000, // SQ_VTX_SEMANTIC_26 + 0x00000000, // SQ_VTX_SEMANTIC_27 + 0x00000000, // SQ_VTX_SEMANTIC_28 + 0x00000000, // SQ_VTX_SEMANTIC_29 + 0x00000000, // SQ_VTX_SEMANTIC_30 + 0x00000000, // SQ_VTX_SEMANTIC_31 + 0xffffffff, // VGT_MAX_VTX_INDX + 0x00000000, // VGT_MIN_VTX_INDX + 0x00000000, // VGT_INDX_OFFSET + 0x00000000, // VGT_MULTI_PRIM_IB_RESET_INDX + 0x00000000, // SX_ALPHA_TEST_CONTROL + 0x00000000, // CB_BLEND_RED + 0x00000000, // CB_BLEND_GREEN + 0x00000000, // CB_BLEND_BLUE + 0x00000000, // CB_BLEND_ALPHA + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0x00000000, // DB_STENCILREFMASK + 0x00000000, // DB_STENCILREFMASK_BF + 0x00000000, // SX_ALPHA_REF + 0x00000000, // PA_CL_VPORT_XSCALE + 0x00000000, // PA_CL_VPORT_XOFFSET + 0x00000000, // PA_CL_VPORT_YSCALE + 0x00000000, // PA_CL_VPORT_YOFFSET + 0x00000000, // PA_CL_VPORT_ZSCALE + 0x00000000, // PA_CL_VPORT_ZOFFSET + 0x00000000, // PA_CL_VPORT_XSCALE_1 + 0x00000000, // PA_CL_VPORT_XOFFSET_1 + 0x00000000, // PA_CL_VPORT_YSCALE_1 + 0x00000000, // PA_CL_VPORT_YOFFSET_1 + 0x00000000, // PA_CL_VPORT_ZSCALE_1 + 0x00000000, // PA_CL_VPORT_ZOFFSET_1 + 0x00000000, // PA_CL_VPORT_XSCALE_2 + 0x00000000, // PA_CL_VPORT_XOFFSET_2 + 0x00000000, // PA_CL_VPORT_YSCALE_2 + 0x00000000, // PA_CL_VPORT_YOFFSET_2 + 0x00000000, // PA_CL_VPORT_ZSCALE_2 + 0x00000000, // PA_CL_VPORT_ZOFFSET_2 + 0x00000000, // PA_CL_VPORT_XSCALE_3 + 0x00000000, // PA_CL_VPORT_XOFFSET_3 + 0x00000000, // PA_CL_VPORT_YSCALE_3 + 0x00000000, // PA_CL_VPORT_YOFFSET_3 + 0x00000000, // PA_CL_VPORT_ZSCALE_3 + 0x00000000, // PA_CL_VPORT_ZOFFSET_3 + 0x00000000, // PA_CL_VPORT_XSCALE_4 + 0x00000000, // PA_CL_VPORT_XOFFSET_4 + 0x00000000, // PA_CL_VPORT_YSCALE_4 + 0x00000000, // PA_CL_VPORT_YOFFSET_4 + 0x00000000, // PA_CL_VPORT_ZSCALE_4 + 0x00000000, // PA_CL_VPORT_ZOFFSET_4 + 0x00000000, // PA_CL_VPORT_XSCALE_5 + 0x00000000, // PA_CL_VPORT_XOFFSET_5 + 0x00000000, // PA_CL_VPORT_YSCALE_5 + 0x00000000, // PA_CL_VPORT_YOFFSET_5 + 0x00000000, // PA_CL_VPORT_ZSCALE_5 + 0x00000000, // PA_CL_VPORT_ZOFFSET_5 + 0x00000000, // PA_CL_VPORT_XSCALE_6 + 0x00000000, // PA_CL_VPORT_XOFFSET_6 + 0x00000000, // PA_CL_VPORT_YSCALE_6 + 0x00000000, // PA_CL_VPORT_YOFFSET_6 + 0x00000000, // PA_CL_VPORT_ZSCALE_6 + 0x00000000, // PA_CL_VPORT_ZOFFSET_6 + 0x00000000, // PA_CL_VPORT_XSCALE_7 + 0x00000000, // PA_CL_VPORT_XOFFSET_7 + 0x00000000, // PA_CL_VPORT_YSCALE_7 + 0x00000000, // PA_CL_VPORT_YOFFSET_7 + 0x00000000, // PA_CL_VPORT_ZSCALE_7 + 0x00000000, // PA_CL_VPORT_ZOFFSET_7 + 0x00000000, // PA_CL_VPORT_XSCALE_8 + 0x00000000, // PA_CL_VPORT_XOFFSET_8 + 0x00000000, // PA_CL_VPORT_YSCALE_8 + 0x00000000, // PA_CL_VPORT_YOFFSET_8 + 0x00000000, // PA_CL_VPORT_ZSCALE_8 + 0x00000000, // PA_CL_VPORT_ZOFFSET_8 + 0x00000000, // PA_CL_VPORT_XSCALE_9 + 0x00000000, // PA_CL_VPORT_XOFFSET_9 + 0x00000000, // PA_CL_VPORT_YSCALE_9 + 0x00000000, // PA_CL_VPORT_YOFFSET_9 + 0x00000000, // PA_CL_VPORT_ZSCALE_9 + 0x00000000, // PA_CL_VPORT_ZOFFSET_9 + 0x00000000, // PA_CL_VPORT_XSCALE_10 + 0x00000000, // PA_CL_VPORT_XOFFSET_10 + 0x00000000, // PA_CL_VPORT_YSCALE_10 + 0x00000000, // PA_CL_VPORT_YOFFSET_10 + 0x00000000, // PA_CL_VPORT_ZSCALE_10 + 0x00000000, // PA_CL_VPORT_ZOFFSET_10 + 0x00000000, // PA_CL_VPORT_XSCALE_11 + 0x00000000, // PA_CL_VPORT_XOFFSET_11 + 0x00000000, // PA_CL_VPORT_YSCALE_11 + 0x00000000, // PA_CL_VPORT_YOFFSET_11 + 0x00000000, // PA_CL_VPORT_ZSCALE_11 + 0x00000000, // PA_CL_VPORT_ZOFFSET_11 + 0x00000000, // PA_CL_VPORT_XSCALE_12 + 0x00000000, // PA_CL_VPORT_XOFFSET_12 + 0x00000000, // PA_CL_VPORT_YSCALE_12 + 0x00000000, // PA_CL_VPORT_YOFFSET_12 + 0x00000000, // PA_CL_VPORT_ZSCALE_12 + 0x00000000, // PA_CL_VPORT_ZOFFSET_12 + 0x00000000, // PA_CL_VPORT_XSCALE_13 + 0x00000000, // PA_CL_VPORT_XOFFSET_13 + 0x00000000, // PA_CL_VPORT_YSCALE_13 + 0x00000000, // PA_CL_VPORT_YOFFSET_13 + 0x00000000, // PA_CL_VPORT_ZSCALE_13 + 0x00000000, // PA_CL_VPORT_ZOFFSET_13 + 0x00000000, // PA_CL_VPORT_XSCALE_14 + 0x00000000, // PA_CL_VPORT_XOFFSET_14 + 0x00000000, // PA_CL_VPORT_YSCALE_14 + 0x00000000, // PA_CL_VPORT_YOFFSET_14 + 0x00000000, // PA_CL_VPORT_ZSCALE_14 + 0x00000000, // PA_CL_VPORT_ZOFFSET_14 + 0x00000000, // PA_CL_VPORT_XSCALE_15 + 0x00000000, // PA_CL_VPORT_XOFFSET_15 + 0x00000000, // PA_CL_VPORT_YSCALE_15 + 0x00000000, // PA_CL_VPORT_YOFFSET_15 + 0x00000000, // PA_CL_VPORT_ZSCALE_15 + 0x00000000, // PA_CL_VPORT_ZOFFSET_15 + 0x00000000, // PA_CL_UCP_0_X + 0x00000000, // PA_CL_UCP_0_Y + 0x00000000, // PA_CL_UCP_0_Z + 0x00000000, // PA_CL_UCP_0_W + 0x00000000, // PA_CL_UCP_1_X + 0x00000000, // PA_CL_UCP_1_Y + 0x00000000, // PA_CL_UCP_1_Z + 0x00000000, // PA_CL_UCP_1_W + 0x00000000, // PA_CL_UCP_2_X + 0x00000000, // PA_CL_UCP_2_Y + 0x00000000, // PA_CL_UCP_2_Z + 0x00000000, // PA_CL_UCP_2_W + 0x00000000, // PA_CL_UCP_3_X + 0x00000000, // PA_CL_UCP_3_Y + 0x00000000, // PA_CL_UCP_3_Z + 0x00000000, // PA_CL_UCP_3_W + 0x00000000, // PA_CL_UCP_4_X + 0x00000000, // PA_CL_UCP_4_Y + 0x00000000, // PA_CL_UCP_4_Z + 0x00000000, // PA_CL_UCP_4_W + 0x00000000, // PA_CL_UCP_5_X + 0x00000000, // PA_CL_UCP_5_Y + 0x00000000, // PA_CL_UCP_5_Z + 0x00000000, // PA_CL_UCP_5_W + 0x00000000, // SPI_VS_OUT_ID_0 + 0x00000000, // SPI_VS_OUT_ID_1 + 0x00000000, // SPI_VS_OUT_ID_2 + 0x00000000, // SPI_VS_OUT_ID_3 + 0x00000000, // SPI_VS_OUT_ID_4 + 0x00000000, // SPI_VS_OUT_ID_5 + 0x00000000, // SPI_VS_OUT_ID_6 + 0x00000000, // SPI_VS_OUT_ID_7 + 0x00000000, // SPI_VS_OUT_ID_8 + 0x00000000, // SPI_VS_OUT_ID_9 + 0x00000000, // SPI_PS_INPUT_CNTL_0 + 0x00000000, // SPI_PS_INPUT_CNTL_1 + 0x00000000, // SPI_PS_INPUT_CNTL_2 + 0x00000000, // SPI_PS_INPUT_CNTL_3 + 0x00000000, // SPI_PS_INPUT_CNTL_4 + 0x00000000, // SPI_PS_INPUT_CNTL_5 + 0x00000000, // SPI_PS_INPUT_CNTL_6 + 0x00000000, // SPI_PS_INPUT_CNTL_7 + 0x00000000, // SPI_PS_INPUT_CNTL_8 + 0x00000000, // SPI_PS_INPUT_CNTL_9 + 0x00000000, // SPI_PS_INPUT_CNTL_10 + 0x00000000, // SPI_PS_INPUT_CNTL_11 + 0x00000000, // SPI_PS_INPUT_CNTL_12 + 0x00000000, // SPI_PS_INPUT_CNTL_13 + 0x00000000, // SPI_PS_INPUT_CNTL_14 + 0x00000000, // SPI_PS_INPUT_CNTL_15 + 0x00000000, // SPI_PS_INPUT_CNTL_16 + 0x00000000, // SPI_PS_INPUT_CNTL_17 + 0x00000000, // SPI_PS_INPUT_CNTL_18 + 0x00000000, // SPI_PS_INPUT_CNTL_19 + 0x00000000, // SPI_PS_INPUT_CNTL_20 + 0x00000000, // SPI_PS_INPUT_CNTL_21 + 0x00000000, // SPI_PS_INPUT_CNTL_22 + 0x00000000, // SPI_PS_INPUT_CNTL_23 + 0x00000000, // SPI_PS_INPUT_CNTL_24 + 0x00000000, // SPI_PS_INPUT_CNTL_25 + 0x00000000, // SPI_PS_INPUT_CNTL_26 + 0x00000000, // SPI_PS_INPUT_CNTL_27 + 0x00000000, // SPI_PS_INPUT_CNTL_28 + 0x00000000, // SPI_PS_INPUT_CNTL_29 + 0x00000000, // SPI_PS_INPUT_CNTL_30 + 0x00000000, // SPI_PS_INPUT_CNTL_31 + 0x00000000, // SPI_VS_OUT_CONFIG + 0x00000001, // SPI_THREAD_GROUPING + 0x00000000, // SPI_PS_IN_CONTROL_0 + 0x00000000, // SPI_PS_IN_CONTROL_1 + 0x00000000, // SPI_INTERP_CONTROL_0 + 0x00000000, // SPI_INPUT_Z + 0x00000000, // SPI_FOG_CNTL + 0x00000000, // SPI_BARYC_CNTL + 0x00000000, // SPI_PS_IN_CONTROL_2 + 0x00000000, // SPI_COMPUTE_INPUT_CNTL + 0x00000000, // SPI_COMPUTE_NUM_THREAD_X + 0x00000000, // SPI_COMPUTE_NUM_THREAD_Y + 0x00000000, // SPI_COMPUTE_NUM_THREAD_Z + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0x00000000, // GDS_ADDR_BASE + 0x00003fff, // GDS_ADDR_SIZE + 0x00000001, // GDS_ORDERED_WAVE_PER_SE + 0x00000000, // GDS_APPEND_CONSUME_UAV0 + 0x00000000, // GDS_APPEND_CONSUME_UAV1 + 0x00000000, // GDS_APPEND_CONSUME_UAV2 + 0x00000000, // GDS_APPEND_CONSUME_UAV3 + 0x00000000, // GDS_APPEND_CONSUME_UAV4 + 0x00000000, // GDS_APPEND_CONSUME_UAV5 + 0x00000000, // GDS_APPEND_CONSUME_UAV6 + 0x00000000, // GDS_APPEND_CONSUME_UAV7 + 0x00000000, // GDS_APPEND_CONSUME_UAV8 + 0x00000000, // GDS_APPEND_CONSUME_UAV9 + 0x00000000, // GDS_APPEND_CONSUME_UAV10 + 0x00000000, // GDS_APPEND_CONSUME_UAV11 + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0x00000000, // CB_BLEND0_CONTROL + 0x00000000, // CB_BLEND1_CONTROL + 0x00000000, // CB_BLEND2_CONTROL + 0x00000000, // CB_BLEND3_CONTROL + 0x00000000, // CB_BLEND4_CONTROL + 0x00000000, // CB_BLEND5_CONTROL + 0x00000000, // CB_BLEND6_CONTROL + 0x00000000, // CB_BLEND7_CONTROL +}; +static const u32 SECT_CONTEXT_def_2[] = +{ + 0x00000000, // PA_CL_POINT_X_RAD + 0x00000000, // PA_CL_POINT_Y_RAD + 0x00000000, // PA_CL_POINT_SIZE + 0x00000000, // PA_CL_POINT_CULL_RAD + 0x00000000, // VGT_DMA_BASE_HI + 0x00000000, // VGT_DMA_BASE +}; +static const u32 SECT_CONTEXT_def_3[] = +{ + 0x00000000, // DB_DEPTH_CONTROL + 0, // HOLE + 0x00000000, // CB_COLOR_CONTROL + 0x00000200, // DB_SHADER_CONTROL + 0x00000000, // PA_CL_CLIP_CNTL + 0x00000000, // PA_SU_SC_MODE_CNTL + 0x00000000, // PA_CL_VTE_CNTL + 0x00000000, // PA_CL_VS_OUT_CNTL + 0x00000000, // PA_CL_NANINF_CNTL + 0x00000000, // PA_SU_LINE_STIPPLE_CNTL + 0x00000000, // PA_SU_LINE_STIPPLE_SCALE + 0x00000000, // PA_SU_PRIM_FILTER_CNTL + 0x00000000, // SQ_LSTMP_RING_ITEMSIZE + 0x00000000, // SQ_HSTMP_RING_ITEMSIZE + 0x00000000, // SQ_DYN_GPR_RESOURCE_LIMIT_1 + 0, // HOLE + 0x00000000, // SQ_PGM_START_PS + 0x00000000, // SQ_PGM_RESOURCES_PS + 0x00000000, // SQ_PGM_RESOURCES_2_PS + 0x00000000, // SQ_PGM_EXPORTS_PS + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0x00000000, // SQ_PGM_START_VS + 0x00000000, // SQ_PGM_RESOURCES_VS + 0x00000000, // SQ_PGM_RESOURCES_2_VS + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0x00000000, // SQ_PGM_START_GS + 0x00000000, // SQ_PGM_RESOURCES_GS + 0x00000000, // SQ_PGM_RESOURCES_2_GS + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0x00000000, // SQ_PGM_START_ES + 0x00000000, // SQ_PGM_RESOURCES_ES + 0x00000000, // SQ_PGM_RESOURCES_2_ES + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0x00000000, // SQ_PGM_START_FS + 0x00000000, // SQ_PGM_RESOURCES_FS + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0x00000000, // SQ_PGM_START_HS + 0x00000000, // SQ_PGM_RESOURCES_HS + 0x00000000, // SQ_PGM_RESOURCES_2_HS + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0x00000000, // SQ_PGM_START_LS + 0x00000000, // SQ_PGM_RESOURCES_LS + 0x00000000, // SQ_PGM_RESOURCES_2_LS +}; +static const u32 SECT_CONTEXT_def_4[] = +{ + 0x00000000, // SQ_LDS_ALLOC + 0x00000000, // SQ_LDS_ALLOC_PS + 0x00000000, // SQ_VTX_SEMANTIC_CLEAR + 0, // HOLE + 0x00000000, // SQ_THREAD_TRACE_CTRL + 0, // HOLE + 0x00000000, // SQ_ESGS_RING_ITEMSIZE + 0x00000000, // SQ_GSVS_RING_ITEMSIZE + 0x00000000, // SQ_ESTMP_RING_ITEMSIZE + 0x00000000, // SQ_GSTMP_RING_ITEMSIZE + 0x00000000, // SQ_VSTMP_RING_ITEMSIZE + 0x00000000, // SQ_PSTMP_RING_ITEMSIZE + 0, // HOLE + 0x00000000, // SQ_GS_VERT_ITEMSIZE + 0x00000000, // SQ_GS_VERT_ITEMSIZE_1 + 0x00000000, // SQ_GS_VERT_ITEMSIZE_2 + 0x00000000, // SQ_GS_VERT_ITEMSIZE_3 + 0x00000000, // SQ_GSVS_RING_OFFSET_1 + 0x00000000, // SQ_GSVS_RING_OFFSET_2 + 0x00000000, // SQ_GSVS_RING_OFFSET_3 + 0, // HOLE + 0, // HOLE + 0x00000000, // SQ_ALU_CONST_CACHE_PS_0 + 0x00000000, // SQ_ALU_CONST_CACHE_PS_1 + 0x00000000, // SQ_ALU_CONST_CACHE_PS_2 + 0x00000000, // SQ_ALU_CONST_CACHE_PS_3 + 0x00000000, // SQ_ALU_CONST_CACHE_PS_4 + 0x00000000, // SQ_ALU_CONST_CACHE_PS_5 + 0x00000000, // SQ_ALU_CONST_CACHE_PS_6 + 0x00000000, // SQ_ALU_CONST_CACHE_PS_7 + 0x00000000, // SQ_ALU_CONST_CACHE_PS_8 + 0x00000000, // SQ_ALU_CONST_CACHE_PS_9 + 0x00000000, // SQ_ALU_CONST_CACHE_PS_10 + 0x00000000, // SQ_ALU_CONST_CACHE_PS_11 + 0x00000000, // SQ_ALU_CONST_CACHE_PS_12 + 0x00000000, // SQ_ALU_CONST_CACHE_PS_13 + 0x00000000, // SQ_ALU_CONST_CACHE_PS_14 + 0x00000000, // SQ_ALU_CONST_CACHE_PS_15 + 0x00000000, // SQ_ALU_CONST_CACHE_VS_0 + 0x00000000, // SQ_ALU_CONST_CACHE_VS_1 + 0x00000000, // SQ_ALU_CONST_CACHE_VS_2 + 0x00000000, // SQ_ALU_CONST_CACHE_VS_3 + 0x00000000, // SQ_ALU_CONST_CACHE_VS_4 + 0x00000000, // SQ_ALU_CONST_CACHE_VS_5 + 0x00000000, // SQ_ALU_CONST_CACHE_VS_6 + 0x00000000, // SQ_ALU_CONST_CACHE_VS_7 + 0x00000000, // SQ_ALU_CONST_CACHE_VS_8 + 0x00000000, // SQ_ALU_CONST_CACHE_VS_9 + 0x00000000, // SQ_ALU_CONST_CACHE_VS_10 + 0x00000000, // SQ_ALU_CONST_CACHE_VS_11 + 0x00000000, // SQ_ALU_CONST_CACHE_VS_12 + 0x00000000, // SQ_ALU_CONST_CACHE_VS_13 + 0x00000000, // SQ_ALU_CONST_CACHE_VS_14 + 0x00000000, // SQ_ALU_CONST_CACHE_VS_15 + 0x00000000, // SQ_ALU_CONST_CACHE_GS_0 + 0x00000000, // SQ_ALU_CONST_CACHE_GS_1 + 0x00000000, // SQ_ALU_CONST_CACHE_GS_2 + 0x00000000, // SQ_ALU_CONST_CACHE_GS_3 + 0x00000000, // SQ_ALU_CONST_CACHE_GS_4 + 0x00000000, // SQ_ALU_CONST_CACHE_GS_5 + 0x00000000, // SQ_ALU_CONST_CACHE_GS_6 + 0x00000000, // SQ_ALU_CONST_CACHE_GS_7 + 0x00000000, // SQ_ALU_CONST_CACHE_GS_8 + 0x00000000, // SQ_ALU_CONST_CACHE_GS_9 + 0x00000000, // SQ_ALU_CONST_CACHE_GS_10 + 0x00000000, // SQ_ALU_CONST_CACHE_GS_11 + 0x00000000, // SQ_ALU_CONST_CACHE_GS_12 + 0x00000000, // SQ_ALU_CONST_CACHE_GS_13 + 0x00000000, // SQ_ALU_CONST_CACHE_GS_14 + 0x00000000, // SQ_ALU_CONST_CACHE_GS_15 + 0x00000000, // PA_SU_POINT_SIZE + 0x00000000, // PA_SU_POINT_MINMAX + 0x00000000, // PA_SU_LINE_CNTL + 0x00000000, // PA_SC_LINE_STIPPLE + 0x00000000, // VGT_OUTPUT_PATH_CNTL + 0x00000000, // VGT_HOS_CNTL + 0x00000000, // VGT_HOS_MAX_TESS_LEVEL + 0x00000000, // VGT_HOS_MIN_TESS_LEVEL + 0x00000000, // VGT_HOS_REUSE_DEPTH + 0x00000000, // VGT_GROUP_PRIM_TYPE + 0x00000000, // VGT_GROUP_FIRST_DECR + 0x00000000, // VGT_GROUP_DECR + 0x00000000, // VGT_GROUP_VECT_0_CNTL + 0x00000000, // VGT_GROUP_VECT_1_CNTL + 0x00000000, // VGT_GROUP_VECT_0_FMT_CNTL + 0x00000000, // VGT_GROUP_VECT_1_FMT_CNTL + 0x00000000, // VGT_GS_MODE + 0, // HOLE + 0x00000000, // PA_SC_MODE_CNTL_0 + 0x00000000, // PA_SC_MODE_CNTL_1 + 0x00000000, // VGT_ENHANCE + 0x00000000, // VGT_GS_PER_ES + 0x00000000, // VGT_ES_PER_GS + 0x00000000, // VGT_GS_PER_VS + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0x00000000, // VGT_GS_OUT_PRIM_TYPE +}; +static const u32 SECT_CONTEXT_def_5[] = +{ + 0x00000000, // VGT_DMA_MAX_SIZE + 0x00000000, // VGT_DMA_INDEX_TYPE + 0, // HOLE + 0x00000000, // VGT_PRIMITIVEID_EN + 0x00000000, // VGT_DMA_NUM_INSTANCES +}; +static const u32 SECT_CONTEXT_def_6[] = +{ + 0x00000000, // VGT_MULTI_PRIM_IB_RESET_EN + 0, // HOLE + 0, // HOLE + 0x00000000, // VGT_INSTANCE_STEP_RATE_0 + 0x00000000, // VGT_INSTANCE_STEP_RATE_1 + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0x00000000, // VGT_REUSE_OFF + 0x00000000, // VGT_VTX_CNT_EN + 0x00000000, // DB_HTILE_SURFACE + 0x00000000, // DB_SRESULTS_COMPARE_STATE0 + 0x00000000, // DB_SRESULTS_COMPARE_STATE1 + 0x00000000, // DB_PRELOAD_CONTROL + 0, // HOLE + 0x00000000, // VGT_STRMOUT_BUFFER_SIZE_0 + 0x00000000, // VGT_STRMOUT_VTX_STRIDE_0 + 0x00000000, // VGT_STRMOUT_BUFFER_BASE_0 + 0x00000000, // VGT_STRMOUT_BUFFER_OFFSET_0 + 0x00000000, // VGT_STRMOUT_BUFFER_SIZE_1 + 0x00000000, // VGT_STRMOUT_VTX_STRIDE_1 + 0x00000000, // VGT_STRMOUT_BUFFER_BASE_1 + 0x00000000, // VGT_STRMOUT_BUFFER_OFFSET_1 + 0x00000000, // VGT_STRMOUT_BUFFER_SIZE_2 + 0x00000000, // VGT_STRMOUT_VTX_STRIDE_2 + 0x00000000, // VGT_STRMOUT_BUFFER_BASE_2 + 0x00000000, // VGT_STRMOUT_BUFFER_OFFSET_2 + 0x00000000, // VGT_STRMOUT_BUFFER_SIZE_3 + 0x00000000, // VGT_STRMOUT_VTX_STRIDE_3 + 0x00000000, // VGT_STRMOUT_BUFFER_BASE_3 + 0x00000000, // VGT_STRMOUT_BUFFER_OFFSET_3 + 0x00000000, // VGT_STRMOUT_BASE_OFFSET_0 + 0x00000000, // VGT_STRMOUT_BASE_OFFSET_1 + 0x00000000, // VGT_STRMOUT_BASE_OFFSET_2 + 0x00000000, // VGT_STRMOUT_BASE_OFFSET_3 + 0, // HOLE + 0, // HOLE + 0x00000000, // VGT_STRMOUT_DRAW_OPAQUE_OFFSET + 0x00000000, // VGT_STRMOUT_DRAW_OPAQUE_BUFFER_FILLED_SIZE + 0x00000000, // VGT_STRMOUT_DRAW_OPAQUE_VERTEX_STRIDE + 0, // HOLE + 0x00000000, // VGT_GS_MAX_VERT_OUT + 0, // HOLE + 0, // HOLE + 0x00000000, // VGT_STRMOUT_BASE_OFFSET_HI_0 + 0x00000000, // VGT_STRMOUT_BASE_OFFSET_HI_1 + 0x00000000, // VGT_STRMOUT_BASE_OFFSET_HI_2 + 0x00000000, // VGT_STRMOUT_BASE_OFFSET_HI_3 + 0x00000000, // VGT_SHADER_STAGES_EN + 0x00000000, // VGT_LS_HS_CONFIG + 0x00000000, // VGT_LS_SIZE + 0x00000000, // VGT_HS_SIZE + 0x00000000, // VGT_LS_HS_ALLOC + 0x00000000, // VGT_HS_PATCH_CONST + 0x00000000, // VGT_TF_PARAM + 0x00000000, // DB_ALPHA_TO_MASK +}; +static const u32 SECT_CONTEXT_def_7[] = +{ + 0x00000000, // PA_SU_POLY_OFFSET_DB_FMT_CNTL + 0x00000000, // PA_SU_POLY_OFFSET_CLAMP + 0x00000000, // PA_SU_POLY_OFFSET_FRONT_SCALE + 0x00000000, // PA_SU_POLY_OFFSET_FRONT_OFFSET + 0x00000000, // PA_SU_POLY_OFFSET_BACK_SCALE + 0x00000000, // PA_SU_POLY_OFFSET_BACK_OFFSET + 0x00000000, // VGT_GS_INSTANCE_CNT + 0x00000000, // VGT_STRMOUT_CONFIG + 0x00000000, // VGT_STRMOUT_BUFFER_CONFIG + 0x00000000, // CB_IMMED0_BASE + 0x00000000, // CB_IMMED1_BASE + 0x00000000, // CB_IMMED2_BASE + 0x00000000, // CB_IMMED3_BASE + 0x00000000, // CB_IMMED4_BASE + 0x00000000, // CB_IMMED5_BASE + 0x00000000, // CB_IMMED6_BASE + 0x00000000, // CB_IMMED7_BASE + 0x00000000, // CB_IMMED8_BASE + 0x00000000, // CB_IMMED9_BASE + 0x00000000, // CB_IMMED10_BASE + 0x00000000, // CB_IMMED11_BASE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0x00001000, // PA_SC_LINE_CNTL + 0x00000000, // PA_SC_AA_CONFIG + 0x00000005, // PA_SU_VTX_CNTL + 0x3f800000, // PA_CL_GB_VERT_CLIP_ADJ + 0x3f800000, // PA_CL_GB_VERT_DISC_ADJ + 0x3f800000, // PA_CL_GB_HORZ_CLIP_ADJ + 0x3f800000, // PA_CL_GB_HORZ_DISC_ADJ + 0x00000000, // PA_SC_AA_SAMPLE_LOCS_0 + 0x00000000, // PA_SC_AA_SAMPLE_LOCS_1 + 0x00000000, // PA_SC_AA_SAMPLE_LOCS_2 + 0x00000000, // PA_SC_AA_SAMPLE_LOCS_3 + 0x00000000, // PA_SC_AA_SAMPLE_LOCS_4 + 0x00000000, // PA_SC_AA_SAMPLE_LOCS_5 + 0x00000000, // PA_SC_AA_SAMPLE_LOCS_6 + 0x00000000, // PA_SC_AA_SAMPLE_LOCS_7 + 0xffffffff, // PA_SC_AA_MASK + 0x00000000, // CB_CLRCMP_CONTROL + 0x00000000, // CB_CLRCMP_SRC + 0x00000000, // CB_CLRCMP_DST + 0x00000000, // CB_CLRCMP_MSK + 0, // HOLE + 0, // HOLE + 0x0000000e, // VGT_VERTEX_REUSE_BLOCK_CNTL + 0x00000010, // VGT_OUT_DEALLOC_CNTL + 0x00000000, // CB_COLOR0_BASE + 0x00000000, // CB_COLOR0_PITCH + 0x00000000, // CB_COLOR0_SLICE + 0x00000000, // CB_COLOR0_VIEW + 0x00000000, // CB_COLOR0_INFO + 0x00000000, // CB_COLOR0_ATTRIB + 0x00000000, // CB_COLOR0_DIM + 0x00000000, // CB_COLOR0_CMASK + 0x00000000, // CB_COLOR0_CMASK_SLICE + 0x00000000, // CB_COLOR0_FMASK + 0x00000000, // CB_COLOR0_FMASK_SLICE + 0x00000000, // CB_COLOR0_CLEAR_WORD0 + 0x00000000, // CB_COLOR0_CLEAR_WORD1 + 0x00000000, // CB_COLOR0_CLEAR_WORD2 + 0x00000000, // CB_COLOR0_CLEAR_WORD3 + 0x00000000, // CB_COLOR1_BASE + 0x00000000, // CB_COLOR1_PITCH + 0x00000000, // CB_COLOR1_SLICE + 0x00000000, // CB_COLOR1_VIEW + 0x00000000, // CB_COLOR1_INFO + 0x00000000, // CB_COLOR1_ATTRIB + 0x00000000, // CB_COLOR1_DIM + 0x00000000, // CB_COLOR1_CMASK + 0x00000000, // CB_COLOR1_CMASK_SLICE + 0x00000000, // CB_COLOR1_FMASK + 0x00000000, // CB_COLOR1_FMASK_SLICE + 0x00000000, // CB_COLOR1_CLEAR_WORD0 + 0x00000000, // CB_COLOR1_CLEAR_WORD1 + 0x00000000, // CB_COLOR1_CLEAR_WORD2 + 0x00000000, // CB_COLOR1_CLEAR_WORD3 + 0x00000000, // CB_COLOR2_BASE + 0x00000000, // CB_COLOR2_PITCH + 0x00000000, // CB_COLOR2_SLICE + 0x00000000, // CB_COLOR2_VIEW + 0x00000000, // CB_COLOR2_INFO + 0x00000000, // CB_COLOR2_ATTRIB + 0x00000000, // CB_COLOR2_DIM + 0x00000000, // CB_COLOR2_CMASK + 0x00000000, // CB_COLOR2_CMASK_SLICE + 0x00000000, // CB_COLOR2_FMASK + 0x00000000, // CB_COLOR2_FMASK_SLICE + 0x00000000, // CB_COLOR2_CLEAR_WORD0 + 0x00000000, // CB_COLOR2_CLEAR_WORD1 + 0x00000000, // CB_COLOR2_CLEAR_WORD2 + 0x00000000, // CB_COLOR2_CLEAR_WORD3 + 0x00000000, // CB_COLOR3_BASE + 0x00000000, // CB_COLOR3_PITCH + 0x00000000, // CB_COLOR3_SLICE + 0x00000000, // CB_COLOR3_VIEW + 0x00000000, // CB_COLOR3_INFO + 0x00000000, // CB_COLOR3_ATTRIB + 0x00000000, // CB_COLOR3_DIM + 0x00000000, // CB_COLOR3_CMASK + 0x00000000, // CB_COLOR3_CMASK_SLICE + 0x00000000, // CB_COLOR3_FMASK + 0x00000000, // CB_COLOR3_FMASK_SLICE + 0x00000000, // CB_COLOR3_CLEAR_WORD0 + 0x00000000, // CB_COLOR3_CLEAR_WORD1 + 0x00000000, // CB_COLOR3_CLEAR_WORD2 + 0x00000000, // CB_COLOR3_CLEAR_WORD3 + 0x00000000, // CB_COLOR4_BASE + 0x00000000, // CB_COLOR4_PITCH + 0x00000000, // CB_COLOR4_SLICE + 0x00000000, // CB_COLOR4_VIEW + 0x00000000, // CB_COLOR4_INFO + 0x00000000, // CB_COLOR4_ATTRIB + 0x00000000, // CB_COLOR4_DIM + 0x00000000, // CB_COLOR4_CMASK + 0x00000000, // CB_COLOR4_CMASK_SLICE + 0x00000000, // CB_COLOR4_FMASK + 0x00000000, // CB_COLOR4_FMASK_SLICE + 0x00000000, // CB_COLOR4_CLEAR_WORD0 + 0x00000000, // CB_COLOR4_CLEAR_WORD1 + 0x00000000, // CB_COLOR4_CLEAR_WORD2 + 0x00000000, // CB_COLOR4_CLEAR_WORD3 + 0x00000000, // CB_COLOR5_BASE + 0x00000000, // CB_COLOR5_PITCH + 0x00000000, // CB_COLOR5_SLICE + 0x00000000, // CB_COLOR5_VIEW + 0x00000000, // CB_COLOR5_INFO + 0x00000000, // CB_COLOR5_ATTRIB + 0x00000000, // CB_COLOR5_DIM + 0x00000000, // CB_COLOR5_CMASK + 0x00000000, // CB_COLOR5_CMASK_SLICE + 0x00000000, // CB_COLOR5_FMASK + 0x00000000, // CB_COLOR5_FMASK_SLICE + 0x00000000, // CB_COLOR5_CLEAR_WORD0 + 0x00000000, // CB_COLOR5_CLEAR_WORD1 + 0x00000000, // CB_COLOR5_CLEAR_WORD2 + 0x00000000, // CB_COLOR5_CLEAR_WORD3 + 0x00000000, // CB_COLOR6_BASE + 0x00000000, // CB_COLOR6_PITCH + 0x00000000, // CB_COLOR6_SLICE + 0x00000000, // CB_COLOR6_VIEW + 0x00000000, // CB_COLOR6_INFO + 0x00000000, // CB_COLOR6_ATTRIB + 0x00000000, // CB_COLOR6_DIM + 0x00000000, // CB_COLOR6_CMASK + 0x00000000, // CB_COLOR6_CMASK_SLICE + 0x00000000, // CB_COLOR6_FMASK + 0x00000000, // CB_COLOR6_FMASK_SLICE + 0x00000000, // CB_COLOR6_CLEAR_WORD0 + 0x00000000, // CB_COLOR6_CLEAR_WORD1 + 0x00000000, // CB_COLOR6_CLEAR_WORD2 + 0x00000000, // CB_COLOR6_CLEAR_WORD3 + 0x00000000, // CB_COLOR7_BASE + 0x00000000, // CB_COLOR7_PITCH + 0x00000000, // CB_COLOR7_SLICE + 0x00000000, // CB_COLOR7_VIEW + 0x00000000, // CB_COLOR7_INFO + 0x00000000, // CB_COLOR7_ATTRIB + 0x00000000, // CB_COLOR7_DIM + 0x00000000, // CB_COLOR7_CMASK + 0x00000000, // CB_COLOR7_CMASK_SLICE + 0x00000000, // CB_COLOR7_FMASK + 0x00000000, // CB_COLOR7_FMASK_SLICE + 0x00000000, // CB_COLOR7_CLEAR_WORD0 + 0x00000000, // CB_COLOR7_CLEAR_WORD1 + 0x00000000, // CB_COLOR7_CLEAR_WORD2 + 0x00000000, // CB_COLOR7_CLEAR_WORD3 + 0x00000000, // CB_COLOR8_BASE + 0x00000000, // CB_COLOR8_PITCH + 0x00000000, // CB_COLOR8_SLICE + 0x00000000, // CB_COLOR8_VIEW + 0x00000000, // CB_COLOR8_INFO + 0x00000000, // CB_COLOR8_ATTRIB + 0x00000000, // CB_COLOR8_DIM + 0x00000000, // CB_COLOR9_BASE + 0x00000000, // CB_COLOR9_PITCH + 0x00000000, // CB_COLOR9_SLICE + 0x00000000, // CB_COLOR9_VIEW + 0x00000000, // CB_COLOR9_INFO + 0x00000000, // CB_COLOR9_ATTRIB + 0x00000000, // CB_COLOR9_DIM + 0x00000000, // CB_COLOR10_BASE + 0x00000000, // CB_COLOR10_PITCH + 0x00000000, // CB_COLOR10_SLICE + 0x00000000, // CB_COLOR10_VIEW + 0x00000000, // CB_COLOR10_INFO + 0x00000000, // CB_COLOR10_ATTRIB + 0x00000000, // CB_COLOR10_DIM + 0x00000000, // CB_COLOR11_BASE + 0x00000000, // CB_COLOR11_PITCH + 0x00000000, // CB_COLOR11_SLICE + 0x00000000, // CB_COLOR11_VIEW + 0x00000000, // CB_COLOR11_INFO + 0x00000000, // CB_COLOR11_ATTRIB + 0x00000000, // CB_COLOR11_DIM + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0, // HOLE + 0x00000000, // SQ_ALU_CONST_CACHE_HS_0 + 0x00000000, // SQ_ALU_CONST_CACHE_HS_1 + 0x00000000, // SQ_ALU_CONST_CACHE_HS_2 + 0x00000000, // SQ_ALU_CONST_CACHE_HS_3 + 0x00000000, // SQ_ALU_CONST_CACHE_HS_4 + 0x00000000, // SQ_ALU_CONST_CACHE_HS_5 + 0x00000000, // SQ_ALU_CONST_CACHE_HS_6 + 0x00000000, // SQ_ALU_CONST_CACHE_HS_7 + 0x00000000, // SQ_ALU_CONST_CACHE_HS_8 + 0x00000000, // SQ_ALU_CONST_CACHE_HS_9 + 0x00000000, // SQ_ALU_CONST_CACHE_HS_10 + 0x00000000, // SQ_ALU_CONST_CACHE_HS_11 + 0x00000000, // SQ_ALU_CONST_CACHE_HS_12 + 0x00000000, // SQ_ALU_CONST_CACHE_HS_13 + 0x00000000, // SQ_ALU_CONST_CACHE_HS_14 + 0x00000000, // SQ_ALU_CONST_CACHE_HS_15 + 0x00000000, // SQ_ALU_CONST_CACHE_LS_0 + 0x00000000, // SQ_ALU_CONST_CACHE_LS_1 + 0x00000000, // SQ_ALU_CONST_CACHE_LS_2 + 0x00000000, // SQ_ALU_CONST_CACHE_LS_3 + 0x00000000, // SQ_ALU_CONST_CACHE_LS_4 + 0x00000000, // SQ_ALU_CONST_CACHE_LS_5 + 0x00000000, // SQ_ALU_CONST_CACHE_LS_6 + 0x00000000, // SQ_ALU_CONST_CACHE_LS_7 + 0x00000000, // SQ_ALU_CONST_CACHE_LS_8 + 0x00000000, // SQ_ALU_CONST_CACHE_LS_9 + 0x00000000, // SQ_ALU_CONST_CACHE_LS_10 + 0x00000000, // SQ_ALU_CONST_CACHE_LS_11 + 0x00000000, // SQ_ALU_CONST_CACHE_LS_12 + 0x00000000, // SQ_ALU_CONST_CACHE_LS_13 + 0x00000000, // SQ_ALU_CONST_CACHE_LS_14 + 0x00000000, // SQ_ALU_CONST_CACHE_LS_15 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_0 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_1 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_2 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_3 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_4 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_5 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_6 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_7 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_8 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_9 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_10 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_11 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_12 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_13 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_14 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_HS_15 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_0 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_1 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_2 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_3 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_4 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_5 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_6 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_7 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_8 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_9 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_10 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_11 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_12 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_13 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_14 + 0x00000000, // SQ_ALU_CONST_BUFFER_SIZE_LS_15 +}; +static const struct cs_extent_def SECT_CONTEXT_defs[] = +{ + {SECT_CONTEXT_def_1, 0x0000a000, 488 }, + {SECT_CONTEXT_def_2, 0x0000a1f5, 6 }, + {SECT_CONTEXT_def_3, 0x0000a200, 55 }, + {SECT_CONTEXT_def_4, 0x0000a23a, 98 }, + {SECT_CONTEXT_def_5, 0x0000a29e, 5 }, + {SECT_CONTEXT_def_6, 0x0000a2a5, 56 }, + {SECT_CONTEXT_def_7, 0x0000a2de, 290 }, + { 0, 0, 0 } +}; +static const u32 SECT_CLEAR_def_1[] = +{ + 0xffffffff, // SQ_TEX_SAMPLER_CLEAR + 0xffffffff, // SQ_TEX_RESOURCE_CLEAR + 0xffffffff, // SQ_LOOP_BOOL_CLEAR +}; +static const struct cs_extent_def SECT_CLEAR_defs[] = +{ + {SECT_CLEAR_def_1, 0x0000ffc0, 3 }, + { 0, 0, 0 } +}; +static const u32 SECT_CTRLCONST_def_1[] = +{ + 0x00000000, // SQ_VTX_BASE_VTX_LOC + 0x00000000, // SQ_VTX_START_INST_LOC +}; +static const struct cs_extent_def SECT_CTRLCONST_defs[] = +{ + {SECT_CTRLCONST_def_1, 0x0000f3fc, 2 }, + { 0, 0, 0 } +}; +struct cs_section_def evergreen_cs_data[] = { + { SECT_CONTEXT_defs, SECT_CONTEXT }, + { SECT_CLEAR_defs, SECT_CLEAR }, + { SECT_CTRLCONST_defs, SECT_CTRLCONST }, + { 0, SECT_NONE } +}; diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 6b559cb5..b9f64f0 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -45,6 +45,94 @@ static const u32 crtc_offsets[6] = EVERGREEN_CRTC5_REGISTER_OFFSET };
+#include "clearstate_evergreen.h" + +static u32 sumo_rlc_save_restore_register_list[] = +{ + 0x98fc, + 0x9830, + 0x9834, + 0x9838, + 0x9870, + 0x9874, + 0x8a14, + 0x8b24, + 0x8bcc, + 0x8b10, + 0x8d00, + 0x8d04, + 0x8c00, + 0x8c04, + 0x8c08, + 0x8c0c, + 0x8d8c, + 0x8c20, + 0x8c24, + 0x8c28, + 0x8c18, + 0x8c1c, + 0x8cf0, + 0x8e2c, + 0x8e38, + 0x8c30, + 0x9508, + 0x9688, + 0x9608, + 0x960c, + 0x9610, + 0x9614, + 0x88c4, + 0x88d4, + 0xa008, + 0x900c, + 0x9100, + 0x913c, + 0x98f8, + 0x98f4, + 0x9b7c, + 0x3f8c, + 0x8950, + 0x8954, + 0x8a18, + 0x8b28, + 0x9144, + 0x9148, + 0x914c, + 0x3f90, + 0x3f94, + 0x915c, + 0x9160, + 0x9178, + 0x917c, + 0x9180, + 0x918c, + 0x9190, + 0x9194, + 0x9198, + 0x919c, + 0x91a8, + 0x91ac, + 0x91b0, + 0x91b4, + 0x91b8, + 0x91c4, + 0x91c8, + 0x91cc, + 0x91d0, + 0x91d4, + 0x91e0, + 0x91e4, + 0x91ec, + 0x91f0, + 0x91f4, + 0x9200, + 0x9204, + 0x929c, + 0x9150, + 0x802c, +}; +static u32 sumo_rlc_save_restore_register_list_size = ARRAY_SIZE(sumo_rlc_save_restore_register_list); + static void evergreen_gpu_init(struct radeon_device *rdev); void evergreen_fini(struct radeon_device *rdev); void evergreen_pcie_gen2_enable(struct radeon_device *rdev); @@ -3723,6 +3811,241 @@ bool evergreen_dma_is_lockup(struct radeon_device *rdev, struct radeon_ring *rin return radeon_ring_test_lockup(rdev, ring); }
+/* + * RLC + */ +#define RLC_SAVE_RESTORE_LIST_END_MARKER 0x00000000 +#define RLC_CLEAR_STATE_END_MARKER 0x00000001 + +void sumo_rlc_fini(struct radeon_device *rdev) +{ + int r; + + /* save restore block */ + if (rdev->rlc.save_restore_obj) { + r = radeon_bo_reserve(rdev->rlc.save_restore_obj, false); + if (unlikely(r != 0)) + dev_warn(rdev->dev, "(%d) reserve RLC sr bo failed\n", r); + radeon_bo_unpin(rdev->rlc.save_restore_obj); + radeon_bo_unreserve(rdev->rlc.save_restore_obj); + + radeon_bo_unref(&rdev->rlc.save_restore_obj); + rdev->rlc.save_restore_obj = NULL; + } + + /* clear state block */ + if (rdev->rlc.clear_state_obj) { + r = radeon_bo_reserve(rdev->rlc.clear_state_obj, false); + if (unlikely(r != 0)) + dev_warn(rdev->dev, "(%d) reserve RLC c bo failed\n", r); + radeon_bo_unpin(rdev->rlc.clear_state_obj); + radeon_bo_unreserve(rdev->rlc.clear_state_obj); + + radeon_bo_unref(&rdev->rlc.clear_state_obj); + rdev->rlc.clear_state_obj = NULL; + } +} + +int sumo_rlc_init(struct radeon_device *rdev) +{ + u32 *src_ptr; + volatile u32 *dst_ptr; + u32 dws, data, i, j, k, reg_num; + u32 reg_list_num, reg_list_hdr_blk_index, reg_list_blk_index; + u64 reg_list_mc_addr; + struct cs_section_def *cs_data; + int r; + + src_ptr = rdev->rlc.reg_list; + dws = rdev->rlc.reg_list_size; + cs_data = rdev->rlc.cs_data; + + /* save restore block */ + if (rdev->rlc.save_restore_obj == NULL) { + r = radeon_bo_create(rdev, dws * 4, PAGE_SIZE, true, + RADEON_GEM_DOMAIN_VRAM, NULL, &rdev->rlc.save_restore_obj); + if (r) { + dev_warn(rdev->dev, "(%d) create RLC sr bo failed\n", r); + return r; + } + } + + r = radeon_bo_reserve(rdev->rlc.save_restore_obj, false); + if (unlikely(r != 0)) { + sumo_rlc_fini(rdev); + return r; + } + r = radeon_bo_pin(rdev->rlc.save_restore_obj, RADEON_GEM_DOMAIN_VRAM, + &rdev->rlc.save_restore_gpu_addr); + if (r) { + radeon_bo_unreserve(rdev->rlc.save_restore_obj); + dev_warn(rdev->dev, "(%d) pin RLC sr bo failed\n", r); + sumo_rlc_fini(rdev); + return r; + } + r = radeon_bo_kmap(rdev->rlc.save_restore_obj, (void **)&rdev->rlc.sr_ptr); + if (r) { + dev_warn(rdev->dev, "(%d) map RLC sr bo failed\n", r); + sumo_rlc_fini(rdev); + return r; + } + /* write the sr buffer */ + dst_ptr = rdev->rlc.sr_ptr; + /* format: + * dw0: (reg2 << 16) | reg1 + * dw1: reg1 save space + * dw2: reg2 save space + */ + for (i = 0; i < dws; i++) { + data = src_ptr[i] >> 2; + i++; + if (i < dws) + data |= (src_ptr[i] >> 2) << 16; + j = (((i - 1) * 3) / 2); + dst_ptr[j] = data; + } + j = ((i * 3) / 2); + dst_ptr[j] = RLC_SAVE_RESTORE_LIST_END_MARKER; + + radeon_bo_kunmap(rdev->rlc.save_restore_obj); + radeon_bo_unreserve(rdev->rlc.save_restore_obj); + + /* clear state block */ + reg_list_num = 0; + dws = 0; + for (i = 0; cs_data[i].section != NULL; i++) { + for (j = 0; cs_data[i].section[j].extent != NULL; j++) { + reg_list_num++; + dws += cs_data[i].section[j].reg_count; + } + } + reg_list_blk_index = (3 * reg_list_num + 2); + dws += reg_list_blk_index; + + if (rdev->rlc.clear_state_obj == NULL) { + r = radeon_bo_create(rdev, dws * 4, PAGE_SIZE, true, + RADEON_GEM_DOMAIN_VRAM, NULL, &rdev->rlc.clear_state_obj); + if (r) { + dev_warn(rdev->dev, "(%d) create RLC c bo failed\n", r); + sumo_rlc_fini(rdev); + return r; + } + } + r = radeon_bo_reserve(rdev->rlc.clear_state_obj, false); + if (unlikely(r != 0)) { + sumo_rlc_fini(rdev); + return r; + } + r = radeon_bo_pin(rdev->rlc.clear_state_obj, RADEON_GEM_DOMAIN_VRAM, + &rdev->rlc.clear_state_gpu_addr); + if (r) { + + radeon_bo_unreserve(rdev->rlc.clear_state_obj); + dev_warn(rdev->dev, "(%d) pin RLC c bo failed\n", r); + sumo_rlc_fini(rdev); + return r; + } + r = radeon_bo_kmap(rdev->rlc.clear_state_obj, (void **)&rdev->rlc.cs_ptr); + if (r) { + dev_warn(rdev->dev, "(%d) map RLC c bo failed\n", r); + sumo_rlc_fini(rdev); + return r; + } + /* set up the cs buffer */ + dst_ptr = rdev->rlc.cs_ptr; + reg_list_hdr_blk_index = 0; + reg_list_mc_addr = rdev->rlc.clear_state_gpu_addr + (reg_list_blk_index * 4); + data = upper_32_bits(reg_list_mc_addr); + dst_ptr[reg_list_hdr_blk_index] = data; + reg_list_hdr_blk_index++; + for (i = 0; cs_data[i].section != NULL; i++) { + for (j = 0; cs_data[i].section[j].extent != NULL; j++) { + reg_num = cs_data[i].section[j].reg_count; + data = reg_list_mc_addr & 0xffffffff; + dst_ptr[reg_list_hdr_blk_index] = data; + reg_list_hdr_blk_index++; + + data = (cs_data[i].section[j].reg_index * 4) & 0xffffffff; + dst_ptr[reg_list_hdr_blk_index] = data; + reg_list_hdr_blk_index++; + + data = 0x08000000 | (reg_num * 4); + dst_ptr[reg_list_hdr_blk_index] = data; + reg_list_hdr_blk_index++; + + for (k = 0; k < reg_num; k++) { + data = cs_data[i].section[j].extent[k]; + dst_ptr[reg_list_blk_index + k] = data; + } + reg_list_mc_addr += reg_num * 4; + reg_list_blk_index += reg_num; + } + } + dst_ptr[reg_list_hdr_blk_index] = RLC_CLEAR_STATE_END_MARKER; + + radeon_bo_kunmap(rdev->rlc.clear_state_obj); + radeon_bo_unreserve(rdev->rlc.clear_state_obj); + + return 0; +} + +static void evergreen_rlc_start(struct radeon_device *rdev) +{ + if (rdev->flags & RADEON_IS_IGP) + WREG32(RLC_CNTL, RLC_ENABLE | GFX_POWER_GATING_ENABLE | GFX_POWER_GATING_SRC); + else + WREG32(RLC_CNTL, RLC_ENABLE); +} + +int evergreen_rlc_resume(struct radeon_device *rdev) +{ + u32 i; + const __be32 *fw_data; + + if (!rdev->rlc_fw) + return -EINVAL; + + r600_rlc_stop(rdev); + + WREG32(RLC_HB_CNTL, 0); + + if (rdev->flags & RADEON_IS_IGP) { + WREG32(TN_RLC_SAVE_AND_RESTORE_BASE, rdev->rlc.save_restore_gpu_addr >> 8); + WREG32(TN_RLC_CLEAR_STATE_RESTORE_BASE, rdev->rlc.clear_state_gpu_addr >> 8); + } else { + WREG32(RLC_HB_BASE, 0); + WREG32(RLC_HB_RPTR, 0); + WREG32(RLC_HB_WPTR, 0); + } + WREG32(RLC_HB_WPTR_LSB_ADDR, 0); + WREG32(RLC_HB_WPTR_MSB_ADDR, 0); + WREG32(RLC_MC_CNTL, 0); + WREG32(RLC_UCODE_CNTL, 0); + + fw_data = (const __be32 *)rdev->rlc_fw->data; + if (rdev->family >= CHIP_ARUBA) { + for (i = 0; i < ARUBA_RLC_UCODE_SIZE; i++) { + WREG32(RLC_UCODE_ADDR, i); + WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++)); + } + } else if (rdev->family >= CHIP_CAYMAN) { + for (i = 0; i < CAYMAN_RLC_UCODE_SIZE; i++) { + WREG32(RLC_UCODE_ADDR, i); + WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++)); + } + } else { + for (i = 0; i < EVERGREEN_RLC_UCODE_SIZE; i++) { + WREG32(RLC_UCODE_ADDR, i); + WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++)); + } + } + WREG32(RLC_UCODE_ADDR, 0); + + evergreen_rlc_start(rdev); + + return 0; +} + /* Interrupts */
u32 evergreen_get_vblank_counter(struct radeon_device *rdev, int crtc) @@ -4721,6 +5044,18 @@ static int evergreen_startup(struct radeon_device *rdev) dev_warn(rdev->dev, "failed blitter (%d) falling back to memcpy\n", r); }
+ /* allocate rlc buffers */ + if (rdev->flags & RADEON_IS_IGP) { + rdev->rlc.reg_list = sumo_rlc_save_restore_register_list; + rdev->rlc.reg_list_size = sumo_rlc_save_restore_register_list_size; + rdev->rlc.cs_data = evergreen_cs_data; + r = sumo_rlc_init(rdev); + if (r) { + DRM_ERROR("Failed to init rlc BOs!\n"); + return r; + } + } + /* allocate wb buffer */ r = radeon_wb_init(rdev); if (r) @@ -4952,6 +5287,8 @@ int evergreen_init(struct radeon_device *rdev) r700_cp_fini(rdev); r600_dma_fini(rdev); r600_irq_fini(rdev); + if (rdev->flags & RADEON_IS_IGP) + sumo_rlc_fini(rdev); radeon_wb_fini(rdev); radeon_ib_pool_fini(rdev); radeon_irq_kms_fini(rdev); @@ -4980,6 +5317,8 @@ void evergreen_fini(struct radeon_device *rdev) r700_cp_fini(rdev); r600_dma_fini(rdev); r600_irq_fini(rdev); + if (rdev->flags & RADEON_IS_IGP) + sumo_rlc_fini(rdev); radeon_wb_fini(rdev); radeon_ib_pool_fini(rdev); radeon_irq_kms_fini(rdev); diff --git a/drivers/gpu/drm/radeon/evergreend.h b/drivers/gpu/drm/radeon/evergreend.h index 75c0563..8603b7c 100644 --- a/drivers/gpu/drm/radeon/evergreend.h +++ b/drivers/gpu/drm/radeon/evergreend.h @@ -90,6 +90,25 @@ #define CG_VCLK_STATUS 0x61c #define CG_SCRATCH1 0x820
+#define RLC_CNTL 0x3f00 +# define RLC_ENABLE (1 << 0) +# define GFX_POWER_GATING_ENABLE (1 << 7) +# define GFX_POWER_GATING_SRC (1 << 8) +#define RLC_HB_BASE 0x3f10 +#define RLC_HB_CNTL 0x3f0c +#define RLC_HB_RPTR 0x3f20 +#define RLC_HB_WPTR 0x3f1c +#define RLC_HB_WPTR_LSB_ADDR 0x3f14 +#define RLC_HB_WPTR_MSB_ADDR 0x3f18 +#define RLC_MC_CNTL 0x3f44 +#define RLC_UCODE_CNTL 0x3f48 +#define RLC_UCODE_ADDR 0x3f2c +#define RLC_UCODE_DATA 0x3f30 + +/* new for TN */ +#define TN_RLC_SAVE_AND_RESTORE_BASE 0x3f10 +#define TN_RLC_CLEAR_STATE_RESTORE_BASE 0x3f20 + #define GRBM_GFX_INDEX 0x802C #define INSTANCE_INDEX(x) ((x) << 0) #define SE_INDEX(x) ((x) << 16) diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c index 9284346..c73d713 100644 --- a/drivers/gpu/drm/radeon/ni.c +++ b/drivers/gpu/drm/radeon/ni.c @@ -34,6 +34,134 @@ #include "ni_reg.h" #include "cayman_blit_shaders.h" #include "radeon_ucode.h" +#include "clearstate_cayman.h" + +static u32 tn_rlc_save_restore_register_list[] = +{ + 0x98fc, + 0x98f0, + 0x9834, + 0x9838, + 0x9870, + 0x9874, + 0x8a14, + 0x8b24, + 0x8bcc, + 0x8b10, + 0x8c30, + 0x8d00, + 0x8d04, + 0x8c00, + 0x8c04, + 0x8c10, + 0x8c14, + 0x8d8c, + 0x8cf0, + 0x8e38, + 0x9508, + 0x9688, + 0x9608, + 0x960c, + 0x9610, + 0x9614, + 0x88c4, + 0x8978, + 0x88d4, + 0x900c, + 0x9100, + 0x913c, + 0x90e8, + 0x9354, + 0xa008, + 0x98f8, + 0x9148, + 0x914c, + 0x3f94, + 0x98f4, + 0x9b7c, + 0x3f8c, + 0x8950, + 0x8954, + 0x8a18, + 0x8b28, + 0x9144, + 0x3f90, + 0x915c, + 0x9160, + 0x9178, + 0x917c, + 0x9180, + 0x918c, + 0x9190, + 0x9194, + 0x9198, + 0x919c, + 0x91a8, + 0x91ac, + 0x91b0, + 0x91b4, + 0x91b8, + 0x91c4, + 0x91c8, + 0x91cc, + 0x91d0, + 0x91d4, + 0x91e0, + 0x91e4, + 0x91ec, + 0x91f0, + 0x91f4, + 0x9200, + 0x9204, + 0x929c, + 0x8030, + 0x9150, + 0x9a60, + 0x920c, + 0x9210, + 0x9228, + 0x922c, + 0x9244, + 0x9248, + 0x91e8, + 0x9294, + 0x9208, + 0x9224, + 0x9240, + 0x9220, + 0x923c, + 0x9258, + 0x9744, + 0xa200, + 0xa204, + 0xa208, + 0xa20c, + 0x8d58, + 0x9030, + 0x9034, + 0x9038, + 0x903c, + 0x9040, + 0x9654, + 0x897c, + 0xa210, + 0xa214, + 0x9868, + 0xa02c, + 0x9664, + 0x9698, + 0x949c, + 0x8e10, + 0x8e18, + 0x8c50, + 0x8c58, + 0x8c60, + 0x8c68, + 0x89b4, + 0x9830, + 0x802c, +}; +static u32 tn_rlc_save_restore_register_list_size = ARRAY_SIZE(tn_rlc_save_restore_register_list);
extern bool evergreen_is_display_hung(struct radeon_device *rdev); extern void evergreen_print_gpu_status_regs(struct radeon_device *rdev); @@ -45,8 +173,8 @@ extern void evergreen_irq_suspend(struct radeon_device *rdev); extern int evergreen_mc_init(struct radeon_device *rdev); extern void evergreen_fix_pci_max_read_req_size(struct radeon_device *rdev); extern void evergreen_pcie_gen2_enable(struct radeon_device *rdev); -extern void si_rlc_fini(struct radeon_device *rdev); -extern int si_rlc_init(struct radeon_device *rdev); +extern void sumo_rlc_fini(struct radeon_device *rdev); +extern int sumo_rlc_init(struct radeon_device *rdev);
/* Firmware Names */ MODULE_FIRMWARE("radeon/BARTS_pfp.bin"); @@ -1969,7 +2097,10 @@ static int cayman_startup(struct radeon_device *rdev)
/* allocate rlc buffers */ if (rdev->flags & RADEON_IS_IGP) { - r = si_rlc_init(rdev); + rdev->rlc.reg_list = tn_rlc_save_restore_register_list; + rdev->rlc.reg_list_size = tn_rlc_save_restore_register_list_size; + rdev->rlc.cs_data = cayman_cs_data; + r = sumo_rlc_init(rdev); if (r) { DRM_ERROR("Failed to init rlc BOs!\n"); return r; @@ -2226,7 +2357,7 @@ int cayman_init(struct radeon_device *rdev) cayman_dma_fini(rdev); r600_irq_fini(rdev); if (rdev->flags & RADEON_IS_IGP) - si_rlc_fini(rdev); + sumo_rlc_fini(rdev); radeon_wb_fini(rdev); radeon_ib_pool_fini(rdev); radeon_vm_manager_fini(rdev); @@ -2257,7 +2388,7 @@ void cayman_fini(struct radeon_device *rdev) cayman_dma_fini(rdev); r600_irq_fini(rdev); if (rdev->flags & RADEON_IS_IGP) - si_rlc_fini(rdev); + sumo_rlc_fini(rdev); radeon_wb_fini(rdev); radeon_vm_manager_fini(rdev); radeon_ib_pool_fini(rdev); diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index 6089261..4678ed1 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -97,6 +97,7 @@ static void r600_gpu_init(struct radeon_device *rdev); void r600_fini(struct radeon_device *rdev); void r600_irq_disable(struct radeon_device *rdev); static void r600_pcie_gen2_enable(struct radeon_device *rdev); +extern int evergreen_rlc_resume(struct radeon_device *rdev);
/** * r600_get_xclk - get the xclk @@ -3778,7 +3779,7 @@ static void r600_rlc_start(struct radeon_device *rdev) WREG32(RLC_CNTL, RLC_ENABLE); }
-static int r600_rlc_init(struct radeon_device *rdev) +static int r600_rlc_resume(struct radeon_device *rdev) { u32 i; const __be32 *fw_data; @@ -3790,39 +3791,16 @@ static int r600_rlc_init(struct radeon_device *rdev)
WREG32(RLC_HB_CNTL, 0);
- if (rdev->family == CHIP_ARUBA) { - WREG32(TN_RLC_SAVE_AND_RESTORE_BASE, rdev->rlc.save_restore_gpu_addr >> 8); - WREG32(TN_RLC_CLEAR_STATE_RESTORE_BASE, rdev->rlc.clear_state_gpu_addr >> 8); - } - if (rdev->family <= CHIP_CAYMAN) { - WREG32(RLC_HB_BASE, 0); - WREG32(RLC_HB_RPTR, 0); - WREG32(RLC_HB_WPTR, 0); - } - if (rdev->family <= CHIP_CAICOS) { - WREG32(RLC_HB_WPTR_LSB_ADDR, 0); - WREG32(RLC_HB_WPTR_MSB_ADDR, 0); - } + WREG32(RLC_HB_BASE, 0); + WREG32(RLC_HB_RPTR, 0); + WREG32(RLC_HB_WPTR, 0); + WREG32(RLC_HB_WPTR_LSB_ADDR, 0); + WREG32(RLC_HB_WPTR_MSB_ADDR, 0); WREG32(RLC_MC_CNTL, 0); WREG32(RLC_UCODE_CNTL, 0);
fw_data = (const __be32 *)rdev->rlc_fw->data; - if (rdev->family >= CHIP_ARUBA) { - for (i = 0; i < ARUBA_RLC_UCODE_SIZE; i++) { - WREG32(RLC_UCODE_ADDR, i); - WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++)); - } - } else if (rdev->family >= CHIP_CAYMAN) { - for (i = 0; i < CAYMAN_RLC_UCODE_SIZE; i++) { - WREG32(RLC_UCODE_ADDR, i); - WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++)); - } - } else if (rdev->family >= CHIP_CEDAR) { - for (i = 0; i < EVERGREEN_RLC_UCODE_SIZE; i++) { - WREG32(RLC_UCODE_ADDR, i); - WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++)); - } - } else if (rdev->family >= CHIP_RV770) { + if (rdev->family >= CHIP_RV770) { for (i = 0; i < R700_RLC_UCODE_SIZE; i++) { WREG32(RLC_UCODE_ADDR, i); WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++)); @@ -3936,7 +3914,10 @@ int r600_irq_init(struct radeon_device *rdev) r600_disable_interrupts(rdev);
/* init rlc */ - ret = r600_rlc_init(rdev); + if (rdev->family >= CHIP_CEDAR) + ret = evergreen_rlc_resume(rdev); + else + ret = r600_rlc_resume(rdev); if (ret) { r600_ih_ring_fini(rdev); return ret; diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h index 79df558..a3f926c 100644 --- a/drivers/gpu/drm/radeon/r600d.h +++ b/drivers/gpu/drm/radeon/r600d.h @@ -684,10 +684,6 @@ #define RLC_UCODE_ADDR 0x3f2c #define RLC_UCODE_DATA 0x3f30
-/* new for TN */ -#define TN_RLC_SAVE_AND_RESTORE_BASE 0x3f10 -#define TN_RLC_CLEAR_STATE_RESTORE_BASE 0x3f20 - #define SRBM_SOFT_RESET 0xe60 # define SOFT_RESET_DMA (1 << 12) # define SOFT_RESET_RLC (1 << 13) diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 40053c8..f4cb768 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -826,15 +826,22 @@ struct r600_blit { };
/* - * SI RLC stuff + * RLC stuff */ -struct si_rlc { +#include "clearstate_defs.h" + +struct radeon_rlc { /* for power gating */ struct radeon_bo *save_restore_obj; uint64_t save_restore_gpu_addr; + volatile uint32_t *sr_ptr; + u32 *reg_list; + u32 reg_list_size; /* for clear state */ struct radeon_bo *clear_state_obj; uint64_t clear_state_gpu_addr; + volatile uint32_t *cs_ptr; + struct cs_section_def *cs_data; };
int radeon_ib_get(struct radeon_device *rdev, int ring, @@ -1774,7 +1781,7 @@ struct radeon_device { struct r600_vram_scratch vram_scratch; int msi_enabled; /* msi enabled */ struct r600_ih ih; /* r6/700 interrupt ring */ - struct si_rlc rlc; + struct radeon_rlc rlc; struct radeon_mec mec; struct work_struct hotplug_work; struct work_struct audio_work;
From: Alex Deucher alexander.deucher@amd.com
dpm needs access to atombios data and command tables for setup and calculation of a number of parameters.
v2: endian fix v3: fix mc reg table bug
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/radeon.h | 33 ++ drivers/gpu/drm/radeon/radeon_atombios.c | 660 +++++++++++++++++++++++++++++- drivers/gpu/drm/radeon/radeon_mode.h | 57 +++ 3 files changed, 743 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index f4cb768..6c445f5 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -219,6 +219,39 @@ int radeon_atom_get_clock_dividers(struct radeon_device *rdev, bool strobe_mode, struct atom_clock_dividers *dividers); void radeon_atom_set_voltage(struct radeon_device *rdev, u16 voltage_level, u8 voltage_type); +int radeon_atom_get_voltage_gpio_settings(struct radeon_device *rdev, + u16 voltage_level, u8 voltage_type, + u32 *gpio_value, u32 *gpio_mask); +void radeon_atom_set_engine_dram_timings(struct radeon_device *rdev, + u32 eng_clock, u32 mem_clock); +int radeon_atom_get_voltage_step(struct radeon_device *rdev, + u8 voltage_type, u16 *voltage_step); +int radeon_atom_round_to_true_voltage(struct radeon_device *rdev, + u8 voltage_type, + u16 nominal_voltage, + u16 *true_voltage); +int radeon_atom_get_min_voltage(struct radeon_device *rdev, + u8 voltage_type, u16 *min_voltage); +int radeon_atom_get_max_voltage(struct radeon_device *rdev, + u8 voltage_type, u16 *max_voltage); +int radeon_atom_get_voltage_table(struct radeon_device *rdev, + u8 voltage_type, + struct atom_voltage_table *voltage_table); +bool radeon_atom_is_voltage_gpio(struct radeon_device *rdev, u8 voltage_type); +void radeon_atom_update_memory_dll(struct radeon_device *rdev, + u32 mem_clock); +void radeon_atom_set_ac_timing(struct radeon_device *rdev, + u32 mem_clock); +int radeon_atom_init_mc_reg_table(struct radeon_device *rdev, + u8 module_index, + struct atom_mc_reg_table *reg_table); +int radeon_atom_get_memory_info(struct radeon_device *rdev, + u8 module_index, struct atom_memory_info *mem_info); +int radeon_atom_get_mclk_range_table(struct radeon_device *rdev, + bool gddr5, u8 module_index, + struct atom_memory_clock_range_table *mclk_range_table); +int radeon_atom_get_max_vddc(struct radeon_device *rdev, u8 voltage_type, + u16 voltage_id, u16 *voltage); void rs690_pm_info(struct radeon_device *rdev); extern void evergreen_tiling_fields(unsigned tiling_flags, unsigned *bankw, unsigned *bankh, unsigned *mtaspect, diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index bf3b924..90401fd 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -56,10 +56,6 @@ extern void radeon_add_legacy_encoder(struct drm_device *dev, uint32_t encoder_enum, uint32_t supported_device);
-/* local */ -static int radeon_atom_get_max_vddc(struct radeon_device *rdev, u8 voltage_type, - u16 voltage_id, u16 *voltage); - union atom_supported_devices { struct _ATOM_SUPPORTED_DEVICES_INFO info; struct _ATOM_SUPPORTED_DEVICES_INFO_2 info_2; @@ -1516,6 +1512,10 @@ bool radeon_atombios_get_asic_ss_info(struct radeon_device *rdev, le16_to_cpu(ss_info->info_2.asSpreadSpectrum[i].usSpreadSpectrumPercentage); ss->type = ss_info->info_2.asSpreadSpectrum[i].ucSpreadSpectrumMode; ss->rate = le16_to_cpu(ss_info->info_2.asSpreadSpectrum[i].usSpreadRateIn10Hz); + if ((crev == 2) && + ((id == ASIC_INTERNAL_ENGINE_SS) || + (id == ASIC_INTERNAL_MEMORY_SS))) + ss->rate /= 100; return true; } } @@ -1530,6 +1530,9 @@ bool radeon_atombios_get_asic_ss_info(struct radeon_device *rdev, le16_to_cpu(ss_info->info_3.asSpreadSpectrum[i].usSpreadSpectrumPercentage); ss->type = ss_info->info_3.asSpreadSpectrum[i].ucSpreadSpectrumMode; ss->rate = le16_to_cpu(ss_info->info_3.asSpreadSpectrum[i].usSpreadRateIn10Hz); + if ((id == ASIC_INTERNAL_ENGINE_SS) || + (id == ASIC_INTERNAL_MEMORY_SS)) + ss->rate /= 100; if (rdev->flags & RADEON_IS_IGP) radeon_atombios_get_igp_ss_overrides(rdev, ss, id); return true; @@ -2340,7 +2343,13 @@ static void radeon_atombios_parse_pplib_non_clock_info(struct radeon_device *rde rdev->pm.default_vddc = rdev->pm.power_state[state_index].clock_info[0].voltage.voltage; rdev->pm.default_vddci = rdev->pm.power_state[state_index].clock_info[0].voltage.vddci; } else { - /* patch the table values with the default slck/mclk from firmware info */ + u16 max_vddci = 0; + + if (ASIC_IS_DCE4(rdev)) + radeon_atom_get_max_voltage(rdev, + SET_VOLTAGE_TYPE_ASIC_VDDCI, + &max_vddci); + /* patch the table values with the default sclk/mclk from firmware info */ for (j = 0; j < mode_index; j++) { rdev->pm.power_state[state_index].clock_info[j].mclk = rdev->clock.default_mclk; @@ -2349,6 +2358,9 @@ static void radeon_atombios_parse_pplib_non_clock_info(struct radeon_device *rde if (vddc) rdev->pm.power_state[state_index].clock_info[j].voltage.voltage = vddc; + if (max_vddci) + rdev->pm.power_state[state_index].clock_info[j].voltage.vddci = + max_vddci; } } } @@ -2874,6 +2886,48 @@ void radeon_atom_set_memory_clock(struct radeon_device *rdev, atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); }
+void radeon_atom_set_engine_dram_timings(struct radeon_device *rdev, + u32 eng_clock, u32 mem_clock) +{ + SET_ENGINE_CLOCK_PS_ALLOCATION args; + int index = GetIndexIntoMasterTable(COMMAND, DynamicMemorySettings); + u32 tmp; + + memset(&args, 0, sizeof(args)); + + tmp = eng_clock & SET_CLOCK_FREQ_MASK; + tmp |= (COMPUTE_ENGINE_PLL_PARAM << 24); + + args.ulTargetEngineClock = cpu_to_le32(tmp); + if (mem_clock) + args.sReserved.ulClock = cpu_to_le32(mem_clock & SET_CLOCK_FREQ_MASK); + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); +} + +void radeon_atom_update_memory_dll(struct radeon_device *rdev, + u32 mem_clock) +{ + u32 args; + int index = GetIndexIntoMasterTable(COMMAND, DynamicMemorySettings); + + args = cpu_to_le32(mem_clock); /* 10 khz */ + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); +} + +void radeon_atom_set_ac_timing(struct radeon_device *rdev, + u32 mem_clock) +{ + SET_MEMORY_CLOCK_PS_ALLOCATION args; + int index = GetIndexIntoMasterTable(COMMAND, DynamicMemorySettings); + u32 tmp = mem_clock | (COMPUTE_MEMORY_PLL_PARAM << 24); + + args.ulTargetMemoryClock = cpu_to_le32(tmp); /* 10 khz */ + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); +} + union set_voltage { struct _SET_VOLTAGE_PS_ALLOCATION alloc; struct _SET_VOLTAGE_PARAMETERS v1; @@ -2918,8 +2972,8 @@ void radeon_atom_set_voltage(struct radeon_device *rdev, u16 voltage_level, u8 v atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); }
-static int radeon_atom_get_max_vddc(struct radeon_device *rdev, u8 voltage_type, - u16 voltage_id, u16 *voltage) +int radeon_atom_get_max_vddc(struct radeon_device *rdev, u8 voltage_type, + u16 voltage_id, u16 *voltage) { union set_voltage args; int index = GetIndexIntoMasterTable(COMMAND, SetVoltage); @@ -2957,6 +3011,598 @@ static int radeon_atom_get_max_vddc(struct radeon_device *rdev, u8 voltage_type, return 0; }
+int radeon_atom_get_voltage_gpio_settings(struct radeon_device *rdev, + u16 voltage_level, u8 voltage_type, + u32 *gpio_value, u32 *gpio_mask) +{ + union set_voltage args; + int index = GetIndexIntoMasterTable(COMMAND, SetVoltage); + u8 frev, crev; + + if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev)) + return -EINVAL; + + switch (crev) { + case 1: + return -EINVAL; + case 2: + args.v2.ucVoltageType = voltage_type; + args.v2.ucVoltageMode = SET_ASIC_VOLTAGE_MODE_GET_GPIOMASK; + args.v2.usVoltageLevel = cpu_to_le16(voltage_level); + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + + *gpio_mask = le32_to_cpu(*(u32 *)&args.v2); + + args.v2.ucVoltageType = voltage_type; + args.v2.ucVoltageMode = SET_ASIC_VOLTAGE_MODE_GET_GPIOVAL; + args.v2.usVoltageLevel = cpu_to_le16(voltage_level); + + atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); + + *gpio_value = le32_to_cpu(*(u32 *)&args.v2); + break; + default: + DRM_ERROR("Unknown table version %d, %d\n", frev, crev); + return -EINVAL; + } + + return 0; +} + +union voltage_object_info { + struct _ATOM_VOLTAGE_OBJECT_INFO v1; + struct _ATOM_VOLTAGE_OBJECT_INFO_V2 v2; +}; + +bool +radeon_atom_is_voltage_gpio(struct radeon_device *rdev, u8 voltage_type) +{ + int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo); + u8 frev, crev; + u16 data_offset, size; + int num_indices, i; + union voltage_object_info *voltage_info; + + if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size, + &frev, &crev, &data_offset)) { + voltage_info = (union voltage_object_info *) + (rdev->mode_info.atom_context->bios + data_offset); + + switch (crev) { + case 1: + num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / + sizeof(ATOM_VOLTAGE_OBJECT); + + for (i = 0; i < num_indices; i++) { + if ((voltage_info->v1.asVoltageObj[i].ucVoltageType == voltage_type) && + (voltage_info->v1.asVoltageObj[i].asControl.ucVoltageControlId == + VOLTAGE_CONTROLLED_BY_GPIO)) + return true; + } + break; + case 2: + num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / + sizeof(ATOM_VOLTAGE_OBJECT_INFO_V2); + + for (i = 0; i < num_indices; i++) { + if ((voltage_info->v2.asVoltageObj[i].ucVoltageType == voltage_type) && + (voltage_info->v2.asVoltageObj[i].asControl.ucVoltageControlId == + VOLTAGE_CONTROLLED_BY_GPIO)) + return true; + } + break; + default: + DRM_ERROR("unknown voltage object table\n"); + return false; + } + + } + return false; +} + +int radeon_atom_get_max_voltage(struct radeon_device *rdev, + u8 voltage_type, u16 *max_voltage) +{ + int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo); + u8 frev, crev; + u16 data_offset, size; + int num_indices, i; + union voltage_object_info *voltage_info; + + if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size, + &frev, &crev, &data_offset)) { + voltage_info = (union voltage_object_info *) + (rdev->mode_info.atom_context->bios + data_offset); + + switch (crev) { + case 1: + num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / + sizeof(ATOM_VOLTAGE_OBJECT); + + for (i = 0; i < num_indices; i++) { + if (voltage_info->v1.asVoltageObj[i].ucVoltageType == voltage_type) { + ATOM_VOLTAGE_FORMULA *formula = + &voltage_info->v1.asVoltageObj[i].asFormula; + if (formula->ucFlag & 1) + *max_voltage = + le16_to_cpu(formula->usVoltageBaseLevel) + + formula->ucNumOfVoltageEntries / 2 * + le16_to_cpu(formula->usVoltageStep); + else + *max_voltage = + le16_to_cpu(formula->usVoltageBaseLevel) + + (formula->ucNumOfVoltageEntries - 1) * + le16_to_cpu(formula->usVoltageStep); + return 0; + } + } + break; + case 2: + num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / + sizeof(ATOM_VOLTAGE_OBJECT_INFO_V2); + + for (i = 0; i < num_indices; i++) { + if (voltage_info->v2.asVoltageObj[i].ucVoltageType == voltage_type) { + ATOM_VOLTAGE_FORMULA_V2 *formula = + &voltage_info->v2.asVoltageObj[i].asFormula; + if (formula->ucNumOfVoltageEntries) { + *max_voltage = + le16_to_cpu(formula->asVIDAdjustEntries[ + formula->ucNumOfVoltageEntries - 1 + ].usVoltageValue); + return 0; + } + } + } + break; + default: + DRM_ERROR("unknown voltage object table\n"); + return -EINVAL; + } + + } + return -EINVAL; +} + +int radeon_atom_get_min_voltage(struct radeon_device *rdev, + u8 voltage_type, u16 *min_voltage) +{ + int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo); + u8 frev, crev; + u16 data_offset, size; + int num_indices, i; + union voltage_object_info *voltage_info; + + if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size, + &frev, &crev, &data_offset)) { + voltage_info = (union voltage_object_info *) + (rdev->mode_info.atom_context->bios + data_offset); + + switch (crev) { + case 1: + num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / + sizeof(ATOM_VOLTAGE_OBJECT); + + for (i = 0; i < num_indices; i++) { + if (voltage_info->v1.asVoltageObj[i].ucVoltageType == voltage_type) { + ATOM_VOLTAGE_FORMULA *formula = + &voltage_info->v1.asVoltageObj[i].asFormula; + *min_voltage = + le16_to_cpu(formula->usVoltageBaseLevel); + return 0; + } + } + break; + case 2: + num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / + sizeof(ATOM_VOLTAGE_OBJECT_INFO_V2); + + for (i = 0; i < num_indices; i++) { + if (voltage_info->v2.asVoltageObj[i].ucVoltageType == voltage_type) { + ATOM_VOLTAGE_FORMULA_V2 *formula = + &voltage_info->v2.asVoltageObj[i].asFormula; + if (formula->ucNumOfVoltageEntries) { + *min_voltage = + le16_to_cpu(formula->asVIDAdjustEntries[ + 0 + ].usVoltageValue); + return 0; + } + } + } + break; + default: + DRM_ERROR("unknown voltage object table\n"); + return -EINVAL; + } + + } + return -EINVAL; +} + +int radeon_atom_get_voltage_step(struct radeon_device *rdev, + u8 voltage_type, u16 *voltage_step) +{ + int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo); + u8 frev, crev; + u16 data_offset, size; + int num_indices, i; + union voltage_object_info *voltage_info; + + if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size, + &frev, &crev, &data_offset)) { + voltage_info = (union voltage_object_info *) + (rdev->mode_info.atom_context->bios + data_offset); + + switch (crev) { + case 1: + num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / + sizeof(ATOM_VOLTAGE_OBJECT); + + for (i = 0; i < num_indices; i++) { + if (voltage_info->v1.asVoltageObj[i].ucVoltageType == voltage_type) { + ATOM_VOLTAGE_FORMULA *formula = + &voltage_info->v1.asVoltageObj[i].asFormula; + if (formula->ucFlag & 1) + *voltage_step = + (le16_to_cpu(formula->usVoltageStep) + 1) / 2; + else + *voltage_step = + le16_to_cpu(formula->usVoltageStep); + return 0; + } + } + break; + case 2: + return -EINVAL; + default: + DRM_ERROR("unknown voltage object table\n"); + return -EINVAL; + } + + } + return -EINVAL; +} + +int radeon_atom_round_to_true_voltage(struct radeon_device *rdev, + u8 voltage_type, + u16 nominal_voltage, + u16 *true_voltage) +{ + u16 min_voltage, max_voltage, voltage_step; + + if (radeon_atom_get_max_voltage(rdev, voltage_type, &max_voltage)) + return -EINVAL; + if (radeon_atom_get_min_voltage(rdev, voltage_type, &min_voltage)) + return -EINVAL; + if (radeon_atom_get_voltage_step(rdev, voltage_type, &voltage_step)) + return -EINVAL; + + if (nominal_voltage <= min_voltage) + *true_voltage = min_voltage; + else if (nominal_voltage >= max_voltage) + *true_voltage = max_voltage; + else + *true_voltage = min_voltage + + ((nominal_voltage - min_voltage) / voltage_step) * + voltage_step; + + return 0; +} + +int radeon_atom_get_voltage_table(struct radeon_device *rdev, + u8 voltage_type, + struct atom_voltage_table *voltage_table) +{ + int index = GetIndexIntoMasterTable(DATA, VoltageObjectInfo); + u8 frev, crev; + u16 data_offset, size; + int num_indices, i, j, ret; + union voltage_object_info *voltage_info; + + if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size, + &frev, &crev, &data_offset)) { + voltage_info = (union voltage_object_info *) + (rdev->mode_info.atom_context->bios + data_offset); + + switch (crev) { + case 1: + DRM_ERROR("old table version %d, %d\n", frev, crev); + return -EINVAL; + case 2: + num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / + sizeof(ATOM_VOLTAGE_OBJECT_INFO_V2); + + for (i = 0; i < num_indices; i++) { + if (voltage_info->v2.asVoltageObj[i].ucVoltageType == voltage_type) { + ATOM_VOLTAGE_FORMULA_V2 *formula = + &voltage_info->v2.asVoltageObj[i].asFormula; + if (formula->ucNumOfVoltageEntries > MAX_VOLTAGE_ENTRIES) + return -EINVAL; + for (j = 0; j < formula->ucNumOfVoltageEntries; j++) { + voltage_table->entries[j].value = + le16_to_cpu(formula->asVIDAdjustEntries[j].usVoltageValue); + ret = radeon_atom_get_voltage_gpio_settings(rdev, + voltage_table->entries[j].value, + voltage_type, + &voltage_table->entries[j].smio_low, + &voltage_table->mask_low); + if (ret) + return ret; + } + voltage_table->count = formula->ucNumOfVoltageEntries; + return 0; + } + } + break; + default: + DRM_ERROR("unknown voltage object table\n"); + return -EINVAL; + } + + } + return -EINVAL; +} + +union vram_info { + struct _ATOM_VRAM_INFO_V3 v1_3; + struct _ATOM_VRAM_INFO_V4 v1_4; + struct _ATOM_VRAM_INFO_HEADER_V2_1 v2_1; +}; + +int radeon_atom_get_memory_info(struct radeon_device *rdev, + u8 module_index, struct atom_memory_info *mem_info) +{ + int index = GetIndexIntoMasterTable(DATA, VRAM_Info); + u8 frev, crev, i; + u16 data_offset, size; + union vram_info *vram_info; + u8 *p; + + memset(mem_info, 0, sizeof(struct atom_memory_info)); + + if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size, + &frev, &crev, &data_offset)) { + vram_info = (union vram_info *) + (rdev->mode_info.atom_context->bios + data_offset); + switch (frev) { + case 1: + switch (crev) { + case 3: + /* r6xx */ + if (module_index < vram_info->v1_3.ucNumOfVRAMModule) { + ATOM_VRAM_MODULE_V3 *vram_module = + (ATOM_VRAM_MODULE_V3 *)vram_info->v1_3.aVramInfo; + p = (u8 *)vram_info->v1_3.aVramInfo; + + for (i = 0; i < module_index; i++) { + vram_module = (ATOM_VRAM_MODULE_V3 *)p; + if (le16_to_cpu(vram_module->usSize) == 0) + return -EINVAL; + p += le16_to_cpu(vram_module->usSize); + } + mem_info->mem_vendor = vram_module->asMemory.ucMemoryVenderID & 0xf; + mem_info->mem_type = vram_module->asMemory.ucMemoryType & 0xf0; + } else + return -EINVAL; + break; + case 4: + /* r7xx, evergreen */ + if (module_index < vram_info->v1_4.ucNumOfVRAMModule) { + ATOM_VRAM_MODULE_V4 *vram_module = + (ATOM_VRAM_MODULE_V4 *)vram_info->v1_4.aVramInfo; + p = (u8 *)vram_info->v1_4.aVramInfo; + + for (i = 0; i < module_index; i++) { + vram_module = (ATOM_VRAM_MODULE_V4 *)p; + if (le16_to_cpu(vram_module->usModuleSize) == 0) + return -EINVAL; + p += le16_to_cpu(vram_module->usModuleSize); + } + mem_info->mem_vendor = vram_module->ucMemoryVenderID & 0xf; + mem_info->mem_type = vram_module->ucMemoryType & 0xf0; + } else + return -EINVAL; + break; + default: + DRM_ERROR("Unknown table version %d, %d\n", frev, crev); + return -EINVAL; + } + break; + case 2: + switch (crev) { + case 1: + /* ni */ + if (module_index < vram_info->v2_1.ucNumOfVRAMModule) { + ATOM_VRAM_MODULE_V7 *vram_module = + (ATOM_VRAM_MODULE_V7 *)vram_info->v2_1.aVramInfo; + p = (u8 *)vram_info->v2_1.aVramInfo; + + for (i = 0; i < module_index; i++) { + vram_module = (ATOM_VRAM_MODULE_V7 *)p; + if (le16_to_cpu(vram_module->usModuleSize) == 0) + return -EINVAL; + p += le16_to_cpu(vram_module->usModuleSize); + } + mem_info->mem_vendor = vram_module->ucMemoryVenderID & 0xf; + mem_info->mem_type = vram_module->ucMemoryType & 0xf0; + } else + return -EINVAL; + break; + default: + DRM_ERROR("Unknown table version %d, %d\n", frev, crev); + return -EINVAL; + } + break; + default: + DRM_ERROR("Unknown table version %d, %d\n", frev, crev); + return -EINVAL; + } + return 0; + } + return -EINVAL; +} + +int radeon_atom_get_mclk_range_table(struct radeon_device *rdev, + bool gddr5, u8 module_index, + struct atom_memory_clock_range_table *mclk_range_table) +{ + int index = GetIndexIntoMasterTable(DATA, VRAM_Info); + u8 frev, crev, i; + u16 data_offset, size; + union vram_info *vram_info; + u32 mem_timing_size = gddr5 ? + sizeof(ATOM_MEMORY_TIMING_FORMAT_V2) : sizeof(ATOM_MEMORY_TIMING_FORMAT); + u8 *p; + + memset(mclk_range_table, 0, sizeof(struct atom_memory_clock_range_table)); + + if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size, + &frev, &crev, &data_offset)) { + vram_info = (union vram_info *) + (rdev->mode_info.atom_context->bios + data_offset); + switch (frev) { + case 1: + switch (crev) { + case 3: + DRM_ERROR("old table version %d, %d\n", frev, crev); + return -EINVAL; + case 4: + /* r7xx, evergreen */ + if (module_index < vram_info->v1_4.ucNumOfVRAMModule) { + ATOM_VRAM_MODULE_V4 *vram_module = + (ATOM_VRAM_MODULE_V4 *)vram_info->v1_4.aVramInfo; + ATOM_MEMORY_TIMING_FORMAT *format; + p = (u8 *)vram_info->v1_4.aVramInfo; + + for (i = 0; i < module_index; i++) { + vram_module = (ATOM_VRAM_MODULE_V4 *)p; + if (le16_to_cpu(vram_module->usModuleSize) == 0) + return -EINVAL; + p += le16_to_cpu(vram_module->usModuleSize); + } + mclk_range_table->num_entries = (u8) + ((vram_module->usModuleSize - offsetof(ATOM_VRAM_MODULE_V4, asMemTiming)) / + mem_timing_size); + p = (u8 *)vram_module->asMemTiming; + for (i = 0; i < mclk_range_table->num_entries; i++) { + format = (ATOM_MEMORY_TIMING_FORMAT *)p; + mclk_range_table->mclk[i] = format->ulClkRange; + p += mem_timing_size; + } + } else + return -EINVAL; + break; + default: + DRM_ERROR("Unknown table version %d, %d\n", frev, crev); + return -EINVAL; + } + break; + case 2: + DRM_ERROR("new table version %d, %d\n", frev, crev); + return -EINVAL; + default: + DRM_ERROR("Unknown table version %d, %d\n", frev, crev); + return -EINVAL; + } + return 0; + } + return -EINVAL; +} + +#define MEM_ID_MASK 0xff000000 +#define MEM_ID_SHIFT 24 +#define CLOCK_RANGE_MASK 0x00ffffff +#define CLOCK_RANGE_SHIFT 0 +#define LOW_NIBBLE_MASK 0xf +#define DATA_EQU_PREV 0 +#define DATA_FROM_TABLE 4 + +int radeon_atom_init_mc_reg_table(struct radeon_device *rdev, + u8 module_index, + struct atom_mc_reg_table *reg_table) +{ + int index = GetIndexIntoMasterTable(DATA, VRAM_Info); + u8 frev, crev, num_entries, t_mem_id, num_ranges = 0; + u32 i = 0, j; + u16 data_offset, size; + union vram_info *vram_info; + + memset(reg_table, 0, sizeof(struct atom_mc_reg_table)); + + if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size, + &frev, &crev, &data_offset)) { + vram_info = (union vram_info *) + (rdev->mode_info.atom_context->bios + data_offset); + switch (frev) { + case 1: + DRM_ERROR("old table version %d, %d\n", frev, crev); + return -EINVAL; + case 2: + switch (crev) { + case 1: + if (module_index < vram_info->v2_1.ucNumOfVRAMModule) { + ATOM_INIT_REG_BLOCK *reg_block = + (ATOM_INIT_REG_BLOCK *) + ((u8 *)vram_info + le16_to_cpu(vram_info->v2_1.usMemClkPatchTblOffset)); + ATOM_MEMORY_SETTING_DATA_BLOCK *reg_data = + (ATOM_MEMORY_SETTING_DATA_BLOCK *) + ((u8 *)reg_block + (2 * sizeof(u16)) + + le16_to_cpu(reg_block->usRegIndexTblSize)); + num_entries = (u8)((le16_to_cpu(reg_block->usRegIndexTblSize)) / + sizeof(ATOM_INIT_REG_INDEX_FORMAT)) - 1; + if (num_entries > VBIOS_MC_REGISTER_ARRAY_SIZE) + return -EINVAL; + while (!(reg_block->asRegIndexBuf[i].ucPreRegDataLength & ACCESS_PLACEHOLDER) && + (i < num_entries)) { + reg_table->mc_reg_address[i].s1 = + (u16)(reg_block->asRegIndexBuf[i].usRegIndex); + reg_table->mc_reg_address[i].pre_reg_data = + (u8)(reg_block->asRegIndexBuf[i].ucPreRegDataLength); + i++; + } + reg_table->last = i; + while ((*(u32 *)reg_data != END_OF_REG_DATA_BLOCK) && + (num_ranges < VBIOS_MAX_AC_TIMING_ENTRIES)) { + t_mem_id = (u8)((*(u32 *)reg_data & MEM_ID_MASK) >> MEM_ID_SHIFT); + if (module_index == t_mem_id) { + reg_table->mc_reg_table_entry[num_ranges].mclk_max = + (u32)((*(u32 *)reg_data & CLOCK_RANGE_MASK) >> CLOCK_RANGE_SHIFT); + for (i = 0, j = 1; i < reg_table->last; i++) { + if ((reg_table->mc_reg_address[i].pre_reg_data & LOW_NIBBLE_MASK) == DATA_FROM_TABLE) { + reg_table->mc_reg_table_entry[num_ranges].mc_data[i] = + (u32)*((u32 *)reg_data + j); + j++; + } else if ((reg_table->mc_reg_address[i].pre_reg_data & LOW_NIBBLE_MASK) == DATA_EQU_PREV) { + reg_table->mc_reg_table_entry[num_ranges].mc_data[i] = + reg_table->mc_reg_table_entry[num_ranges].mc_data[i - 1]; + } + } + num_ranges++; + } + reg_data += reg_block->usRegDataBlkSize; + } + if (*(u32 *)reg_data != END_OF_REG_DATA_BLOCK) + return -EINVAL; + reg_table->num_entries = num_ranges; + } else + return -EINVAL; + break; + default: + DRM_ERROR("Unknown table version %d, %d\n", frev, crev); + return -EINVAL; + } + break; + default: + DRM_ERROR("Unknown table version %d, %d\n", frev, crev); + return -EINVAL; + } + return 0; + } + return -EINVAL; +} + void radeon_atom_initialize_bios_scratch_regs(struct drm_device *dev) { struct radeon_device *rdev = dev->dev_private; diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index 576511f..5a1c69e 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -519,6 +519,63 @@ struct atom_clock_dividers { u32 flags; };
+#define MEM_TYPE_GDDR5 0x50 +#define MEM_TYPE_GDDR4 0x40 +#define MEM_TYPE_GDDR3 0x30 +#define MEM_TYPE_DDR2 0x20 +#define MEM_TYPE_GDDR1 0x10 +#define MEM_TYPE_DDR3 0xb0 +#define MEM_TYPE_MASK 0xf0 + +struct atom_memory_info { + u8 mem_vendor; + u8 mem_type; +}; + +#define MAX_AC_TIMING_ENTRIES 16 + +struct atom_memory_clock_range_table +{ + u8 num_entries; + u8 rsv[3]; + u32 mclk[MAX_AC_TIMING_ENTRIES]; +}; + +#define VBIOS_MC_REGISTER_ARRAY_SIZE 32 +#define VBIOS_MAX_AC_TIMING_ENTRIES 20 + +struct atom_mc_reg_entry { + u32 mclk_max; + u32 mc_data[VBIOS_MC_REGISTER_ARRAY_SIZE]; +}; + +struct atom_mc_register_address { + u16 s1; + u8 pre_reg_data; +}; + +struct atom_mc_reg_table { + u8 last; + u8 num_entries; + struct atom_mc_reg_entry mc_reg_table_entry[VBIOS_MAX_AC_TIMING_ENTRIES]; + struct atom_mc_register_address mc_reg_address[VBIOS_MC_REGISTER_ARRAY_SIZE]; +}; + +#define MAX_VOLTAGE_ENTRIES 32 + +struct atom_voltage_table_entry +{ + u16 value; + u32 smio_low; +}; + +struct atom_voltage_table +{ + u32 count; + u32 mask_low; + struct atom_voltage_table_entry entries[MAX_VOLTAGE_ENTRIES]; +}; + extern enum radeon_tv_std radeon_combios_get_tv_info(struct radeon_device *rdev); extern enum radeon_tv_std
From: Alex Deucher alexander.deucher@amd.com
Has a different dpm controller than r600.
v2: rebase on gpu reset changes v3: rebase on get_xclk changes
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/radeon_asic.c | 96 ++++++++++++++++++++++++++++++++-- 1 files changed, 91 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index 5736377..d9c8e9a 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1025,6 +1025,93 @@ static struct radeon_asic r600_asic = { }, };
+static struct radeon_asic rv6xx_asic = { + .init = &r600_init, + .fini = &r600_fini, + .suspend = &r600_suspend, + .resume = &r600_resume, + .vga_set_state = &r600_vga_set_state, + .asic_reset = &r600_asic_reset, + .ioctl_wait_idle = r600_ioctl_wait_idle, + .gui_idle = &r600_gui_idle, + .mc_wait_for_idle = &r600_mc_wait_for_idle, + .get_xclk = &r600_get_xclk, + .get_gpu_clock_counter = &r600_get_gpu_clock_counter, + .gart = { + .tlb_flush = &r600_pcie_gart_tlb_flush, + .set_page = &rs600_gart_set_page, + }, + .ring = { + [RADEON_RING_TYPE_GFX_INDEX] = { + .ib_execute = &r600_ring_ib_execute, + .emit_fence = &r600_fence_ring_emit, + .emit_semaphore = &r600_semaphore_ring_emit, + .cs_parse = &r600_cs_parse, + .ring_test = &r600_ring_test, + .ib_test = &r600_ib_test, + .is_lockup = &r600_gfx_is_lockup, + }, + [R600_RING_TYPE_DMA_INDEX] = { + .ib_execute = &r600_dma_ring_ib_execute, + .emit_fence = &r600_dma_fence_ring_emit, + .emit_semaphore = &r600_dma_semaphore_ring_emit, + .cs_parse = &r600_dma_cs_parse, + .ring_test = &r600_dma_ring_test, + .ib_test = &r600_dma_ib_test, + .is_lockup = &r600_dma_is_lockup, + } + }, + .irq = { + .set = &r600_irq_set, + .process = &r600_irq_process, + }, + .display = { + .bandwidth_update = &rv515_bandwidth_update, + .get_vblank_counter = &rs600_get_vblank_counter, + .wait_for_vblank = &avivo_wait_for_vblank, + .set_backlight_level = &atombios_set_backlight_level, + .get_backlight_level = &atombios_get_backlight_level, + }, + .copy = { + .blit = &r600_copy_blit, + .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX, + .dma = &r600_copy_dma, + .dma_ring_index = R600_RING_TYPE_DMA_INDEX, + .copy = &r600_copy_dma, + .copy_ring_index = R600_RING_TYPE_DMA_INDEX, + }, + .surface = { + .set_reg = r600_set_surface_reg, + .clear_reg = r600_clear_surface_reg, + }, + .hpd = { + .init = &r600_hpd_init, + .fini = &r600_hpd_fini, + .sense = &r600_hpd_sense, + .set_polarity = &r600_hpd_set_polarity, + }, + .pm = { + .misc = &r600_pm_misc, + .prepare = &rs600_pm_prepare, + .finish = &rs600_pm_finish, + .init_profile = &r600_pm_init_profile, + .get_dynpm_state = &r600_pm_get_dynpm_state, + .get_engine_clock = &radeon_atom_get_engine_clock, + .set_engine_clock = &radeon_atom_set_engine_clock, + .get_memory_clock = &radeon_atom_get_memory_clock, + .set_memory_clock = &radeon_atom_set_memory_clock, + .get_pcie_lanes = &r600_get_pcie_lanes, + .set_pcie_lanes = &r600_set_pcie_lanes, + .set_clock_gating = NULL, + .get_temperature = &rv6xx_get_temp, + }, + .pflip = { + .pre_page_flip = &rs600_pre_page_flip, + .page_flip = &rs600_page_flip, + .post_page_flip = &rs600_post_page_flip, + }, +}; + static struct radeon_asic rs780_asic = { .init = &r600_init, .fini = &r600_fini, @@ -2286,16 +2373,15 @@ int radeon_asic_init(struct radeon_device *rdev) rdev->asic = &r520_asic; break; case CHIP_R600: + rdev->asic = &r600_asic; + break; case CHIP_RV610: case CHIP_RV630: case CHIP_RV620: case CHIP_RV635: case CHIP_RV670: - rdev->asic = &r600_asic; - if (rdev->family == CHIP_R600) - rdev->has_uvd = false; - else - rdev->has_uvd = true; + rdev->asic = &rv6xx_asic; + rdev->has_uvd = true; break; case CHIP_RS780: case CHIP_RS880:
From: Alex Deucher alexander.deucher@amd.com
This adds the common dpm (dynamic power management) infrastructure: - dpm callbacks - dpm init/fini/suspend/resume - dpm power state selection
No device specific code is enabled yet.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/radeon.h | 100 +++++++- drivers/gpu/drm/radeon/radeon_drv.c | 4 + drivers/gpu/drm/radeon/radeon_pm.c | 496 ++++++++++++++++++++++++++++++++++- 3 files changed, 591 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 6c445f5..c43673c 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -96,6 +96,7 @@ extern int radeon_pcie_gen2; extern int radeon_msi; extern int radeon_lockup_timeout; extern int radeon_fastfb; +extern int radeon_dpm;
/* * Copy from radeon_drv.h so we don't have to include both and have conflicting @@ -1048,6 +1049,7 @@ struct radeon_wb { enum radeon_pm_method { PM_METHOD_PROFILE, PM_METHOD_DYNPM, + PM_METHOD_DPM, };
enum radeon_dynpm_state { @@ -1073,11 +1075,23 @@ enum radeon_voltage_type { };
enum radeon_pm_state_type { + /* not used for dpm */ POWER_STATE_TYPE_DEFAULT, POWER_STATE_TYPE_POWERSAVE, + /* user selectable states */ POWER_STATE_TYPE_BATTERY, POWER_STATE_TYPE_BALANCED, POWER_STATE_TYPE_PERFORMANCE, + /* internal states */ + POWER_STATE_TYPE_INTERNAL_UVD, + POWER_STATE_TYPE_INTERNAL_UVD_SD, + POWER_STATE_TYPE_INTERNAL_UVD_HD, + POWER_STATE_TYPE_INTERNAL_UVD_HD2, + POWER_STATE_TYPE_INTERNAL_UVD_MVC, + POWER_STATE_TYPE_INTERNAL_BOOT, + POWER_STATE_TYPE_INTERNAL_THERMAL, + POWER_STATE_TYPE_INTERNAL_ACPI, + POWER_STATE_TYPE_INTERNAL_ULV, };
enum radeon_pm_profile_type { @@ -1106,12 +1120,16 @@ struct radeon_pm_profile {
enum radeon_int_thermal_type { THERMAL_TYPE_NONE, + THERMAL_TYPE_EXTERNAL, + THERMAL_TYPE_EXTERNAL_GPIO, THERMAL_TYPE_RV6XX, THERMAL_TYPE_RV770, + THERMAL_TYPE_ADT7473_WITH_INTERNAL, THERMAL_TYPE_EVERGREEN, THERMAL_TYPE_SUMO, THERMAL_TYPE_NI, THERMAL_TYPE_SI, + THERMAL_TYPE_EMC2103_WITH_INTERNAL, THERMAL_TYPE_CI, };
@@ -1166,6 +1184,60 @@ struct radeon_power_state { */ #define RADEON_MODE_OVERCLOCK_MARGIN 500 /* 5 MHz */
+struct radeon_ps { + u32 caps; /* vbios flags */ + u32 class; /* vbios flags */ + u32 class2; /* vbios flags */ + /* UVD clocks */ + u32 vclk; + u32 dclk; + /* asic priv */ + void *ps_priv; +}; + +struct radeon_dpm_thermal { + /* thermal interrupt work */ + struct work_struct work; + /* low temperature threshold */ + int min_temp; + /* high temperature threshold */ + int max_temp; + /* was interrupt low to high or high to low */ + bool high_to_low; +}; + +struct radeon_dpm { + struct radeon_ps *ps; + /* number of valid power states */ + int num_ps; + /* current power state that is active */ + struct radeon_ps *current_ps; + /* requested power state */ + struct radeon_ps *requested_ps; + /* boot up power state */ + struct radeon_ps *boot_ps; + /* default uvd power state */ + struct radeon_ps *uvd_ps; + enum radeon_pm_state_type state; + enum radeon_pm_state_type user_state; + u32 platform_caps; + u32 voltage_response_time; + u32 backbias_response_time; + void *priv; + u32 new_active_crtcs; + int new_active_crtc_count; + u32 current_active_crtcs; + int current_active_crtc_count; + /* special states active */ + bool thermal_active; + /* thermal handling */ + struct radeon_dpm_thermal thermal; +}; + +void radeon_dpm_enable_power_state(struct radeon_device *rdev, + enum radeon_pm_state_type dpm_state); + + struct radeon_pm { struct mutex mutex; /* write locked while reprogramming mclk */ @@ -1219,6 +1291,9 @@ struct radeon_pm { /* internal thermal controller on rv6xx+ */ enum radeon_int_thermal_type int_thermal_type; struct device *int_hwmon_dev; + /* dpm */ + bool dpm_enabled; + struct radeon_dpm dpm; };
int radeon_pm_get_type_index(struct radeon_device *rdev, @@ -1416,7 +1491,7 @@ struct radeon_asic { bool (*sense)(struct radeon_device *rdev, enum radeon_hpd_id hpd); void (*set_polarity)(struct radeon_device *rdev, enum radeon_hpd_id hpd); } hpd; - /* power management */ + /* static power management */ struct { void (*misc)(struct radeon_device *rdev); void (*prepare)(struct radeon_device *rdev); @@ -1433,6 +1508,19 @@ struct radeon_asic { int (*set_uvd_clocks)(struct radeon_device *rdev, u32 vclk, u32 dclk); int (*get_temperature)(struct radeon_device *rdev); } pm; + /* dynamic power management */ + struct { + int (*init)(struct radeon_device *rdev); + void (*setup_asic)(struct radeon_device *rdev); + int (*enable)(struct radeon_device *rdev); + void (*disable)(struct radeon_device *rdev); + int (*set_power_state)(struct radeon_device *rdev); + void (*display_configuration_changed)(struct radeon_device *rdev); + void (*fini)(struct radeon_device *rdev); + u32 (*get_sclk)(struct radeon_device *rdev, bool low); + u32 (*get_mclk)(struct radeon_device *rdev, bool low); + void (*print_power_state)(struct radeon_device *rdev, struct radeon_ps *ps); + } dpm; /* pageflipping */ struct { void (*pre_page_flip)(struct radeon_device *rdev, int crtc); @@ -2122,6 +2210,16 @@ void radeon_ring_write(struct radeon_ring *ring, uint32_t v); #define radeon_mc_wait_for_idle(rdev) (rdev)->asic->mc_wait_for_idle((rdev)) #define radeon_get_xclk(rdev) (rdev)->asic->get_xclk((rdev)) #define radeon_get_gpu_clock_counter(rdev) (rdev)->asic->get_gpu_clock_counter((rdev)) +#define radeon_dpm_init(rdev) rdev->asic->dpm.init((rdev)) +#define radeon_dpm_setup_asic(rdev) rdev->asic->dpm.setup_asic((rdev)) +#define radeon_dpm_enable(rdev) rdev->asic->dpm.enable((rdev)) +#define radeon_dpm_disable(rdev) rdev->asic->dpm.disable((rdev)) +#define radeon_dpm_set_power_state(rdev) rdev->asic->dpm.set_power_state((rdev)) +#define radeon_dpm_display_configuration_changed(rdev) rdev->asic->dpm.display_configuration_changed((rdev)) +#define radeon_dpm_fini(rdev) rdev->asic->dpm.fini((rdev)) +#define radeon_dpm_get_sclk(rdev, l) rdev->asic->dpm.get_sclk((rdev), (l)) +#define radeon_dpm_get_mclk(rdev, l) rdev->asic->dpm.get_mclk((rdev), (l)) +#define radeon_dpm_print_power_state(rdev, ps) rdev->asic->dpm.print_power_state((rdev), (ps))
/* Common functions */ /* AGP */ diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 02709e4..00cc52e 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -165,6 +165,7 @@ int radeon_pcie_gen2 = -1; int radeon_msi = -1; int radeon_lockup_timeout = 10000; int radeon_fastfb = 0; +int radeon_dpm = -1;
MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch registers"); module_param_named(no_wb, radeon_no_wb, int, 0444); @@ -220,6 +221,9 @@ module_param_named(lockup_timeout, radeon_lockup_timeout, int, 0444); MODULE_PARM_DESC(fastfb, "Direct FB access for IGP chips (0 = disable, 1 = enable)"); module_param_named(fastfb, radeon_fastfb, int, 0444);
+MODULE_PARM_DESC(dpm, "DPM support (1 = enable, 0 = disable, -1 = auto)"); +module_param_named(dpm, radeon_dpm, int, 0444); + static struct pci_device_id pciidlist[] = { radeon_PCI_IDS }; diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index e8c1bea..4f5422e 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -388,7 +388,8 @@ static ssize_t radeon_get_pm_method(struct device *dev, int pm = rdev->pm.pm_method;
return snprintf(buf, PAGE_SIZE, "%s\n", - (pm == PM_METHOD_DYNPM) ? "dynpm" : "profile"); + (pm == PM_METHOD_DYNPM) ? "dynpm" : + (pm == PM_METHOD_PROFILE) ? "profile" : "dpm"); }
static ssize_t radeon_set_pm_method(struct device *dev, @@ -399,6 +400,11 @@ static ssize_t radeon_set_pm_method(struct device *dev, struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev)); struct radeon_device *rdev = ddev->dev_private;
+ /* we don't support the legacy modes with dpm */ + if (rdev->pm.pm_method == PM_METHOD_DPM) { + count = -EINVAL; + goto fail; + }
if (strncmp("dynpm", buf, strlen("dynpm")) == 0) { mutex_lock(&rdev->pm.mutex); @@ -423,8 +429,48 @@ fail: return count; }
+static ssize_t radeon_get_dpm_state(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev)); + struct radeon_device *rdev = ddev->dev_private; + enum radeon_pm_state_type pm = rdev->pm.dpm.user_state; + + return snprintf(buf, PAGE_SIZE, "%s\n", + (pm == POWER_STATE_TYPE_BATTERY) ? "battery" : + (pm == POWER_STATE_TYPE_BALANCED) ? "balanced" : "performance"); +} + +static ssize_t radeon_set_dpm_state(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev)); + struct radeon_device *rdev = ddev->dev_private; + + mutex_lock(&rdev->pm.mutex); + if (strncmp("battery", buf, strlen("battery")) == 0) + rdev->pm.dpm.user_state = POWER_STATE_TYPE_BATTERY; + else if (strncmp("balanced", buf, strlen("balanced")) == 0) + rdev->pm.dpm.user_state = POWER_STATE_TYPE_BALANCED; + else if (strncmp("performance", buf, strlen("performance")) == 0) + rdev->pm.dpm.user_state = POWER_STATE_TYPE_PERFORMANCE; + else { + mutex_unlock(&rdev->pm.mutex); + count = -EINVAL; + goto fail; + } + mutex_unlock(&rdev->pm.mutex); + radeon_pm_compute_clocks(rdev); +fail: + return count; +} + static DEVICE_ATTR(power_profile, S_IRUGO | S_IWUSR, radeon_get_pm_profile, radeon_set_pm_profile); static DEVICE_ATTR(power_method, S_IRUGO | S_IWUSR, radeon_get_pm_method, radeon_set_pm_method); +static DEVICE_ATTR(power_dpm_state, S_IRUGO | S_IWUSR, radeon_get_dpm_state, radeon_set_dpm_state);
static ssize_t radeon_hwmon_show_temp(struct device *dev, struct device_attribute *attr, @@ -508,7 +554,228 @@ static void radeon_hwmon_fini(struct radeon_device *rdev) } }
-void radeon_pm_suspend(struct radeon_device *rdev) +static void radeon_dpm_thermal_work_handler(struct work_struct *work) +{ + struct radeon_device *rdev = + container_of(work, struct radeon_device, + pm.dpm.thermal.work); + /* switch to the thermal state */ + enum radeon_pm_state_type dpm_state = POWER_STATE_TYPE_INTERNAL_THERMAL; + + if (!rdev->pm.dpm_enabled) + return; + + if (rdev->asic->pm.get_temperature) { + int temp = radeon_get_temperature(rdev); + + if (temp < rdev->pm.dpm.thermal.min_temp) + /* switch back the user state */ + dpm_state = rdev->pm.dpm.user_state; + } else { + if (rdev->pm.dpm.thermal.high_to_low) + /* switch back the user state */ + dpm_state = rdev->pm.dpm.user_state; + } + radeon_dpm_enable_power_state(rdev, dpm_state); +} + +static struct radeon_ps *radeon_dpm_pick_power_state(struct radeon_device *rdev, + enum radeon_pm_state_type dpm_state) +{ + int i; + struct radeon_ps *ps; + u32 ui_class; + +restart_search: + /* balanced states don't exist at the moment */ + if (dpm_state == POWER_STATE_TYPE_BALANCED) + dpm_state = POWER_STATE_TYPE_PERFORMANCE; + + /* Pick the best power state based on current conditions */ + for (i = 0; i < rdev->pm.dpm.num_ps; i++) { + ps = &rdev->pm.dpm.ps[i]; + ui_class = ps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK; + switch (dpm_state) { + /* user states */ + case POWER_STATE_TYPE_BATTERY: + if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_BATTERY) { + if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) { + if (rdev->pm.dpm.new_active_crtc_count < 2) + return ps; + } else + return ps; + } + break; + case POWER_STATE_TYPE_BALANCED: + if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_BALANCED) { + if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) { + if (rdev->pm.dpm.new_active_crtc_count < 2) + return ps; + } else + return ps; + } + break; + case POWER_STATE_TYPE_PERFORMANCE: + if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE) { + if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) { + if (rdev->pm.dpm.new_active_crtc_count < 2) + return ps; + } else + return ps; + } + break; + /* internal states */ + case POWER_STATE_TYPE_INTERNAL_UVD: + return rdev->pm.dpm.uvd_ps; + case POWER_STATE_TYPE_INTERNAL_UVD_SD: + if (ps->class & ATOM_PPLIB_CLASSIFICATION_SDSTATE) + return ps; + break; + case POWER_STATE_TYPE_INTERNAL_UVD_HD: + if (ps->class & ATOM_PPLIB_CLASSIFICATION_HDSTATE) + return ps; + break; + case POWER_STATE_TYPE_INTERNAL_UVD_HD2: + if (ps->class & ATOM_PPLIB_CLASSIFICATION_HD2STATE) + return ps; + break; + case POWER_STATE_TYPE_INTERNAL_UVD_MVC: + if (ps->class2 & ATOM_PPLIB_CLASSIFICATION2_MVC) + return ps; + break; + case POWER_STATE_TYPE_INTERNAL_BOOT: + return rdev->pm.dpm.boot_ps; + case POWER_STATE_TYPE_INTERNAL_THERMAL: + if (ps->class & ATOM_PPLIB_CLASSIFICATION_THERMAL) + return ps; + break; + case POWER_STATE_TYPE_INTERNAL_ACPI: + if (ps->class & ATOM_PPLIB_CLASSIFICATION_ACPI) + return ps; + break; + case POWER_STATE_TYPE_INTERNAL_ULV: + if (ps->class2 & ATOM_PPLIB_CLASSIFICATION2_ULV) + return ps; + break; + default: + break; + } + } + /* use a fallback state if we didn't match */ + switch (dpm_state) { + case POWER_STATE_TYPE_INTERNAL_UVD_SD: + case POWER_STATE_TYPE_INTERNAL_UVD_HD: + case POWER_STATE_TYPE_INTERNAL_UVD_HD2: + case POWER_STATE_TYPE_INTERNAL_UVD_MVC: + return rdev->pm.dpm.uvd_ps; + case POWER_STATE_TYPE_INTERNAL_THERMAL: + dpm_state = POWER_STATE_TYPE_INTERNAL_ACPI; + goto restart_search; + case POWER_STATE_TYPE_INTERNAL_ACPI: + dpm_state = POWER_STATE_TYPE_BATTERY; + goto restart_search; + case POWER_STATE_TYPE_BATTERY: + dpm_state = POWER_STATE_TYPE_PERFORMANCE; + goto restart_search; + default: + break; + } + + return NULL; +} + +static void radeon_dpm_change_power_state_locked(struct radeon_device *rdev) +{ + int i; + struct radeon_ps *ps; + enum radeon_pm_state_type dpm_state; + + /* if dpm init failed */ + if (!rdev->pm.dpm_enabled) + return; + + if (rdev->pm.dpm.user_state != rdev->pm.dpm.state) { + /* add other state override checks here */ + if (!rdev->pm.dpm.thermal_active) + rdev->pm.dpm.state = rdev->pm.dpm.user_state; + } + dpm_state = rdev->pm.dpm.state; + + ps = radeon_dpm_pick_power_state(rdev, dpm_state); + if (ps) + rdev->pm.dpm.requested_ps = ps; + else + return; + + /* no need to reprogram if nothing changed */ + if (rdev->pm.dpm.current_ps == rdev->pm.dpm.requested_ps) { + /* update display watermarks based on new power state */ + if (rdev->pm.dpm.new_active_crtcs != rdev->pm.dpm.current_active_crtcs) { + radeon_bandwidth_update(rdev); + /* update displays */ + radeon_dpm_display_configuration_changed(rdev); + rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs; + rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count; + } + return; + } + + printk("switching from power state:\n"); + radeon_dpm_print_power_state(rdev, rdev->pm.dpm.current_ps); + printk("switching to power state:\n"); + radeon_dpm_print_power_state(rdev, rdev->pm.dpm.requested_ps); + + mutex_lock(&rdev->ddev->struct_mutex); + down_write(&rdev->pm.mclk_lock); + mutex_lock(&rdev->ring_lock); + + /* update display watermarks based on new power state */ + radeon_bandwidth_update(rdev); + /* update displays */ + radeon_dpm_display_configuration_changed(rdev); + + rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs; + rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count; + + /* wait for the rings to drain */ + for (i = 0; i < RADEON_NUM_RINGS; i++) { + struct radeon_ring *ring = &rdev->ring[i]; + if (ring->ready) + radeon_fence_wait_empty_locked(rdev, i); + } + + /* program the new power state */ + radeon_dpm_set_power_state(rdev); + + /* update current power state */ + rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps; + + mutex_unlock(&rdev->ring_lock); + up_write(&rdev->pm.mclk_lock); + mutex_unlock(&rdev->ddev->struct_mutex); +} + +void radeon_dpm_enable_power_state(struct radeon_device *rdev, + enum radeon_pm_state_type dpm_state) +{ + if (!rdev->pm.dpm_enabled) + return; + + mutex_lock(&rdev->pm.mutex); + switch (dpm_state) { + case POWER_STATE_TYPE_INTERNAL_THERMAL: + rdev->pm.dpm.thermal_active = true; + break; + default: + rdev->pm.dpm.thermal_active = false; + break; + } + rdev->pm.dpm.state = dpm_state; + mutex_unlock(&rdev->pm.mutex); + radeon_pm_compute_clocks(rdev); +} + +static void radeon_pm_suspend_old(struct radeon_device *rdev) { mutex_lock(&rdev->pm.mutex); if (rdev->pm.pm_method == PM_METHOD_DYNPM) { @@ -520,7 +787,26 @@ void radeon_pm_suspend(struct radeon_device *rdev) cancel_delayed_work_sync(&rdev->pm.dynpm_idle_work); }
-void radeon_pm_resume(struct radeon_device *rdev) +static void radeon_pm_suspend_dpm(struct radeon_device *rdev) +{ + mutex_lock(&rdev->pm.mutex); + /* disable dpm */ + radeon_dpm_disable(rdev); + /* reset the power state */ + rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps; + rdev->pm.dpm_enabled = false; + mutex_unlock(&rdev->pm.mutex); +} + +void radeon_pm_suspend(struct radeon_device *rdev) +{ + if (rdev->pm.pm_method == PM_METHOD_DPM) + radeon_pm_suspend_dpm(rdev); + else + radeon_pm_suspend_old(rdev); +} + +static void radeon_pm_resume_old(struct radeon_device *rdev) { /* set up the default clocks if the MC ucode is loaded */ if ((rdev->family >= CHIP_BARTS) && @@ -555,12 +841,50 @@ void radeon_pm_resume(struct radeon_device *rdev) radeon_pm_compute_clocks(rdev); }
-int radeon_pm_init(struct radeon_device *rdev) +static void radeon_pm_resume_dpm(struct radeon_device *rdev) +{ + int ret; + + /* asic init will reset to the boot state */ + mutex_lock(&rdev->pm.mutex); + rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps; + radeon_dpm_setup_asic(rdev); + ret = radeon_dpm_enable(rdev); + mutex_unlock(&rdev->pm.mutex); + if (ret) { + DRM_ERROR("radeon: dpm resume failed\n"); + if ((rdev->family >= CHIP_BARTS) && + (rdev->family <= CHIP_CAYMAN) && + rdev->mc_fw) { + if (rdev->pm.default_vddc) + radeon_atom_set_voltage(rdev, rdev->pm.default_vddc, + SET_VOLTAGE_TYPE_ASIC_VDDC); + if (rdev->pm.default_vddci) + radeon_atom_set_voltage(rdev, rdev->pm.default_vddci, + SET_VOLTAGE_TYPE_ASIC_VDDCI); + if (rdev->pm.default_sclk) + radeon_set_engine_clock(rdev, rdev->pm.default_sclk); + if (rdev->pm.default_mclk) + radeon_set_memory_clock(rdev, rdev->pm.default_mclk); + } + } else { + rdev->pm.dpm_enabled = true; + radeon_pm_compute_clocks(rdev); + } +} + +void radeon_pm_resume(struct radeon_device *rdev) +{ + if (rdev->pm.pm_method == PM_METHOD_DPM) + radeon_pm_resume_dpm(rdev); + else + radeon_pm_resume_old(rdev); +} + +static int radeon_pm_init_old(struct radeon_device *rdev) { int ret;
- /* default to profile method */ - rdev->pm.pm_method = PM_METHOD_PROFILE; rdev->pm.profile = PM_PROFILE_DEFAULT; rdev->pm.dynpm_state = DYNPM_STATE_DISABLED; rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE; @@ -622,7 +946,103 @@ int radeon_pm_init(struct radeon_device *rdev) return 0; }
-void radeon_pm_fini(struct radeon_device *rdev) +static void radeon_dpm_print_power_states(struct radeon_device *rdev) +{ + int i; + + for (i = 0; i < rdev->pm.dpm.num_ps; i++) { + printk("== power state %d ==\n", i); + radeon_dpm_print_power_state(rdev, &rdev->pm.dpm.ps[i]); + } +} + +static int radeon_pm_init_dpm(struct radeon_device *rdev) +{ + int ret; + + /* default to performance state */ + rdev->pm.dpm.state = POWER_STATE_TYPE_PERFORMANCE; + rdev->pm.dpm.user_state = POWER_STATE_TYPE_PERFORMANCE; + rdev->pm.default_sclk = rdev->clock.default_sclk; + rdev->pm.default_mclk = rdev->clock.default_mclk; + rdev->pm.current_sclk = rdev->clock.default_sclk; + rdev->pm.current_mclk = rdev->clock.default_mclk; + rdev->pm.int_thermal_type = THERMAL_TYPE_NONE; + + if (rdev->bios && rdev->is_atom_bios) + radeon_atombios_get_power_modes(rdev); + else + return -EINVAL; + + /* set up the internal thermal sensor if applicable */ + ret = radeon_hwmon_init(rdev); + if (ret) + return ret; + + INIT_WORK(&rdev->pm.dpm.thermal.work, radeon_dpm_thermal_work_handler); + mutex_lock(&rdev->pm.mutex); + radeon_dpm_init(rdev); + rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps; + radeon_dpm_print_power_states(rdev); + radeon_dpm_setup_asic(rdev); + ret = radeon_dpm_enable(rdev); + mutex_unlock(&rdev->pm.mutex); + if (ret) { + rdev->pm.dpm_enabled = false; + if ((rdev->family >= CHIP_BARTS) && + (rdev->family <= CHIP_CAYMAN) && + rdev->mc_fw) { + if (rdev->pm.default_vddc) + radeon_atom_set_voltage(rdev, rdev->pm.default_vddc, + SET_VOLTAGE_TYPE_ASIC_VDDC); + if (rdev->pm.default_vddci) + radeon_atom_set_voltage(rdev, rdev->pm.default_vddci, + SET_VOLTAGE_TYPE_ASIC_VDDCI); + if (rdev->pm.default_sclk) + radeon_set_engine_clock(rdev, rdev->pm.default_sclk); + if (rdev->pm.default_mclk) + radeon_set_memory_clock(rdev, rdev->pm.default_mclk); + } + DRM_ERROR("radeon: dpm initialization failed\n"); + return ret; + } + rdev->pm.dpm_enabled = true; + radeon_pm_compute_clocks(rdev); + + if (rdev->pm.num_power_states > 1) { + ret = device_create_file(rdev->dev, &dev_attr_power_dpm_state); + if (ret) + DRM_ERROR("failed to create device file for dpm state\n"); + /* XXX: these are noops for dpm but are here for backwards compat */ + ret = device_create_file(rdev->dev, &dev_attr_power_profile); + if (ret) + DRM_ERROR("failed to create device file for power profile\n"); + ret = device_create_file(rdev->dev, &dev_attr_power_method); + if (ret) + DRM_ERROR("failed to create device file for power method\n"); + DRM_INFO("radeon: dpm initialized\n"); + } + + return 0; +} + +int radeon_pm_init(struct radeon_device *rdev) +{ + /* enable dpm on rv6xx+ */ + switch (rdev->family) { + default: + /* default to profile method */ + rdev->pm.pm_method = PM_METHOD_PROFILE; + break; + } + + if (rdev->pm.pm_method == PM_METHOD_DPM) + return radeon_pm_init_dpm(rdev); + else + return radeon_pm_init_old(rdev); +} + +static void radeon_pm_fini_old(struct radeon_device *rdev) { if (rdev->pm.num_power_states > 1) { mutex_lock(&rdev->pm.mutex); @@ -650,7 +1070,35 @@ void radeon_pm_fini(struct radeon_device *rdev) radeon_hwmon_fini(rdev); }
-void radeon_pm_compute_clocks(struct radeon_device *rdev) +static void radeon_pm_fini_dpm(struct radeon_device *rdev) +{ + if (rdev->pm.num_power_states > 1) { + mutex_lock(&rdev->pm.mutex); + radeon_dpm_disable(rdev); + mutex_unlock(&rdev->pm.mutex); + + device_remove_file(rdev->dev, &dev_attr_power_dpm_state); + /* XXX backwards compat */ + device_remove_file(rdev->dev, &dev_attr_power_profile); + device_remove_file(rdev->dev, &dev_attr_power_method); + } + radeon_dpm_fini(rdev); + + if (rdev->pm.power_state) + kfree(rdev->pm.power_state); + + radeon_hwmon_fini(rdev); +} + +void radeon_pm_fini(struct radeon_device *rdev) +{ + if (rdev->pm.pm_method == PM_METHOD_DPM) + radeon_pm_fini_dpm(rdev); + else + radeon_pm_fini_old(rdev); +} + +static void radeon_pm_compute_clocks_old(struct radeon_device *rdev) { struct drm_device *ddev = rdev->ddev; struct drm_crtc *crtc; @@ -721,6 +1169,38 @@ void radeon_pm_compute_clocks(struct radeon_device *rdev) mutex_unlock(&rdev->pm.mutex); }
+static void radeon_pm_compute_clocks_dpm(struct radeon_device *rdev) +{ + struct drm_device *ddev = rdev->ddev; + struct drm_crtc *crtc; + struct radeon_crtc *radeon_crtc; + + mutex_lock(&rdev->pm.mutex); + + rdev->pm.dpm.new_active_crtcs = 0; + rdev->pm.dpm.new_active_crtc_count = 0; + list_for_each_entry(crtc, + &ddev->mode_config.crtc_list, head) { + radeon_crtc = to_radeon_crtc(crtc); + if (crtc->enabled) { + rdev->pm.dpm.new_active_crtcs |= (1 << radeon_crtc->crtc_id); + rdev->pm.dpm.new_active_crtc_count++; + } + } + + radeon_dpm_change_power_state_locked(rdev); + + mutex_unlock(&rdev->pm.mutex); +} + +void radeon_pm_compute_clocks(struct radeon_device *rdev) +{ + if (rdev->pm.pm_method == PM_METHOD_DPM) + radeon_pm_compute_clocks_dpm(rdev); + else + radeon_pm_compute_clocks_old(rdev); +} + static bool radeon_pm_in_vbl(struct radeon_device *rdev) { int crtc, vpos, hpos, vbl_status;
On Wed, Jun 26, 2013 at 09:22:29AM -0400, alexdeucher@gmail.com wrote:
From: Alex Deucher alexander.deucher@amd.com
This adds the common dpm (dynamic power management) infrastructure:
- dpm callbacks
- dpm init/fini/suspend/resume
- dpm power state selection
No device specific code is enabled yet.
Signed-off-by: Alex Deucher alexander.deucher@amd.com
Reviewed-by: Jerome Glisse jglisse@redhat.com
drivers/gpu/drm/radeon/radeon.h | 100 +++++++- drivers/gpu/drm/radeon/radeon_drv.c | 4 + drivers/gpu/drm/radeon/radeon_pm.c | 496 ++++++++++++++++++++++++++++++++++- 3 files changed, 591 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 6c445f5..c43673c 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -96,6 +96,7 @@ extern int radeon_pcie_gen2; extern int radeon_msi; extern int radeon_lockup_timeout; extern int radeon_fastfb; +extern int radeon_dpm;
/*
- Copy from radeon_drv.h so we don't have to include both and have conflicting
@@ -1048,6 +1049,7 @@ struct radeon_wb { enum radeon_pm_method { PM_METHOD_PROFILE, PM_METHOD_DYNPM,
- PM_METHOD_DPM,
};
enum radeon_dynpm_state { @@ -1073,11 +1075,23 @@ enum radeon_voltage_type { };
enum radeon_pm_state_type {
- /* not used for dpm */ POWER_STATE_TYPE_DEFAULT, POWER_STATE_TYPE_POWERSAVE,
- /* user selectable states */ POWER_STATE_TYPE_BATTERY, POWER_STATE_TYPE_BALANCED, POWER_STATE_TYPE_PERFORMANCE,
- /* internal states */
- POWER_STATE_TYPE_INTERNAL_UVD,
- POWER_STATE_TYPE_INTERNAL_UVD_SD,
- POWER_STATE_TYPE_INTERNAL_UVD_HD,
- POWER_STATE_TYPE_INTERNAL_UVD_HD2,
- POWER_STATE_TYPE_INTERNAL_UVD_MVC,
- POWER_STATE_TYPE_INTERNAL_BOOT,
- POWER_STATE_TYPE_INTERNAL_THERMAL,
- POWER_STATE_TYPE_INTERNAL_ACPI,
- POWER_STATE_TYPE_INTERNAL_ULV,
};
enum radeon_pm_profile_type { @@ -1106,12 +1120,16 @@ struct radeon_pm_profile {
enum radeon_int_thermal_type { THERMAL_TYPE_NONE,
- THERMAL_TYPE_EXTERNAL,
- THERMAL_TYPE_EXTERNAL_GPIO, THERMAL_TYPE_RV6XX, THERMAL_TYPE_RV770,
- THERMAL_TYPE_ADT7473_WITH_INTERNAL, THERMAL_TYPE_EVERGREEN, THERMAL_TYPE_SUMO, THERMAL_TYPE_NI, THERMAL_TYPE_SI,
- THERMAL_TYPE_EMC2103_WITH_INTERNAL, THERMAL_TYPE_CI,
};
@@ -1166,6 +1184,60 @@ struct radeon_power_state { */ #define RADEON_MODE_OVERCLOCK_MARGIN 500 /* 5 MHz */
+struct radeon_ps {
- u32 caps; /* vbios flags */
- u32 class; /* vbios flags */
- u32 class2; /* vbios flags */
- /* UVD clocks */
- u32 vclk;
- u32 dclk;
- /* asic priv */
- void *ps_priv;
+};
+struct radeon_dpm_thermal {
- /* thermal interrupt work */
- struct work_struct work;
- /* low temperature threshold */
- int min_temp;
- /* high temperature threshold */
- int max_temp;
- /* was interrupt low to high or high to low */
- bool high_to_low;
+};
+struct radeon_dpm {
- struct radeon_ps *ps;
- /* number of valid power states */
- int num_ps;
- /* current power state that is active */
- struct radeon_ps *current_ps;
- /* requested power state */
- struct radeon_ps *requested_ps;
- /* boot up power state */
- struct radeon_ps *boot_ps;
- /* default uvd power state */
- struct radeon_ps *uvd_ps;
- enum radeon_pm_state_type state;
- enum radeon_pm_state_type user_state;
- u32 platform_caps;
- u32 voltage_response_time;
- u32 backbias_response_time;
- void *priv;
Just nitpick all of the above have broken indentation space instead of tab.
- u32 new_active_crtcs;
- int new_active_crtc_count;
- u32 current_active_crtcs;
- int current_active_crtc_count;
- /* special states active */
- bool thermal_active;
- /* thermal handling */
- struct radeon_dpm_thermal thermal;
+};
+void radeon_dpm_enable_power_state(struct radeon_device *rdev,
enum radeon_pm_state_type dpm_state);
struct radeon_pm { struct mutex mutex; /* write locked while reprogramming mclk */ @@ -1219,6 +1291,9 @@ struct radeon_pm { /* internal thermal controller on rv6xx+ */ enum radeon_int_thermal_type int_thermal_type; struct device *int_hwmon_dev;
- /* dpm */
- bool dpm_enabled;
- struct radeon_dpm dpm;
};
int radeon_pm_get_type_index(struct radeon_device *rdev, @@ -1416,7 +1491,7 @@ struct radeon_asic { bool (*sense)(struct radeon_device *rdev, enum radeon_hpd_id hpd); void (*set_polarity)(struct radeon_device *rdev, enum radeon_hpd_id hpd); } hpd;
- /* power management */
- /* static power management */ struct { void (*misc)(struct radeon_device *rdev); void (*prepare)(struct radeon_device *rdev);
@@ -1433,6 +1508,19 @@ struct radeon_asic { int (*set_uvd_clocks)(struct radeon_device *rdev, u32 vclk, u32 dclk); int (*get_temperature)(struct radeon_device *rdev); } pm;
- /* dynamic power management */
- struct {
int (*init)(struct radeon_device *rdev);
void (*setup_asic)(struct radeon_device *rdev);
int (*enable)(struct radeon_device *rdev);
void (*disable)(struct radeon_device *rdev);
int (*set_power_state)(struct radeon_device *rdev);
void (*display_configuration_changed)(struct radeon_device *rdev);
void (*fini)(struct radeon_device *rdev);
u32 (*get_sclk)(struct radeon_device *rdev, bool low);
u32 (*get_mclk)(struct radeon_device *rdev, bool low);
void (*print_power_state)(struct radeon_device *rdev, struct radeon_ps *ps);
- } dpm; /* pageflipping */ struct { void (*pre_page_flip)(struct radeon_device *rdev, int crtc);
@@ -2122,6 +2210,16 @@ void radeon_ring_write(struct radeon_ring *ring, uint32_t v); #define radeon_mc_wait_for_idle(rdev) (rdev)->asic->mc_wait_for_idle((rdev)) #define radeon_get_xclk(rdev) (rdev)->asic->get_xclk((rdev)) #define radeon_get_gpu_clock_counter(rdev) (rdev)->asic->get_gpu_clock_counter((rdev)) +#define radeon_dpm_init(rdev) rdev->asic->dpm.init((rdev)) +#define radeon_dpm_setup_asic(rdev) rdev->asic->dpm.setup_asic((rdev)) +#define radeon_dpm_enable(rdev) rdev->asic->dpm.enable((rdev)) +#define radeon_dpm_disable(rdev) rdev->asic->dpm.disable((rdev)) +#define radeon_dpm_set_power_state(rdev) rdev->asic->dpm.set_power_state((rdev)) +#define radeon_dpm_display_configuration_changed(rdev) rdev->asic->dpm.display_configuration_changed((rdev)) +#define radeon_dpm_fini(rdev) rdev->asic->dpm.fini((rdev)) +#define radeon_dpm_get_sclk(rdev, l) rdev->asic->dpm.get_sclk((rdev), (l)) +#define radeon_dpm_get_mclk(rdev, l) rdev->asic->dpm.get_mclk((rdev), (l)) +#define radeon_dpm_print_power_state(rdev, ps) rdev->asic->dpm.print_power_state((rdev), (ps))
/* Common functions */ /* AGP */ diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 02709e4..00cc52e 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -165,6 +165,7 @@ int radeon_pcie_gen2 = -1; int radeon_msi = -1; int radeon_lockup_timeout = 10000; int radeon_fastfb = 0; +int radeon_dpm = -1;
MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch registers"); module_param_named(no_wb, radeon_no_wb, int, 0444); @@ -220,6 +221,9 @@ module_param_named(lockup_timeout, radeon_lockup_timeout, int, 0444); MODULE_PARM_DESC(fastfb, "Direct FB access for IGP chips (0 = disable, 1 = enable)"); module_param_named(fastfb, radeon_fastfb, int, 0444);
+MODULE_PARM_DESC(dpm, "DPM support (1 = enable, 0 = disable, -1 = auto)"); +module_param_named(dpm, radeon_dpm, int, 0444);
static struct pci_device_id pciidlist[] = { radeon_PCI_IDS }; diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index e8c1bea..4f5422e 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -388,7 +388,8 @@ static ssize_t radeon_get_pm_method(struct device *dev, int pm = rdev->pm.pm_method;
return snprintf(buf, PAGE_SIZE, "%s\n",
(pm == PM_METHOD_DYNPM) ? "dynpm" : "profile");
(pm == PM_METHOD_DYNPM) ? "dynpm" :
(pm == PM_METHOD_PROFILE) ? "profile" : "dpm");
}
static ssize_t radeon_set_pm_method(struct device *dev, @@ -399,6 +400,11 @@ static ssize_t radeon_set_pm_method(struct device *dev, struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev)); struct radeon_device *rdev = ddev->dev_private;
/* we don't support the legacy modes with dpm */
if (rdev->pm.pm_method == PM_METHOD_DPM) {
count = -EINVAL;
goto fail;
}
if (strncmp("dynpm", buf, strlen("dynpm")) == 0) { mutex_lock(&rdev->pm.mutex);
@@ -423,8 +429,48 @@ fail: return count; }
+static ssize_t radeon_get_dpm_state(struct device *dev,
struct device_attribute *attr,
char *buf)
+{
- struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev));
- struct radeon_device *rdev = ddev->dev_private;
- enum radeon_pm_state_type pm = rdev->pm.dpm.user_state;
- return snprintf(buf, PAGE_SIZE, "%s\n",
(pm == POWER_STATE_TYPE_BATTERY) ? "battery" :
(pm == POWER_STATE_TYPE_BALANCED) ? "balanced" : "performance");
+}
+static ssize_t radeon_set_dpm_state(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
+{
- struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev));
- struct radeon_device *rdev = ddev->dev_private;
- mutex_lock(&rdev->pm.mutex);
- if (strncmp("battery", buf, strlen("battery")) == 0)
rdev->pm.dpm.user_state = POWER_STATE_TYPE_BATTERY;
- else if (strncmp("balanced", buf, strlen("balanced")) == 0)
rdev->pm.dpm.user_state = POWER_STATE_TYPE_BALANCED;
- else if (strncmp("performance", buf, strlen("performance")) == 0)
rdev->pm.dpm.user_state = POWER_STATE_TYPE_PERFORMANCE;
- else {
mutex_unlock(&rdev->pm.mutex);
count = -EINVAL;
goto fail;
- }
- mutex_unlock(&rdev->pm.mutex);
- radeon_pm_compute_clocks(rdev);
+fail:
- return count;
+}
static DEVICE_ATTR(power_profile, S_IRUGO | S_IWUSR, radeon_get_pm_profile, radeon_set_pm_profile); static DEVICE_ATTR(power_method, S_IRUGO | S_IWUSR, radeon_get_pm_method, radeon_set_pm_method); +static DEVICE_ATTR(power_dpm_state, S_IRUGO | S_IWUSR, radeon_get_dpm_state, radeon_set_dpm_state);
static ssize_t radeon_hwmon_show_temp(struct device *dev, struct device_attribute *attr, @@ -508,7 +554,228 @@ static void radeon_hwmon_fini(struct radeon_device *rdev) } }
-void radeon_pm_suspend(struct radeon_device *rdev) +static void radeon_dpm_thermal_work_handler(struct work_struct *work) +{
- struct radeon_device *rdev =
container_of(work, struct radeon_device,
pm.dpm.thermal.work);
- /* switch to the thermal state */
- enum radeon_pm_state_type dpm_state = POWER_STATE_TYPE_INTERNAL_THERMAL;
- if (!rdev->pm.dpm_enabled)
return;
- if (rdev->asic->pm.get_temperature) {
int temp = radeon_get_temperature(rdev);
if (temp < rdev->pm.dpm.thermal.min_temp)
/* switch back the user state */
dpm_state = rdev->pm.dpm.user_state;
- } else {
if (rdev->pm.dpm.thermal.high_to_low)
/* switch back the user state */
dpm_state = rdev->pm.dpm.user_state;
- }
- radeon_dpm_enable_power_state(rdev, dpm_state);
+}
+static struct radeon_ps *radeon_dpm_pick_power_state(struct radeon_device *rdev,
enum radeon_pm_state_type dpm_state)
+{
- int i;
- struct radeon_ps *ps;
- u32 ui_class;
+restart_search:
- /* balanced states don't exist at the moment */
- if (dpm_state == POWER_STATE_TYPE_BALANCED)
dpm_state = POWER_STATE_TYPE_PERFORMANCE;
- /* Pick the best power state based on current conditions */
- for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
ps = &rdev->pm.dpm.ps[i];
ui_class = ps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK;
switch (dpm_state) {
/* user states */
case POWER_STATE_TYPE_BATTERY:
if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_BATTERY) {
if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) {
if (rdev->pm.dpm.new_active_crtc_count < 2)
return ps;
} else
return ps;
}
break;
case POWER_STATE_TYPE_BALANCED:
if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_BALANCED) {
if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) {
if (rdev->pm.dpm.new_active_crtc_count < 2)
return ps;
} else
return ps;
}
break;
case POWER_STATE_TYPE_PERFORMANCE:
if (ui_class == ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE) {
if (ps->caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) {
if (rdev->pm.dpm.new_active_crtc_count < 2)
return ps;
} else
return ps;
}
break;
/* internal states */
case POWER_STATE_TYPE_INTERNAL_UVD:
return rdev->pm.dpm.uvd_ps;
case POWER_STATE_TYPE_INTERNAL_UVD_SD:
if (ps->class & ATOM_PPLIB_CLASSIFICATION_SDSTATE)
return ps;
break;
case POWER_STATE_TYPE_INTERNAL_UVD_HD:
if (ps->class & ATOM_PPLIB_CLASSIFICATION_HDSTATE)
return ps;
break;
case POWER_STATE_TYPE_INTERNAL_UVD_HD2:
if (ps->class & ATOM_PPLIB_CLASSIFICATION_HD2STATE)
return ps;
break;
case POWER_STATE_TYPE_INTERNAL_UVD_MVC:
if (ps->class2 & ATOM_PPLIB_CLASSIFICATION2_MVC)
return ps;
break;
case POWER_STATE_TYPE_INTERNAL_BOOT:
return rdev->pm.dpm.boot_ps;
case POWER_STATE_TYPE_INTERNAL_THERMAL:
if (ps->class & ATOM_PPLIB_CLASSIFICATION_THERMAL)
return ps;
break;
case POWER_STATE_TYPE_INTERNAL_ACPI:
if (ps->class & ATOM_PPLIB_CLASSIFICATION_ACPI)
return ps;
break;
case POWER_STATE_TYPE_INTERNAL_ULV:
if (ps->class2 & ATOM_PPLIB_CLASSIFICATION2_ULV)
return ps;
break;
default:
break;
}
- }
- /* use a fallback state if we didn't match */
- switch (dpm_state) {
- case POWER_STATE_TYPE_INTERNAL_UVD_SD:
- case POWER_STATE_TYPE_INTERNAL_UVD_HD:
- case POWER_STATE_TYPE_INTERNAL_UVD_HD2:
- case POWER_STATE_TYPE_INTERNAL_UVD_MVC:
return rdev->pm.dpm.uvd_ps;
- case POWER_STATE_TYPE_INTERNAL_THERMAL:
dpm_state = POWER_STATE_TYPE_INTERNAL_ACPI;
goto restart_search;
- case POWER_STATE_TYPE_INTERNAL_ACPI:
dpm_state = POWER_STATE_TYPE_BATTERY;
goto restart_search;
- case POWER_STATE_TYPE_BATTERY:
dpm_state = POWER_STATE_TYPE_PERFORMANCE;
goto restart_search;
- default:
break;
- }
- return NULL;
+}
+static void radeon_dpm_change_power_state_locked(struct radeon_device *rdev) +{
- int i;
- struct radeon_ps *ps;
- enum radeon_pm_state_type dpm_state;
- /* if dpm init failed */
- if (!rdev->pm.dpm_enabled)
return;
- if (rdev->pm.dpm.user_state != rdev->pm.dpm.state) {
/* add other state override checks here */
if (!rdev->pm.dpm.thermal_active)
rdev->pm.dpm.state = rdev->pm.dpm.user_state;
- }
- dpm_state = rdev->pm.dpm.state;
- ps = radeon_dpm_pick_power_state(rdev, dpm_state);
- if (ps)
rdev->pm.dpm.requested_ps = ps;
- else
return;
- /* no need to reprogram if nothing changed */
- if (rdev->pm.dpm.current_ps == rdev->pm.dpm.requested_ps) {
/* update display watermarks based on new power state */
if (rdev->pm.dpm.new_active_crtcs != rdev->pm.dpm.current_active_crtcs) {
radeon_bandwidth_update(rdev);
/* update displays */
radeon_dpm_display_configuration_changed(rdev);
rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs;
rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count;
}
return;
- }
- printk("switching from power state:\n");
- radeon_dpm_print_power_state(rdev, rdev->pm.dpm.current_ps);
- printk("switching to power state:\n");
- radeon_dpm_print_power_state(rdev, rdev->pm.dpm.requested_ps);
- mutex_lock(&rdev->ddev->struct_mutex);
- down_write(&rdev->pm.mclk_lock);
- mutex_lock(&rdev->ring_lock);
- /* update display watermarks based on new power state */
- radeon_bandwidth_update(rdev);
- /* update displays */
- radeon_dpm_display_configuration_changed(rdev);
- rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs;
- rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count;
- /* wait for the rings to drain */
- for (i = 0; i < RADEON_NUM_RINGS; i++) {
struct radeon_ring *ring = &rdev->ring[i];
if (ring->ready)
radeon_fence_wait_empty_locked(rdev, i);
- }
- /* program the new power state */
- radeon_dpm_set_power_state(rdev);
- /* update current power state */
- rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps;
- mutex_unlock(&rdev->ring_lock);
- up_write(&rdev->pm.mclk_lock);
- mutex_unlock(&rdev->ddev->struct_mutex);
+}
+void radeon_dpm_enable_power_state(struct radeon_device *rdev,
enum radeon_pm_state_type dpm_state)
+{
- if (!rdev->pm.dpm_enabled)
return;
- mutex_lock(&rdev->pm.mutex);
- switch (dpm_state) {
- case POWER_STATE_TYPE_INTERNAL_THERMAL:
rdev->pm.dpm.thermal_active = true;
break;
- default:
rdev->pm.dpm.thermal_active = false;
break;
- }
- rdev->pm.dpm.state = dpm_state;
- mutex_unlock(&rdev->pm.mutex);
- radeon_pm_compute_clocks(rdev);
+}
+static void radeon_pm_suspend_old(struct radeon_device *rdev) { mutex_lock(&rdev->pm.mutex); if (rdev->pm.pm_method == PM_METHOD_DYNPM) { @@ -520,7 +787,26 @@ void radeon_pm_suspend(struct radeon_device *rdev) cancel_delayed_work_sync(&rdev->pm.dynpm_idle_work); }
-void radeon_pm_resume(struct radeon_device *rdev) +static void radeon_pm_suspend_dpm(struct radeon_device *rdev) +{
- mutex_lock(&rdev->pm.mutex);
- /* disable dpm */
- radeon_dpm_disable(rdev);
- /* reset the power state */
- rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps;
- rdev->pm.dpm_enabled = false;
- mutex_unlock(&rdev->pm.mutex);
+}
+void radeon_pm_suspend(struct radeon_device *rdev) +{
- if (rdev->pm.pm_method == PM_METHOD_DPM)
radeon_pm_suspend_dpm(rdev);
- else
radeon_pm_suspend_old(rdev);
+}
+static void radeon_pm_resume_old(struct radeon_device *rdev) { /* set up the default clocks if the MC ucode is loaded */ if ((rdev->family >= CHIP_BARTS) && @@ -555,12 +841,50 @@ void radeon_pm_resume(struct radeon_device *rdev) radeon_pm_compute_clocks(rdev); }
-int radeon_pm_init(struct radeon_device *rdev) +static void radeon_pm_resume_dpm(struct radeon_device *rdev) +{
- int ret;
- /* asic init will reset to the boot state */
- mutex_lock(&rdev->pm.mutex);
- rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps;
- radeon_dpm_setup_asic(rdev);
- ret = radeon_dpm_enable(rdev);
- mutex_unlock(&rdev->pm.mutex);
- if (ret) {
DRM_ERROR("radeon: dpm resume failed\n");
if ((rdev->family >= CHIP_BARTS) &&
(rdev->family <= CHIP_CAYMAN) &&
rdev->mc_fw) {
if (rdev->pm.default_vddc)
radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
SET_VOLTAGE_TYPE_ASIC_VDDC);
if (rdev->pm.default_vddci)
radeon_atom_set_voltage(rdev, rdev->pm.default_vddci,
SET_VOLTAGE_TYPE_ASIC_VDDCI);
if (rdev->pm.default_sclk)
radeon_set_engine_clock(rdev, rdev->pm.default_sclk);
if (rdev->pm.default_mclk)
radeon_set_memory_clock(rdev, rdev->pm.default_mclk);
}
- } else {
rdev->pm.dpm_enabled = true;
radeon_pm_compute_clocks(rdev);
- }
+}
+void radeon_pm_resume(struct radeon_device *rdev) +{
- if (rdev->pm.pm_method == PM_METHOD_DPM)
radeon_pm_resume_dpm(rdev);
- else
radeon_pm_resume_old(rdev);
+}
+static int radeon_pm_init_old(struct radeon_device *rdev) { int ret;
- /* default to profile method */
- rdev->pm.pm_method = PM_METHOD_PROFILE; rdev->pm.profile = PM_PROFILE_DEFAULT; rdev->pm.dynpm_state = DYNPM_STATE_DISABLED; rdev->pm.dynpm_planned_action = DYNPM_ACTION_NONE;
@@ -622,7 +946,103 @@ int radeon_pm_init(struct radeon_device *rdev) return 0; }
-void radeon_pm_fini(struct radeon_device *rdev) +static void radeon_dpm_print_power_states(struct radeon_device *rdev) +{
- int i;
- for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
printk("== power state %d ==\n", i);
radeon_dpm_print_power_state(rdev, &rdev->pm.dpm.ps[i]);
- }
+}
+static int radeon_pm_init_dpm(struct radeon_device *rdev) +{
- int ret;
- /* default to performance state */
- rdev->pm.dpm.state = POWER_STATE_TYPE_PERFORMANCE;
- rdev->pm.dpm.user_state = POWER_STATE_TYPE_PERFORMANCE;
- rdev->pm.default_sclk = rdev->clock.default_sclk;
- rdev->pm.default_mclk = rdev->clock.default_mclk;
- rdev->pm.current_sclk = rdev->clock.default_sclk;
- rdev->pm.current_mclk = rdev->clock.default_mclk;
- rdev->pm.int_thermal_type = THERMAL_TYPE_NONE;
- if (rdev->bios && rdev->is_atom_bios)
radeon_atombios_get_power_modes(rdev);
- else
return -EINVAL;
- /* set up the internal thermal sensor if applicable */
- ret = radeon_hwmon_init(rdev);
- if (ret)
return ret;
- INIT_WORK(&rdev->pm.dpm.thermal.work, radeon_dpm_thermal_work_handler);
- mutex_lock(&rdev->pm.mutex);
- radeon_dpm_init(rdev);
- rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps;
- radeon_dpm_print_power_states(rdev);
- radeon_dpm_setup_asic(rdev);
- ret = radeon_dpm_enable(rdev);
- mutex_unlock(&rdev->pm.mutex);
- if (ret) {
rdev->pm.dpm_enabled = false;
if ((rdev->family >= CHIP_BARTS) &&
(rdev->family <= CHIP_CAYMAN) &&
rdev->mc_fw) {
if (rdev->pm.default_vddc)
radeon_atom_set_voltage(rdev, rdev->pm.default_vddc,
SET_VOLTAGE_TYPE_ASIC_VDDC);
if (rdev->pm.default_vddci)
radeon_atom_set_voltage(rdev, rdev->pm.default_vddci,
SET_VOLTAGE_TYPE_ASIC_VDDCI);
if (rdev->pm.default_sclk)
radeon_set_engine_clock(rdev, rdev->pm.default_sclk);
if (rdev->pm.default_mclk)
radeon_set_memory_clock(rdev, rdev->pm.default_mclk);
}
DRM_ERROR("radeon: dpm initialization failed\n");
return ret;
- }
- rdev->pm.dpm_enabled = true;
- radeon_pm_compute_clocks(rdev);
- if (rdev->pm.num_power_states > 1) {
ret = device_create_file(rdev->dev, &dev_attr_power_dpm_state);
if (ret)
DRM_ERROR("failed to create device file for dpm state\n");
/* XXX: these are noops for dpm but are here for backwards compat */
ret = device_create_file(rdev->dev, &dev_attr_power_profile);
if (ret)
DRM_ERROR("failed to create device file for power profile\n");
ret = device_create_file(rdev->dev, &dev_attr_power_method);
if (ret)
DRM_ERROR("failed to create device file for power method\n");
DRM_INFO("radeon: dpm initialized\n");
- }
- return 0;
+}
+int radeon_pm_init(struct radeon_device *rdev) +{
- /* enable dpm on rv6xx+ */
- switch (rdev->family) {
- default:
/* default to profile method */
rdev->pm.pm_method = PM_METHOD_PROFILE;
break;
- }
- if (rdev->pm.pm_method == PM_METHOD_DPM)
return radeon_pm_init_dpm(rdev);
- else
return radeon_pm_init_old(rdev);
+}
+static void radeon_pm_fini_old(struct radeon_device *rdev) { if (rdev->pm.num_power_states > 1) { mutex_lock(&rdev->pm.mutex); @@ -650,7 +1070,35 @@ void radeon_pm_fini(struct radeon_device *rdev) radeon_hwmon_fini(rdev); }
-void radeon_pm_compute_clocks(struct radeon_device *rdev) +static void radeon_pm_fini_dpm(struct radeon_device *rdev) +{
- if (rdev->pm.num_power_states > 1) {
mutex_lock(&rdev->pm.mutex);
radeon_dpm_disable(rdev);
mutex_unlock(&rdev->pm.mutex);
device_remove_file(rdev->dev, &dev_attr_power_dpm_state);
/* XXX backwards compat */
device_remove_file(rdev->dev, &dev_attr_power_profile);
device_remove_file(rdev->dev, &dev_attr_power_method);
- }
- radeon_dpm_fini(rdev);
- if (rdev->pm.power_state)
kfree(rdev->pm.power_state);
- radeon_hwmon_fini(rdev);
+}
+void radeon_pm_fini(struct radeon_device *rdev) +{
- if (rdev->pm.pm_method == PM_METHOD_DPM)
radeon_pm_fini_dpm(rdev);
- else
radeon_pm_fini_old(rdev);
+}
+static void radeon_pm_compute_clocks_old(struct radeon_device *rdev) { struct drm_device *ddev = rdev->ddev; struct drm_crtc *crtc; @@ -721,6 +1169,38 @@ void radeon_pm_compute_clocks(struct radeon_device *rdev) mutex_unlock(&rdev->pm.mutex); }
+static void radeon_pm_compute_clocks_dpm(struct radeon_device *rdev) +{
- struct drm_device *ddev = rdev->ddev;
- struct drm_crtc *crtc;
- struct radeon_crtc *radeon_crtc;
- mutex_lock(&rdev->pm.mutex);
- rdev->pm.dpm.new_active_crtcs = 0;
- rdev->pm.dpm.new_active_crtc_count = 0;
- list_for_each_entry(crtc,
&ddev->mode_config.crtc_list, head) {
radeon_crtc = to_radeon_crtc(crtc);
if (crtc->enabled) {
rdev->pm.dpm.new_active_crtcs |= (1 << radeon_crtc->crtc_id);
rdev->pm.dpm.new_active_crtc_count++;
}
- }
- radeon_dpm_change_power_state_locked(rdev);
- mutex_unlock(&rdev->pm.mutex);
+}
+void radeon_pm_compute_clocks(struct radeon_device *rdev) +{
- if (rdev->pm.pm_method == PM_METHOD_DPM)
radeon_pm_compute_clocks_dpm(rdev);
- else
radeon_pm_compute_clocks_old(rdev);
+}
static bool radeon_pm_in_vbl(struct radeon_device *rdev) { int crtc, vpos, hpos, vbl_status; -- 1.7.7.5
dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
Thanks for the work the whole thing seems to work fine on my RV770, although I cannot really say if does anything as I found no way to query the current clk or voltage?! There is one little gripe though, the rest of the code uses DRM_INFO() for printing whereas this series uses plain printk() resulting in some weird looking dmesg output:
Jun 27 14:41:54 my-little-devil kernel: [drm] VGA-1 Jun 27 14:41:54 my-little-devil kernel: [drm] DDC: 0x7e60 0x7e60 0x7e64 0x7e64 0x7e68 0x7e68 0x7e6c 0x7e6c Jun 27 14:41:54 my-little-devil kernel: [drm] Encoders: Jun 27 14:41:54 my-little-devil kernel: [drm] CRT2: INTERNAL_KLDSCP_DAC2 Jun 27 14:41:54 my-little-devil kernel: [drm] Internal thermal controller with fan control Jun 27 14:41:54 my-little-devil kernel: == power state 0 == Jun 27 14:41:54 my-little-devil kernel: ui class: none Jun 27 14:41:54 my-little-devil kernel: internal class: boot Jun 27 14:41:54 my-little-devil kernel: caps: video Jun 27 14:41:54 my-little-devil kernel: uvd vclk: 0 dclk: 0 Jun 27 14:41:54 my-little-devil kernel: power level 0 sclk: 62500 mclk: 99300 vddc: 1123 Jun 27 14:41:54 my-little-devil kernel: power level 1 sclk: 62500 mclk: 99300 vddc: 1123 Jun 27 14:41:54 my-little-devil kernel: power level 2 sclk: 62500 mclk: 99300 vddc: 1123
best regards K. Schnass
From: Alex Deucher alexander.deucher@amd.com
calculate the low and high watermarks based on the low and high clocks for the current power state. The dynamic pm hw will select the appropriate watermark based on the internal dpm state.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/rs690.c | 291 +++++++++++++++++++++++----------------- 1 files changed, 167 insertions(+), 124 deletions(-)
diff --git a/drivers/gpu/drm/radeon/rs690.c b/drivers/gpu/drm/radeon/rs690.c index 55880d5..d8ddfb3 100644 --- a/drivers/gpu/drm/radeon/rs690.c +++ b/drivers/gpu/drm/radeon/rs690.c @@ -248,13 +248,16 @@ struct rs690_watermark { };
static void rs690_crtc_bandwidth_compute(struct radeon_device *rdev, - struct radeon_crtc *crtc, - struct rs690_watermark *wm) + struct radeon_crtc *crtc, + struct rs690_watermark *wm, + bool low) { struct drm_display_mode *mode = &crtc->base.mode; fixed20_12 a, b, c; fixed20_12 pclk, request_fifo_depth, tolerable_latency, estimated_width; fixed20_12 consumption_time, line_time, chunk_time, read_delay_latency; + fixed20_12 sclk, core_bandwidth, max_bandwidth; + u32 selected_sclk;
if (!crtc->base.enabled) { /* FIXME: wouldn't it better to set priority mark to maximum */ @@ -262,6 +265,21 @@ static void rs690_crtc_bandwidth_compute(struct radeon_device *rdev, return; }
+ if (((rdev->family == CHIP_RS780) || (rdev->family == CHIP_RS880)) && + (rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) + selected_sclk = radeon_dpm_get_sclk(rdev, low); + else + selected_sclk = rdev->pm.current_sclk; + + /* sclk in Mhz */ + a.full = dfixed_const(100); + sclk.full = dfixed_const(selected_sclk); + sclk.full = dfixed_div(sclk, a); + + /* core_bandwidth = sclk(Mhz) * 16 */ + a.full = dfixed_const(16); + core_bandwidth.full = dfixed_div(rdev->pm.sclk, a); + if (crtc->vsc.full > dfixed_const(2)) wm->num_line_pair.full = dfixed_const(2); else @@ -322,36 +340,36 @@ static void rs690_crtc_bandwidth_compute(struct radeon_device *rdev, wm->active_time.full = dfixed_div(wm->active_time, a);
/* Maximun bandwidth is the minimun bandwidth of all component */ - rdev->pm.max_bandwidth = rdev->pm.core_bandwidth; + max_bandwidth = core_bandwidth; if (rdev->mc.igp_sideport_enabled) { - if (rdev->pm.max_bandwidth.full > rdev->pm.sideport_bandwidth.full && + if (max_bandwidth.full > rdev->pm.sideport_bandwidth.full && rdev->pm.sideport_bandwidth.full) - rdev->pm.max_bandwidth = rdev->pm.sideport_bandwidth; + max_bandwidth = rdev->pm.sideport_bandwidth; read_delay_latency.full = dfixed_const(370 * 800 * 1000); read_delay_latency.full = dfixed_div(read_delay_latency, rdev->pm.igp_sideport_mclk); } else { - if (rdev->pm.max_bandwidth.full > rdev->pm.k8_bandwidth.full && + if (max_bandwidth.full > rdev->pm.k8_bandwidth.full && rdev->pm.k8_bandwidth.full) - rdev->pm.max_bandwidth = rdev->pm.k8_bandwidth; - if (rdev->pm.max_bandwidth.full > rdev->pm.ht_bandwidth.full && + max_bandwidth = rdev->pm.k8_bandwidth; + if (max_bandwidth.full > rdev->pm.ht_bandwidth.full && rdev->pm.ht_bandwidth.full) - rdev->pm.max_bandwidth = rdev->pm.ht_bandwidth; + max_bandwidth = rdev->pm.ht_bandwidth; read_delay_latency.full = dfixed_const(5000); }
/* sclk = system clocks(ns) = 1000 / max_bandwidth / 16 */ a.full = dfixed_const(16); - rdev->pm.sclk.full = dfixed_mul(rdev->pm.max_bandwidth, a); + sclk.full = dfixed_mul(max_bandwidth, a); a.full = dfixed_const(1000); - rdev->pm.sclk.full = dfixed_div(a, rdev->pm.sclk); + sclk.full = dfixed_div(a, sclk); /* Determine chunk time * ChunkTime = the time it takes the DCP to send one chunk of data * to the LB which consists of pipeline delay and inter chunk gap * sclk = system clock(ns) */ a.full = dfixed_const(256 * 13); - chunk_time.full = dfixed_mul(rdev->pm.sclk, a); + chunk_time.full = dfixed_mul(sclk, a); a.full = dfixed_const(10); chunk_time.full = dfixed_div(chunk_time, a);
@@ -415,175 +433,200 @@ static void rs690_crtc_bandwidth_compute(struct radeon_device *rdev, } }
-void rs690_bandwidth_update(struct radeon_device *rdev) +static void rs690_compute_mode_priority(struct radeon_device *rdev, + struct rs690_watermark *wm0, + struct rs690_watermark *wm1, + struct drm_display_mode *mode0, + struct drm_display_mode *mode1, + u32 *d1mode_priority_a_cnt, + u32 *d2mode_priority_a_cnt) { - struct drm_display_mode *mode0 = NULL; - struct drm_display_mode *mode1 = NULL; - struct rs690_watermark wm0; - struct rs690_watermark wm1; - u32 tmp; - u32 d1mode_priority_a_cnt = S_006548_D1MODE_PRIORITY_A_OFF(1); - u32 d2mode_priority_a_cnt = S_006548_D1MODE_PRIORITY_A_OFF(1); fixed20_12 priority_mark02, priority_mark12, fill_rate; fixed20_12 a, b;
- radeon_update_display_priority(rdev); - - if (rdev->mode_info.crtcs[0]->base.enabled) - mode0 = &rdev->mode_info.crtcs[0]->base.mode; - if (rdev->mode_info.crtcs[1]->base.enabled) - mode1 = &rdev->mode_info.crtcs[1]->base.mode; - /* - * Set display0/1 priority up in the memory controller for - * modes if the user specifies HIGH for displaypriority - * option. - */ - if ((rdev->disp_priority == 2) && - ((rdev->family == CHIP_RS690) || (rdev->family == CHIP_RS740))) { - tmp = RREG32_MC(R_000104_MC_INIT_MISC_LAT_TIMER); - tmp &= C_000104_MC_DISP0R_INIT_LAT; - tmp &= C_000104_MC_DISP1R_INIT_LAT; - if (mode0) - tmp |= S_000104_MC_DISP0R_INIT_LAT(1); - if (mode1) - tmp |= S_000104_MC_DISP1R_INIT_LAT(1); - WREG32_MC(R_000104_MC_INIT_MISC_LAT_TIMER, tmp); - } - rs690_line_buffer_adjust(rdev, mode0, mode1); - - if ((rdev->family == CHIP_RS690) || (rdev->family == CHIP_RS740)) - WREG32(R_006C9C_DCP_CONTROL, 0); - if ((rdev->family == CHIP_RS780) || (rdev->family == CHIP_RS880)) - WREG32(R_006C9C_DCP_CONTROL, 2); - - rs690_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[0], &wm0); - rs690_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[1], &wm1); - - tmp = (wm0.lb_request_fifo_depth - 1); - tmp |= (wm1.lb_request_fifo_depth - 1) << 16; - WREG32(R_006D58_LB_MAX_REQ_OUTSTANDING, tmp); + *d1mode_priority_a_cnt = S_006548_D1MODE_PRIORITY_A_OFF(1); + *d2mode_priority_a_cnt = S_006548_D1MODE_PRIORITY_A_OFF(1);
if (mode0 && mode1) { - if (dfixed_trunc(wm0.dbpp) > 64) - a.full = dfixed_mul(wm0.dbpp, wm0.num_line_pair); + if (dfixed_trunc(wm0->dbpp) > 64) + a.full = dfixed_mul(wm0->dbpp, wm0->num_line_pair); else - a.full = wm0.num_line_pair.full; - if (dfixed_trunc(wm1.dbpp) > 64) - b.full = dfixed_mul(wm1.dbpp, wm1.num_line_pair); + a.full = wm0->num_line_pair.full; + if (dfixed_trunc(wm1->dbpp) > 64) + b.full = dfixed_mul(wm1->dbpp, wm1->num_line_pair); else - b.full = wm1.num_line_pair.full; + b.full = wm1->num_line_pair.full; a.full += b.full; - fill_rate.full = dfixed_div(wm0.sclk, a); - if (wm0.consumption_rate.full > fill_rate.full) { - b.full = wm0.consumption_rate.full - fill_rate.full; - b.full = dfixed_mul(b, wm0.active_time); - a.full = dfixed_mul(wm0.worst_case_latency, - wm0.consumption_rate); + fill_rate.full = dfixed_div(wm0->sclk, a); + if (wm0->consumption_rate.full > fill_rate.full) { + b.full = wm0->consumption_rate.full - fill_rate.full; + b.full = dfixed_mul(b, wm0->active_time); + a.full = dfixed_mul(wm0->worst_case_latency, + wm0->consumption_rate); a.full = a.full + b.full; b.full = dfixed_const(16 * 1000); priority_mark02.full = dfixed_div(a, b); } else { - a.full = dfixed_mul(wm0.worst_case_latency, - wm0.consumption_rate); + a.full = dfixed_mul(wm0->worst_case_latency, + wm0->consumption_rate); b.full = dfixed_const(16 * 1000); priority_mark02.full = dfixed_div(a, b); } - if (wm1.consumption_rate.full > fill_rate.full) { - b.full = wm1.consumption_rate.full - fill_rate.full; - b.full = dfixed_mul(b, wm1.active_time); - a.full = dfixed_mul(wm1.worst_case_latency, - wm1.consumption_rate); + if (wm1->consumption_rate.full > fill_rate.full) { + b.full = wm1->consumption_rate.full - fill_rate.full; + b.full = dfixed_mul(b, wm1->active_time); + a.full = dfixed_mul(wm1->worst_case_latency, + wm1->consumption_rate); a.full = a.full + b.full; b.full = dfixed_const(16 * 1000); priority_mark12.full = dfixed_div(a, b); } else { - a.full = dfixed_mul(wm1.worst_case_latency, - wm1.consumption_rate); + a.full = dfixed_mul(wm1->worst_case_latency, + wm1->consumption_rate); b.full = dfixed_const(16 * 1000); priority_mark12.full = dfixed_div(a, b); } - if (wm0.priority_mark.full > priority_mark02.full) - priority_mark02.full = wm0.priority_mark.full; + if (wm0->priority_mark.full > priority_mark02.full) + priority_mark02.full = wm0->priority_mark.full; if (dfixed_trunc(priority_mark02) < 0) priority_mark02.full = 0; - if (wm0.priority_mark_max.full > priority_mark02.full) - priority_mark02.full = wm0.priority_mark_max.full; - if (wm1.priority_mark.full > priority_mark12.full) - priority_mark12.full = wm1.priority_mark.full; + if (wm0->priority_mark_max.full > priority_mark02.full) + priority_mark02.full = wm0->priority_mark_max.full; + if (wm1->priority_mark.full > priority_mark12.full) + priority_mark12.full = wm1->priority_mark.full; if (dfixed_trunc(priority_mark12) < 0) priority_mark12.full = 0; - if (wm1.priority_mark_max.full > priority_mark12.full) - priority_mark12.full = wm1.priority_mark_max.full; - d1mode_priority_a_cnt = dfixed_trunc(priority_mark02); - d2mode_priority_a_cnt = dfixed_trunc(priority_mark12); + if (wm1->priority_mark_max.full > priority_mark12.full) + priority_mark12.full = wm1->priority_mark_max.full; + *d1mode_priority_a_cnt = dfixed_trunc(priority_mark02); + *d2mode_priority_a_cnt = dfixed_trunc(priority_mark12); if (rdev->disp_priority == 2) { - d1mode_priority_a_cnt |= S_006548_D1MODE_PRIORITY_A_ALWAYS_ON(1); - d2mode_priority_a_cnt |= S_006D48_D2MODE_PRIORITY_A_ALWAYS_ON(1); + *d1mode_priority_a_cnt |= S_006548_D1MODE_PRIORITY_A_ALWAYS_ON(1); + *d2mode_priority_a_cnt |= S_006D48_D2MODE_PRIORITY_A_ALWAYS_ON(1); } } else if (mode0) { - if (dfixed_trunc(wm0.dbpp) > 64) - a.full = dfixed_mul(wm0.dbpp, wm0.num_line_pair); + if (dfixed_trunc(wm0->dbpp) > 64) + a.full = dfixed_mul(wm0->dbpp, wm0->num_line_pair); else - a.full = wm0.num_line_pair.full; - fill_rate.full = dfixed_div(wm0.sclk, a); - if (wm0.consumption_rate.full > fill_rate.full) { - b.full = wm0.consumption_rate.full - fill_rate.full; - b.full = dfixed_mul(b, wm0.active_time); - a.full = dfixed_mul(wm0.worst_case_latency, - wm0.consumption_rate); + a.full = wm0->num_line_pair.full; + fill_rate.full = dfixed_div(wm0->sclk, a); + if (wm0->consumption_rate.full > fill_rate.full) { + b.full = wm0->consumption_rate.full - fill_rate.full; + b.full = dfixed_mul(b, wm0->active_time); + a.full = dfixed_mul(wm0->worst_case_latency, + wm0->consumption_rate); a.full = a.full + b.full; b.full = dfixed_const(16 * 1000); priority_mark02.full = dfixed_div(a, b); } else { - a.full = dfixed_mul(wm0.worst_case_latency, - wm0.consumption_rate); + a.full = dfixed_mul(wm0->worst_case_latency, + wm0->consumption_rate); b.full = dfixed_const(16 * 1000); priority_mark02.full = dfixed_div(a, b); } - if (wm0.priority_mark.full > priority_mark02.full) - priority_mark02.full = wm0.priority_mark.full; + if (wm0->priority_mark.full > priority_mark02.full) + priority_mark02.full = wm0->priority_mark.full; if (dfixed_trunc(priority_mark02) < 0) priority_mark02.full = 0; - if (wm0.priority_mark_max.full > priority_mark02.full) - priority_mark02.full = wm0.priority_mark_max.full; - d1mode_priority_a_cnt = dfixed_trunc(priority_mark02); + if (wm0->priority_mark_max.full > priority_mark02.full) + priority_mark02.full = wm0->priority_mark_max.full; + *d1mode_priority_a_cnt = dfixed_trunc(priority_mark02); if (rdev->disp_priority == 2) - d1mode_priority_a_cnt |= S_006548_D1MODE_PRIORITY_A_ALWAYS_ON(1); + *d1mode_priority_a_cnt |= S_006548_D1MODE_PRIORITY_A_ALWAYS_ON(1); } else if (mode1) { - if (dfixed_trunc(wm1.dbpp) > 64) - a.full = dfixed_mul(wm1.dbpp, wm1.num_line_pair); + if (dfixed_trunc(wm1->dbpp) > 64) + a.full = dfixed_mul(wm1->dbpp, wm1->num_line_pair); else - a.full = wm1.num_line_pair.full; - fill_rate.full = dfixed_div(wm1.sclk, a); - if (wm1.consumption_rate.full > fill_rate.full) { - b.full = wm1.consumption_rate.full - fill_rate.full; - b.full = dfixed_mul(b, wm1.active_time); - a.full = dfixed_mul(wm1.worst_case_latency, - wm1.consumption_rate); + a.full = wm1->num_line_pair.full; + fill_rate.full = dfixed_div(wm1->sclk, a); + if (wm1->consumption_rate.full > fill_rate.full) { + b.full = wm1->consumption_rate.full - fill_rate.full; + b.full = dfixed_mul(b, wm1->active_time); + a.full = dfixed_mul(wm1->worst_case_latency, + wm1->consumption_rate); a.full = a.full + b.full; b.full = dfixed_const(16 * 1000); priority_mark12.full = dfixed_div(a, b); } else { - a.full = dfixed_mul(wm1.worst_case_latency, - wm1.consumption_rate); + a.full = dfixed_mul(wm1->worst_case_latency, + wm1->consumption_rate); b.full = dfixed_const(16 * 1000); priority_mark12.full = dfixed_div(a, b); } - if (wm1.priority_mark.full > priority_mark12.full) - priority_mark12.full = wm1.priority_mark.full; + if (wm1->priority_mark.full > priority_mark12.full) + priority_mark12.full = wm1->priority_mark.full; if (dfixed_trunc(priority_mark12) < 0) priority_mark12.full = 0; - if (wm1.priority_mark_max.full > priority_mark12.full) - priority_mark12.full = wm1.priority_mark_max.full; - d2mode_priority_a_cnt = dfixed_trunc(priority_mark12); + if (wm1->priority_mark_max.full > priority_mark12.full) + priority_mark12.full = wm1->priority_mark_max.full; + *d2mode_priority_a_cnt = dfixed_trunc(priority_mark12); if (rdev->disp_priority == 2) - d2mode_priority_a_cnt |= S_006D48_D2MODE_PRIORITY_A_ALWAYS_ON(1); + *d2mode_priority_a_cnt |= S_006D48_D2MODE_PRIORITY_A_ALWAYS_ON(1); } +} + +void rs690_bandwidth_update(struct radeon_device *rdev) +{ + struct drm_display_mode *mode0 = NULL; + struct drm_display_mode *mode1 = NULL; + struct rs690_watermark wm0_high, wm0_low; + struct rs690_watermark wm1_high, wm1_low; + u32 tmp; + u32 d1mode_priority_a_cnt, d1mode_priority_b_cnt; + u32 d2mode_priority_a_cnt, d2mode_priority_b_cnt; + + radeon_update_display_priority(rdev); + + if (rdev->mode_info.crtcs[0]->base.enabled) + mode0 = &rdev->mode_info.crtcs[0]->base.mode; + if (rdev->mode_info.crtcs[1]->base.enabled) + mode1 = &rdev->mode_info.crtcs[1]->base.mode; + /* + * Set display0/1 priority up in the memory controller for + * modes if the user specifies HIGH for displaypriority + * option. + */ + if ((rdev->disp_priority == 2) && + ((rdev->family == CHIP_RS690) || (rdev->family == CHIP_RS740))) { + tmp = RREG32_MC(R_000104_MC_INIT_MISC_LAT_TIMER); + tmp &= C_000104_MC_DISP0R_INIT_LAT; + tmp &= C_000104_MC_DISP1R_INIT_LAT; + if (mode0) + tmp |= S_000104_MC_DISP0R_INIT_LAT(1); + if (mode1) + tmp |= S_000104_MC_DISP1R_INIT_LAT(1); + WREG32_MC(R_000104_MC_INIT_MISC_LAT_TIMER, tmp); + } + rs690_line_buffer_adjust(rdev, mode0, mode1); + + if ((rdev->family == CHIP_RS690) || (rdev->family == CHIP_RS740)) + WREG32(R_006C9C_DCP_CONTROL, 0); + if ((rdev->family == CHIP_RS780) || (rdev->family == CHIP_RS880)) + WREG32(R_006C9C_DCP_CONTROL, 2); + + rs690_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[0], &wm0_high, false); + rs690_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[1], &wm1_high, false); + + rs690_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[0], &wm0_low, true); + rs690_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[1], &wm1_low, true); + + tmp = (wm0_high.lb_request_fifo_depth - 1); + tmp |= (wm1_high.lb_request_fifo_depth - 1) << 16; + WREG32(R_006D58_LB_MAX_REQ_OUTSTANDING, tmp); + + rs690_compute_mode_priority(rdev, + &wm0_high, &wm1_high, + mode0, mode1, + &d1mode_priority_a_cnt, &d2mode_priority_a_cnt); + rs690_compute_mode_priority(rdev, + &wm0_low, &wm1_low, + mode0, mode1, + &d1mode_priority_b_cnt, &d2mode_priority_b_cnt);
WREG32(R_006548_D1MODE_PRIORITY_A_CNT, d1mode_priority_a_cnt); - WREG32(R_00654C_D1MODE_PRIORITY_B_CNT, d1mode_priority_a_cnt); + WREG32(R_00654C_D1MODE_PRIORITY_B_CNT, d1mode_priority_b_cnt); WREG32(R_006D48_D2MODE_PRIORITY_A_CNT, d2mode_priority_a_cnt); - WREG32(R_006D4C_D2MODE_PRIORITY_B_CNT, d2mode_priority_a_cnt); + WREG32(R_006D4C_D2MODE_PRIORITY_B_CNT, d2mode_priority_b_cnt); }
uint32_t rs690_mc_rreg(struct radeon_device *rdev, uint32_t reg)
From: Alex Deucher alexander.deucher@amd.com
Calculate the low and high watermarks based on the low and high clocks for the current power state. The dynamic pm hw will select the appropriate watermark based on the internal dpm state.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/rv515.c | 224 +++++++++++++++++++++++---------------- 1 files changed, 132 insertions(+), 92 deletions(-)
diff --git a/drivers/gpu/drm/radeon/rv515.c b/drivers/gpu/drm/radeon/rv515.c index 21c7d7b..8ea1573 100644 --- a/drivers/gpu/drm/radeon/rv515.c +++ b/drivers/gpu/drm/radeon/rv515.c @@ -937,13 +937,16 @@ struct rv515_watermark { };
static void rv515_crtc_bandwidth_compute(struct radeon_device *rdev, - struct radeon_crtc *crtc, - struct rv515_watermark *wm) + struct radeon_crtc *crtc, + struct rv515_watermark *wm, + bool low) { struct drm_display_mode *mode = &crtc->base.mode; fixed20_12 a, b, c; fixed20_12 pclk, request_fifo_depth, tolerable_latency, estimated_width; fixed20_12 consumption_time, line_time, chunk_time, read_delay_latency; + fixed20_12 sclk; + u32 selected_sclk;
if (!crtc->base.enabled) { /* FIXME: wouldn't it better to set priority mark to maximum */ @@ -951,6 +954,18 @@ static void rv515_crtc_bandwidth_compute(struct radeon_device *rdev, return; }
+ /* rv6xx, rv7xx */ + if ((rdev->family >= CHIP_RV610) && + (rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) + selected_sclk = radeon_dpm_get_sclk(rdev, low); + else + selected_sclk = rdev->pm.current_sclk; + + /* sclk in Mhz */ + a.full = dfixed_const(100); + sclk.full = dfixed_const(selected_sclk); + sclk.full = dfixed_div(sclk, a); + if (crtc->vsc.full > dfixed_const(2)) wm->num_line_pair.full = dfixed_const(2); else @@ -1016,7 +1031,7 @@ static void rv515_crtc_bandwidth_compute(struct radeon_device *rdev, * sclk = system clock(Mhz) */ a.full = dfixed_const(600 * 1000); - chunk_time.full = dfixed_div(a, rdev->pm.sclk); + chunk_time.full = dfixed_div(a, sclk); read_delay_latency.full = dfixed_const(1000);
/* Determine the worst case latency @@ -1077,152 +1092,177 @@ static void rv515_crtc_bandwidth_compute(struct radeon_device *rdev, } }
-void rv515_bandwidth_avivo_update(struct radeon_device *rdev) +static void rv515_compute_mode_priority(struct radeon_device *rdev, + struct rv515_watermark *wm0, + struct rv515_watermark *wm1, + struct drm_display_mode *mode0, + struct drm_display_mode *mode1, + u32 *d1mode_priority_a_cnt, + u32 *d2mode_priority_a_cnt) { - struct drm_display_mode *mode0 = NULL; - struct drm_display_mode *mode1 = NULL; - struct rv515_watermark wm0; - struct rv515_watermark wm1; - u32 tmp; - u32 d1mode_priority_a_cnt = MODE_PRIORITY_OFF; - u32 d2mode_priority_a_cnt = MODE_PRIORITY_OFF; fixed20_12 priority_mark02, priority_mark12, fill_rate; fixed20_12 a, b;
- if (rdev->mode_info.crtcs[0]->base.enabled) - mode0 = &rdev->mode_info.crtcs[0]->base.mode; - if (rdev->mode_info.crtcs[1]->base.enabled) - mode1 = &rdev->mode_info.crtcs[1]->base.mode; - rs690_line_buffer_adjust(rdev, mode0, mode1); - - rv515_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[0], &wm0); - rv515_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[1], &wm1); - - tmp = wm0.lb_request_fifo_depth; - tmp |= wm1.lb_request_fifo_depth << 16; - WREG32(LB_MAX_REQ_OUTSTANDING, tmp); + *d1mode_priority_a_cnt = MODE_PRIORITY_OFF; + *d2mode_priority_a_cnt = MODE_PRIORITY_OFF;
if (mode0 && mode1) { - if (dfixed_trunc(wm0.dbpp) > 64) - a.full = dfixed_div(wm0.dbpp, wm0.num_line_pair); + if (dfixed_trunc(wm0->dbpp) > 64) + a.full = dfixed_div(wm0->dbpp, wm0->num_line_pair); else - a.full = wm0.num_line_pair.full; - if (dfixed_trunc(wm1.dbpp) > 64) - b.full = dfixed_div(wm1.dbpp, wm1.num_line_pair); + a.full = wm0->num_line_pair.full; + if (dfixed_trunc(wm1->dbpp) > 64) + b.full = dfixed_div(wm1->dbpp, wm1->num_line_pair); else - b.full = wm1.num_line_pair.full; + b.full = wm1->num_line_pair.full; a.full += b.full; - fill_rate.full = dfixed_div(wm0.sclk, a); - if (wm0.consumption_rate.full > fill_rate.full) { - b.full = wm0.consumption_rate.full - fill_rate.full; - b.full = dfixed_mul(b, wm0.active_time); + fill_rate.full = dfixed_div(wm0->sclk, a); + if (wm0->consumption_rate.full > fill_rate.full) { + b.full = wm0->consumption_rate.full - fill_rate.full; + b.full = dfixed_mul(b, wm0->active_time); a.full = dfixed_const(16); b.full = dfixed_div(b, a); - a.full = dfixed_mul(wm0.worst_case_latency, - wm0.consumption_rate); + a.full = dfixed_mul(wm0->worst_case_latency, + wm0->consumption_rate); priority_mark02.full = a.full + b.full; } else { - a.full = dfixed_mul(wm0.worst_case_latency, - wm0.consumption_rate); + a.full = dfixed_mul(wm0->worst_case_latency, + wm0->consumption_rate); b.full = dfixed_const(16 * 1000); priority_mark02.full = dfixed_div(a, b); } - if (wm1.consumption_rate.full > fill_rate.full) { - b.full = wm1.consumption_rate.full - fill_rate.full; - b.full = dfixed_mul(b, wm1.active_time); + if (wm1->consumption_rate.full > fill_rate.full) { + b.full = wm1->consumption_rate.full - fill_rate.full; + b.full = dfixed_mul(b, wm1->active_time); a.full = dfixed_const(16); b.full = dfixed_div(b, a); - a.full = dfixed_mul(wm1.worst_case_latency, - wm1.consumption_rate); + a.full = dfixed_mul(wm1->worst_case_latency, + wm1->consumption_rate); priority_mark12.full = a.full + b.full; } else { - a.full = dfixed_mul(wm1.worst_case_latency, - wm1.consumption_rate); + a.full = dfixed_mul(wm1->worst_case_latency, + wm1->consumption_rate); b.full = dfixed_const(16 * 1000); priority_mark12.full = dfixed_div(a, b); } - if (wm0.priority_mark.full > priority_mark02.full) - priority_mark02.full = wm0.priority_mark.full; + if (wm0->priority_mark.full > priority_mark02.full) + priority_mark02.full = wm0->priority_mark.full; if (dfixed_trunc(priority_mark02) < 0) priority_mark02.full = 0; - if (wm0.priority_mark_max.full > priority_mark02.full) - priority_mark02.full = wm0.priority_mark_max.full; - if (wm1.priority_mark.full > priority_mark12.full) - priority_mark12.full = wm1.priority_mark.full; + if (wm0->priority_mark_max.full > priority_mark02.full) + priority_mark02.full = wm0->priority_mark_max.full; + if (wm1->priority_mark.full > priority_mark12.full) + priority_mark12.full = wm1->priority_mark.full; if (dfixed_trunc(priority_mark12) < 0) priority_mark12.full = 0; - if (wm1.priority_mark_max.full > priority_mark12.full) - priority_mark12.full = wm1.priority_mark_max.full; - d1mode_priority_a_cnt = dfixed_trunc(priority_mark02); - d2mode_priority_a_cnt = dfixed_trunc(priority_mark12); + if (wm1->priority_mark_max.full > priority_mark12.full) + priority_mark12.full = wm1->priority_mark_max.full; + *d1mode_priority_a_cnt = dfixed_trunc(priority_mark02); + *d2mode_priority_a_cnt = dfixed_trunc(priority_mark12); if (rdev->disp_priority == 2) { - d1mode_priority_a_cnt |= MODE_PRIORITY_ALWAYS_ON; - d2mode_priority_a_cnt |= MODE_PRIORITY_ALWAYS_ON; + *d1mode_priority_a_cnt |= MODE_PRIORITY_ALWAYS_ON; + *d2mode_priority_a_cnt |= MODE_PRIORITY_ALWAYS_ON; } } else if (mode0) { - if (dfixed_trunc(wm0.dbpp) > 64) - a.full = dfixed_div(wm0.dbpp, wm0.num_line_pair); + if (dfixed_trunc(wm0->dbpp) > 64) + a.full = dfixed_div(wm0->dbpp, wm0->num_line_pair); else - a.full = wm0.num_line_pair.full; - fill_rate.full = dfixed_div(wm0.sclk, a); - if (wm0.consumption_rate.full > fill_rate.full) { - b.full = wm0.consumption_rate.full - fill_rate.full; - b.full = dfixed_mul(b, wm0.active_time); + a.full = wm0->num_line_pair.full; + fill_rate.full = dfixed_div(wm0->sclk, a); + if (wm0->consumption_rate.full > fill_rate.full) { + b.full = wm0->consumption_rate.full - fill_rate.full; + b.full = dfixed_mul(b, wm0->active_time); a.full = dfixed_const(16); b.full = dfixed_div(b, a); - a.full = dfixed_mul(wm0.worst_case_latency, - wm0.consumption_rate); + a.full = dfixed_mul(wm0->worst_case_latency, + wm0->consumption_rate); priority_mark02.full = a.full + b.full; } else { - a.full = dfixed_mul(wm0.worst_case_latency, - wm0.consumption_rate); + a.full = dfixed_mul(wm0->worst_case_latency, + wm0->consumption_rate); b.full = dfixed_const(16); priority_mark02.full = dfixed_div(a, b); } - if (wm0.priority_mark.full > priority_mark02.full) - priority_mark02.full = wm0.priority_mark.full; + if (wm0->priority_mark.full > priority_mark02.full) + priority_mark02.full = wm0->priority_mark.full; if (dfixed_trunc(priority_mark02) < 0) priority_mark02.full = 0; - if (wm0.priority_mark_max.full > priority_mark02.full) - priority_mark02.full = wm0.priority_mark_max.full; - d1mode_priority_a_cnt = dfixed_trunc(priority_mark02); + if (wm0->priority_mark_max.full > priority_mark02.full) + priority_mark02.full = wm0->priority_mark_max.full; + *d1mode_priority_a_cnt = dfixed_trunc(priority_mark02); if (rdev->disp_priority == 2) - d1mode_priority_a_cnt |= MODE_PRIORITY_ALWAYS_ON; + *d1mode_priority_a_cnt |= MODE_PRIORITY_ALWAYS_ON; } else if (mode1) { - if (dfixed_trunc(wm1.dbpp) > 64) - a.full = dfixed_div(wm1.dbpp, wm1.num_line_pair); + if (dfixed_trunc(wm1->dbpp) > 64) + a.full = dfixed_div(wm1->dbpp, wm1->num_line_pair); else - a.full = wm1.num_line_pair.full; - fill_rate.full = dfixed_div(wm1.sclk, a); - if (wm1.consumption_rate.full > fill_rate.full) { - b.full = wm1.consumption_rate.full - fill_rate.full; - b.full = dfixed_mul(b, wm1.active_time); + a.full = wm1->num_line_pair.full; + fill_rate.full = dfixed_div(wm1->sclk, a); + if (wm1->consumption_rate.full > fill_rate.full) { + b.full = wm1->consumption_rate.full - fill_rate.full; + b.full = dfixed_mul(b, wm1->active_time); a.full = dfixed_const(16); b.full = dfixed_div(b, a); - a.full = dfixed_mul(wm1.worst_case_latency, - wm1.consumption_rate); + a.full = dfixed_mul(wm1->worst_case_latency, + wm1->consumption_rate); priority_mark12.full = a.full + b.full; } else { - a.full = dfixed_mul(wm1.worst_case_latency, - wm1.consumption_rate); + a.full = dfixed_mul(wm1->worst_case_latency, + wm1->consumption_rate); b.full = dfixed_const(16 * 1000); priority_mark12.full = dfixed_div(a, b); } - if (wm1.priority_mark.full > priority_mark12.full) - priority_mark12.full = wm1.priority_mark.full; + if (wm1->priority_mark.full > priority_mark12.full) + priority_mark12.full = wm1->priority_mark.full; if (dfixed_trunc(priority_mark12) < 0) priority_mark12.full = 0; - if (wm1.priority_mark_max.full > priority_mark12.full) - priority_mark12.full = wm1.priority_mark_max.full; - d2mode_priority_a_cnt = dfixed_trunc(priority_mark12); + if (wm1->priority_mark_max.full > priority_mark12.full) + priority_mark12.full = wm1->priority_mark_max.full; + *d2mode_priority_a_cnt = dfixed_trunc(priority_mark12); if (rdev->disp_priority == 2) - d2mode_priority_a_cnt |= MODE_PRIORITY_ALWAYS_ON; + *d2mode_priority_a_cnt |= MODE_PRIORITY_ALWAYS_ON; } +} + +void rv515_bandwidth_avivo_update(struct radeon_device *rdev) +{ + struct drm_display_mode *mode0 = NULL; + struct drm_display_mode *mode1 = NULL; + struct rv515_watermark wm0_high, wm0_low; + struct rv515_watermark wm1_high, wm1_low; + u32 tmp; + u32 d1mode_priority_a_cnt, d1mode_priority_b_cnt; + u32 d2mode_priority_a_cnt, d2mode_priority_b_cnt; + + if (rdev->mode_info.crtcs[0]->base.enabled) + mode0 = &rdev->mode_info.crtcs[0]->base.mode; + if (rdev->mode_info.crtcs[1]->base.enabled) + mode1 = &rdev->mode_info.crtcs[1]->base.mode; + rs690_line_buffer_adjust(rdev, mode0, mode1); + + rv515_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[0], &wm0_high, false); + rv515_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[1], &wm1_high, false); + + rv515_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[0], &wm0_low, false); + rv515_crtc_bandwidth_compute(rdev, rdev->mode_info.crtcs[1], &wm1_low, false); + + tmp = wm0_high.lb_request_fifo_depth; + tmp |= wm1_high.lb_request_fifo_depth << 16; + WREG32(LB_MAX_REQ_OUTSTANDING, tmp); + + rv515_compute_mode_priority(rdev, + &wm0_high, &wm1_high, + mode0, mode1, + &d1mode_priority_a_cnt, &d2mode_priority_a_cnt); + rv515_compute_mode_priority(rdev, + &wm0_low, &wm1_low, + mode0, mode1, + &d1mode_priority_b_cnt, &d2mode_priority_b_cnt);
WREG32(D1MODE_PRIORITY_A_CNT, d1mode_priority_a_cnt); - WREG32(D1MODE_PRIORITY_B_CNT, d1mode_priority_a_cnt); + WREG32(D1MODE_PRIORITY_B_CNT, d1mode_priority_b_cnt); WREG32(D2MODE_PRIORITY_A_CNT, d2mode_priority_a_cnt); - WREG32(D2MODE_PRIORITY_B_CNT, d2mode_priority_a_cnt); + WREG32(D2MODE_PRIORITY_B_CNT, d2mode_priority_b_cnt); }
void rv515_bandwidth_update(struct radeon_device *rdev)
From: Alex Deucher alexander.deucher@amd.com
Calculate the low and high watermarks based on the low and high clocks for the current power state. The dynamic pm hw will select the appropriate watermark based on the internal dpm state.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/evergreen.c | 89 ++++++++++++++++++++++++++--------- 1 files changed, 66 insertions(+), 23 deletions(-)
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index b9f64f0..63a1e6e 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -2122,7 +2122,8 @@ static void evergreen_program_watermarks(struct radeon_device *rdev, u32 lb_size, u32 num_heads) { struct drm_display_mode *mode = &radeon_crtc->base.mode; - struct evergreen_wm_params wm; + struct evergreen_wm_params wm_low, wm_high; + u32 dram_channels; u32 pixel_period; u32 line_time = 0; u32 latency_watermark_a = 0, latency_watermark_b = 0; @@ -2138,39 +2139,81 @@ static void evergreen_program_watermarks(struct radeon_device *rdev, line_time = min((u32)mode->crtc_htotal * pixel_period, (u32)65535); priority_a_cnt = 0; priority_b_cnt = 0; + dram_channels = evergreen_get_number_of_dram_channels(rdev); + + /* watermark for high clocks */ + if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) { + wm_high.yclk = + radeon_dpm_get_mclk(rdev, false) * 10; + wm_high.sclk = + radeon_dpm_get_sclk(rdev, false) * 10; + } else { + wm_high.yclk = rdev->pm.current_mclk * 10; + wm_high.sclk = rdev->pm.current_sclk * 10; + }
- wm.yclk = rdev->pm.current_mclk * 10; - wm.sclk = rdev->pm.current_sclk * 10; - wm.disp_clk = mode->clock; - wm.src_width = mode->crtc_hdisplay; - wm.active_time = mode->crtc_hdisplay * pixel_period; - wm.blank_time = line_time - wm.active_time; - wm.interlaced = false; + wm_high.disp_clk = mode->clock; + wm_high.src_width = mode->crtc_hdisplay; + wm_high.active_time = mode->crtc_hdisplay * pixel_period; + wm_high.blank_time = line_time - wm_high.active_time; + wm_high.interlaced = false; if (mode->flags & DRM_MODE_FLAG_INTERLACE) - wm.interlaced = true; - wm.vsc = radeon_crtc->vsc; - wm.vtaps = 1; + wm_high.interlaced = true; + wm_high.vsc = radeon_crtc->vsc; + wm_high.vtaps = 1; if (radeon_crtc->rmx_type != RMX_OFF) - wm.vtaps = 2; - wm.bytes_per_pixel = 4; /* XXX: get this from fb config */ - wm.lb_size = lb_size; - wm.dram_channels = evergreen_get_number_of_dram_channels(rdev); - wm.num_heads = num_heads; + wm_high.vtaps = 2; + wm_high.bytes_per_pixel = 4; /* XXX: get this from fb config */ + wm_high.lb_size = lb_size; + wm_high.dram_channels = dram_channels; + wm_high.num_heads = num_heads; + + /* watermark for low clocks */ + if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) { + wm_low.yclk = + radeon_dpm_get_mclk(rdev, true) * 10; + wm_low.sclk = + radeon_dpm_get_sclk(rdev, true) * 10; + } else { + wm_low.yclk = rdev->pm.current_mclk * 10; + wm_low.sclk = rdev->pm.current_sclk * 10; + } + + wm_low.disp_clk = mode->clock; + wm_low.src_width = mode->crtc_hdisplay; + wm_low.active_time = mode->crtc_hdisplay * pixel_period; + wm_low.blank_time = line_time - wm_low.active_time; + wm_low.interlaced = false; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + wm_low.interlaced = true; + wm_low.vsc = radeon_crtc->vsc; + wm_low.vtaps = 1; + if (radeon_crtc->rmx_type != RMX_OFF) + wm_low.vtaps = 2; + wm_low.bytes_per_pixel = 4; /* XXX: get this from fb config */ + wm_low.lb_size = lb_size; + wm_low.dram_channels = dram_channels; + wm_low.num_heads = num_heads;
/* set for high clocks */ - latency_watermark_a = min(evergreen_latency_watermark(&wm), (u32)65535); + latency_watermark_a = min(evergreen_latency_watermark(&wm_high), (u32)65535); /* set for low clocks */ - /* wm.yclk = low clk; wm.sclk = low clk */ - latency_watermark_b = min(evergreen_latency_watermark(&wm), (u32)65535); + latency_watermark_b = min(evergreen_latency_watermark(&wm_low), (u32)65535);
/* possibly force display priority to high */ /* should really do this at mode validation time... */ - if (!evergreen_average_bandwidth_vs_dram_bandwidth_for_display(&wm) || - !evergreen_average_bandwidth_vs_available_bandwidth(&wm) || - !evergreen_check_latency_hiding(&wm) || + if (!evergreen_average_bandwidth_vs_dram_bandwidth_for_display(&wm_high) || + !evergreen_average_bandwidth_vs_available_bandwidth(&wm_high) || + !evergreen_check_latency_hiding(&wm_high) || (rdev->disp_priority == 2)) { - DRM_DEBUG_KMS("force priority to high\n"); + DRM_DEBUG_KMS("force priority a to high\n"); priority_a_cnt |= PRIORITY_ALWAYS_ON; + } + if (!evergreen_average_bandwidth_vs_dram_bandwidth_for_display(&wm_low) || + !evergreen_average_bandwidth_vs_available_bandwidth(&wm_low) || + !evergreen_check_latency_hiding(&wm_low) || + (rdev->disp_priority == 2)) { + DRM_DEBUG_KMS("force priority b to high\n"); priority_b_cnt |= PRIORITY_ALWAYS_ON; }
From: Alex Deucher alexander.deucher@amd.com
Calculate the low and high watermarks based on the low and high clocks for the current power state. The dynamic pm hw will select the appropriate watermark based on the internal dpm state.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/si.c | 96 +++++++++++++++++++++++++++++++----------- 1 files changed, 71 insertions(+), 25 deletions(-)
diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c index 813a8a9..882509a 100644 --- a/drivers/gpu/drm/radeon/si.c +++ b/drivers/gpu/drm/radeon/si.c @@ -1792,7 +1792,8 @@ static void dce6_program_watermarks(struct radeon_device *rdev, u32 lb_size, u32 num_heads) { struct drm_display_mode *mode = &radeon_crtc->base.mode; - struct dce6_wm_params wm; + struct dce6_wm_params wm_low, wm_high; + u32 dram_channels; u32 pixel_period; u32 line_time = 0; u32 latency_watermark_a = 0, latency_watermark_b = 0; @@ -1808,38 +1809,83 @@ static void dce6_program_watermarks(struct radeon_device *rdev, priority_a_cnt = 0; priority_b_cnt = 0;
- wm.yclk = rdev->pm.current_mclk * 10; - wm.sclk = rdev->pm.current_sclk * 10; - wm.disp_clk = mode->clock; - wm.src_width = mode->crtc_hdisplay; - wm.active_time = mode->crtc_hdisplay * pixel_period; - wm.blank_time = line_time - wm.active_time; - wm.interlaced = false; - if (mode->flags & DRM_MODE_FLAG_INTERLACE) - wm.interlaced = true; - wm.vsc = radeon_crtc->vsc; - wm.vtaps = 1; - if (radeon_crtc->rmx_type != RMX_OFF) - wm.vtaps = 2; - wm.bytes_per_pixel = 4; /* XXX: get this from fb config */ - wm.lb_size = lb_size; if (rdev->family == CHIP_ARUBA) - wm.dram_channels = evergreen_get_number_of_dram_channels(rdev); + dram_channels = evergreen_get_number_of_dram_channels(rdev); else - wm.dram_channels = si_get_number_of_dram_channels(rdev); - wm.num_heads = num_heads; + dram_channels = si_get_number_of_dram_channels(rdev); + + /* watermark for high clocks */ + if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) { + wm_high.yclk = + radeon_dpm_get_mclk(rdev, false) * 10; + wm_high.sclk = + radeon_dpm_get_sclk(rdev, false) * 10; + } else { + wm_high.yclk = rdev->pm.current_mclk * 10; + wm_high.sclk = rdev->pm.current_sclk * 10; + } + + wm_high.disp_clk = mode->clock; + wm_high.src_width = mode->crtc_hdisplay; + wm_high.active_time = mode->crtc_hdisplay * pixel_period; + wm_high.blank_time = line_time - wm_high.active_time; + wm_high.interlaced = false; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + wm_high.interlaced = true; + wm_high.vsc = radeon_crtc->vsc; + wm_high.vtaps = 1; + if (radeon_crtc->rmx_type != RMX_OFF) + wm_high.vtaps = 2; + wm_high.bytes_per_pixel = 4; /* XXX: get this from fb config */ + wm_high.lb_size = lb_size; + wm_high.dram_channels = dram_channels; + wm_high.num_heads = num_heads; + + /* watermark for low clocks */ + if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) { + wm_low.yclk = + radeon_dpm_get_mclk(rdev, true) * 10; + wm_low.sclk = + radeon_dpm_get_sclk(rdev, true) * 10; + } else { + wm_low.yclk = rdev->pm.current_mclk * 10; + wm_low.sclk = rdev->pm.current_sclk * 10; + } + + wm_low.disp_clk = mode->clock; + wm_low.src_width = mode->crtc_hdisplay; + wm_low.active_time = mode->crtc_hdisplay * pixel_period; + wm_low.blank_time = line_time - wm_low.active_time; + wm_low.interlaced = false; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + wm_low.interlaced = true; + wm_low.vsc = radeon_crtc->vsc; + wm_low.vtaps = 1; + if (radeon_crtc->rmx_type != RMX_OFF) + wm_low.vtaps = 2; + wm_low.bytes_per_pixel = 4; /* XXX: get this from fb config */ + wm_low.lb_size = lb_size; + wm_low.dram_channels = dram_channels; + wm_low.num_heads = num_heads;
/* set for high clocks */ - latency_watermark_a = min(dce6_latency_watermark(&wm), (u32)65535); + latency_watermark_a = min(dce6_latency_watermark(&wm_high), (u32)65535); /* set for low clocks */ - /* wm.yclk = low clk; wm.sclk = low clk */ - latency_watermark_b = min(dce6_latency_watermark(&wm), (u32)65535); + latency_watermark_b = min(dce6_latency_watermark(&wm_low), (u32)65535);
/* possibly force display priority to high */ /* should really do this at mode validation time... */ - if (!dce6_average_bandwidth_vs_dram_bandwidth_for_display(&wm) || - !dce6_average_bandwidth_vs_available_bandwidth(&wm) || - !dce6_check_latency_hiding(&wm) || + if (!dce6_average_bandwidth_vs_dram_bandwidth_for_display(&wm_high) || + !dce6_average_bandwidth_vs_available_bandwidth(&wm_high) || + !dce6_check_latency_hiding(&wm_high) || + (rdev->disp_priority == 2)) { + DRM_DEBUG_KMS("force priority to high\n"); + priority_a_cnt |= PRIORITY_ALWAYS_ON; + priority_b_cnt |= PRIORITY_ALWAYS_ON; + } + if (!dce6_average_bandwidth_vs_dram_bandwidth_for_display(&wm_low) || + !dce6_average_bandwidth_vs_available_bandwidth(&wm_low) || + !dce6_check_latency_hiding(&wm_low) || (rdev->disp_priority == 2)) { DRM_DEBUG_KMS("force priority to high\n"); priority_a_cnt |= PRIORITY_ALWAYS_ON;
From: Alex Deucher alexander.deucher@amd.com
These are shared by rs780/rs880, rv6xx, and newer chips.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/Makefile | 3 +- drivers/gpu/drm/radeon/r600_dpm.c | 678 +++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/r600_dpm.h | 210 ++++++++++++ drivers/gpu/drm/radeon/r600d.h | 213 ++++++++++++ drivers/gpu/drm/radeon/radeon.h | 13 + 5 files changed, 1116 insertions(+), 1 deletions(-) create mode 100644 drivers/gpu/drm/radeon/r600_dpm.c create mode 100644 drivers/gpu/drm/radeon/r600_dpm.h
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index 292fd25..a131a13 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile @@ -76,7 +76,8 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \ evergreen.o evergreen_cs.o evergreen_blit_shaders.o evergreen_blit_kms.o \ evergreen_hdmi.o radeon_trace_points.o ni.o cayman_blit_shaders.o \ atombios_encoders.o radeon_semaphore.o radeon_sa.o atombios_i2c.o si.o \ - si_blit_shaders.o radeon_prime.o radeon_uvd.o cik.o cik_blit_shaders.o + si_blit_shaders.o radeon_prime.o radeon_uvd.o cik.o cik_blit_shaders.o \ + r600_dpm.o
radeon-$(CONFIG_COMPAT) += radeon_ioc32.o radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o diff --git a/drivers/gpu/drm/radeon/r600_dpm.c b/drivers/gpu/drm/radeon/r600_dpm.c new file mode 100644 index 0000000..91bc5ab --- /dev/null +++ b/drivers/gpu/drm/radeon/r600_dpm.c @@ -0,0 +1,678 @@ +/* + * Copyright 2011 Advanced Micro Devices, 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: Alex Deucher + */ + +#include "drmP.h" +#include "radeon.h" +#include "r600d.h" +#include "r600_dpm.h" +#include "atom.h" + +const u32 r600_utc[R600_PM_NUMBER_OF_TC] = +{ + R600_UTC_DFLT_00, + R600_UTC_DFLT_01, + R600_UTC_DFLT_02, + R600_UTC_DFLT_03, + R600_UTC_DFLT_04, + R600_UTC_DFLT_05, + R600_UTC_DFLT_06, + R600_UTC_DFLT_07, + R600_UTC_DFLT_08, + R600_UTC_DFLT_09, + R600_UTC_DFLT_10, + R600_UTC_DFLT_11, + R600_UTC_DFLT_12, + R600_UTC_DFLT_13, + R600_UTC_DFLT_14, +}; + +const u32 r600_dtc[R600_PM_NUMBER_OF_TC] = +{ + R600_DTC_DFLT_00, + R600_DTC_DFLT_01, + R600_DTC_DFLT_02, + R600_DTC_DFLT_03, + R600_DTC_DFLT_04, + R600_DTC_DFLT_05, + R600_DTC_DFLT_06, + R600_DTC_DFLT_07, + R600_DTC_DFLT_08, + R600_DTC_DFLT_09, + R600_DTC_DFLT_10, + R600_DTC_DFLT_11, + R600_DTC_DFLT_12, + R600_DTC_DFLT_13, + R600_DTC_DFLT_14, +}; + +void r600_dpm_print_class_info(u32 class, u32 class2) +{ + printk("\tui class: "); + switch (class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) { + case ATOM_PPLIB_CLASSIFICATION_UI_NONE: + default: + printk("none\n"); + break; + case ATOM_PPLIB_CLASSIFICATION_UI_BATTERY: + printk("battery\n"); + break; + case ATOM_PPLIB_CLASSIFICATION_UI_BALANCED: + printk("balanced\n"); + break; + case ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE: + printk("performance\n"); + break; + } + printk("\tinternal class: "); + if (((class & ~ATOM_PPLIB_CLASSIFICATION_UI_MASK) == 0) && + (class2 == 0)) + printk("none"); + else { + if (class & ATOM_PPLIB_CLASSIFICATION_BOOT) + printk("boot "); + if (class & ATOM_PPLIB_CLASSIFICATION_THERMAL) + printk("thermal "); + if (class & ATOM_PPLIB_CLASSIFICATION_LIMITEDPOWERSOURCE) + printk("limited_pwr "); + if (class & ATOM_PPLIB_CLASSIFICATION_REST) + printk("rest "); + if (class & ATOM_PPLIB_CLASSIFICATION_FORCED) + printk("forced "); + if (class & ATOM_PPLIB_CLASSIFICATION_3DPERFORMANCE) + printk("3d_perf "); + if (class & ATOM_PPLIB_CLASSIFICATION_OVERDRIVETEMPLATE) + printk("ovrdrv "); + if (class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE) + printk("uvd "); + if (class & ATOM_PPLIB_CLASSIFICATION_3DLOW) + printk("3d_low "); + if (class & ATOM_PPLIB_CLASSIFICATION_ACPI) + printk("acpi "); + if (class & ATOM_PPLIB_CLASSIFICATION_HD2STATE) + printk("uvd_hd2 "); + if (class & ATOM_PPLIB_CLASSIFICATION_HDSTATE) + printk("uvd_hd "); + if (class & ATOM_PPLIB_CLASSIFICATION_SDSTATE) + printk("uvd_sd "); + if (class2 & ATOM_PPLIB_CLASSIFICATION2_LIMITEDPOWERSOURCE_2) + printk("limited_pwr2 "); + if (class2 & ATOM_PPLIB_CLASSIFICATION2_ULV) + printk("ulv "); + if (class2 & ATOM_PPLIB_CLASSIFICATION2_MVC) + printk("uvd_mvc "); + } + printk("\n"); +} + +void r600_dpm_print_cap_info(u32 caps) +{ + printk("\tcaps: "); + if (caps & ATOM_PPLIB_SINGLE_DISPLAY_ONLY) + printk("single_disp "); + if (caps & ATOM_PPLIB_SUPPORTS_VIDEO_PLAYBACK) + printk("video "); + if (caps & ATOM_PPLIB_DISALLOW_ON_DC) + printk("no_dc "); + printk("\n"); +} + +void r600_dpm_print_ps_status(struct radeon_device *rdev, + struct radeon_ps *rps) +{ + printk("\tstatus: "); + if (rps == rdev->pm.dpm.current_ps) + printk("c "); + if (rps == rdev->pm.dpm.requested_ps) + printk("r "); + if (rps == rdev->pm.dpm.boot_ps) + printk("b "); + printk("\n"); +} + +void r600_calculate_u_and_p(u32 i, u32 r_c, u32 p_b, + u32 *p, u32 *u) +{ + u32 b_c = 0; + u32 i_c; + u32 tmp; + + i_c = (i * r_c) / 100; + tmp = i_c >> p_b; + + while (tmp) { + b_c++; + tmp >>= 1; + } + + *u = (b_c + 1) / 2; + *p = i_c / (1 << (2 * (*u))); +} + +int r600_calculate_at(u32 t, u32 h, u32 fh, u32 fl, u32 *tl, u32 *th) +{ + u32 k, a, ah, al; + u32 t1; + + if ((fl == 0) || (fh == 0) || (fl > fh)) + return -EINVAL; + + k = (100 * fh) / fl; + t1 = (t * (k - 100)); + a = (1000 * (100 * h + t1)) / (10000 + (t1 / 100)); + a = (a + 5) / 10; + ah = ((a * t) + 5000) / 10000; + al = a - ah; + + *th = t - ah; + *tl = t + al; + + return 0; +} + +void r600_gfx_clockgating_enable(struct radeon_device *rdev, bool enable) +{ + int i; + + if (enable) { + WREG32_P(SCLK_PWRMGT_CNTL, DYN_GFX_CLK_OFF_EN, ~DYN_GFX_CLK_OFF_EN); + } else { + WREG32_P(SCLK_PWRMGT_CNTL, 0, ~DYN_GFX_CLK_OFF_EN); + + WREG32(CG_RLC_REQ_AND_RSP, 0x2); + + for (i = 0; i < rdev->usec_timeout; i++) { + if (((RREG32(CG_RLC_REQ_AND_RSP) & CG_RLC_RSP_TYPE_MASK) >> CG_RLC_RSP_TYPE_SHIFT) == 1) + break; + udelay(1); + } + + WREG32(CG_RLC_REQ_AND_RSP, 0x0); + + WREG32(GRBM_PWR_CNTL, 0x1); + RREG32(GRBM_PWR_CNTL); + } +} + +void r600_dynamicpm_enable(struct radeon_device *rdev, bool enable) +{ + if (enable) + WREG32_P(GENERAL_PWRMGT, GLOBAL_PWRMGT_EN, ~GLOBAL_PWRMGT_EN); + else + WREG32_P(GENERAL_PWRMGT, 0, ~GLOBAL_PWRMGT_EN); +} + +void r600_enable_thermal_protection(struct radeon_device *rdev, bool enable) +{ + if (enable) + WREG32_P(GENERAL_PWRMGT, 0, ~THERMAL_PROTECTION_DIS); + else + WREG32_P(GENERAL_PWRMGT, THERMAL_PROTECTION_DIS, ~THERMAL_PROTECTION_DIS); +} + +void r600_enable_acpi_pm(struct radeon_device *rdev) +{ + WREG32_P(GENERAL_PWRMGT, STATIC_PM_EN, ~STATIC_PM_EN); +} + +void r600_enable_dynamic_pcie_gen2(struct radeon_device *rdev, bool enable) +{ + if (enable) + WREG32_P(GENERAL_PWRMGT, ENABLE_GEN2PCIE, ~ENABLE_GEN2PCIE); + else + WREG32_P(GENERAL_PWRMGT, 0, ~ENABLE_GEN2PCIE); +} + +bool r600_dynamicpm_enabled(struct radeon_device *rdev) +{ + if (RREG32(GENERAL_PWRMGT) & GLOBAL_PWRMGT_EN) + return true; + else + return false; +} + +void r600_enable_sclk_control(struct radeon_device *rdev, bool enable) +{ + if (enable) + WREG32_P(GENERAL_PWRMGT, 0, ~SCLK_PWRMGT_OFF); + else + WREG32_P(GENERAL_PWRMGT, SCLK_PWRMGT_OFF, ~SCLK_PWRMGT_OFF); +} + +void r600_enable_mclk_control(struct radeon_device *rdev, bool enable) +{ + if (enable) + WREG32_P(MCLK_PWRMGT_CNTL, 0, ~MPLL_PWRMGT_OFF); + else + WREG32_P(MCLK_PWRMGT_CNTL, MPLL_PWRMGT_OFF, ~MPLL_PWRMGT_OFF); +} + +void r600_enable_spll_bypass(struct radeon_device *rdev, bool enable) +{ + if (enable) + WREG32_P(CG_SPLL_FUNC_CNTL, SPLL_BYPASS_EN, ~SPLL_BYPASS_EN); + else + WREG32_P(CG_SPLL_FUNC_CNTL, 0, ~SPLL_BYPASS_EN); +} + +void r600_wait_for_spll_change(struct radeon_device *rdev) +{ + int i; + + for (i = 0; i < rdev->usec_timeout; i++) { + if (RREG32(CG_SPLL_FUNC_CNTL) & SPLL_CHG_STATUS) + break; + udelay(1); + } +} + +void r600_set_bsp(struct radeon_device *rdev, u32 u, u32 p) +{ + WREG32(CG_BSP, BSP(p) | BSU(u)); +} + +void r600_set_at(struct radeon_device *rdev, + u32 l_to_m, u32 m_to_h, + u32 h_to_m, u32 m_to_l) +{ + WREG32(CG_RT, FLS(l_to_m) | FMS(m_to_h)); + WREG32(CG_LT, FHS(h_to_m) | FMS(m_to_l)); +} + +void r600_set_tc(struct radeon_device *rdev, + u32 index, u32 u_t, u32 d_t) +{ + WREG32(CG_FFCT_0 + (index * 4), UTC_0(u_t) | DTC_0(d_t)); +} + +void r600_select_td(struct radeon_device *rdev, + enum r600_td td) +{ + if (td == R600_TD_AUTO) + WREG32_P(SCLK_PWRMGT_CNTL, 0, ~FIR_FORCE_TREND_SEL); + else + WREG32_P(SCLK_PWRMGT_CNTL, FIR_FORCE_TREND_SEL, ~FIR_FORCE_TREND_SEL); + if (td == R600_TD_UP) + WREG32_P(SCLK_PWRMGT_CNTL, 0, ~FIR_TREND_MODE); + if (td == R600_TD_DOWN) + WREG32_P(SCLK_PWRMGT_CNTL, FIR_TREND_MODE, ~FIR_TREND_MODE); +} + +void r600_set_vrc(struct radeon_device *rdev, u32 vrv) +{ + WREG32(CG_FTV, vrv); +} + +void r600_set_tpu(struct radeon_device *rdev, u32 u) +{ + WREG32_P(CG_TPC, TPU(u), ~TPU_MASK); +} + +void r600_set_tpc(struct radeon_device *rdev, u32 c) +{ + WREG32_P(CG_TPC, TPCC(c), ~TPCC_MASK); +} + +void r600_set_sstu(struct radeon_device *rdev, u32 u) +{ + WREG32_P(CG_SSP, CG_SSTU(u), ~CG_SSTU_MASK); +} + +void r600_set_sst(struct radeon_device *rdev, u32 t) +{ + WREG32_P(CG_SSP, CG_SST(t), ~CG_SST_MASK); +} + +void r600_set_git(struct radeon_device *rdev, u32 t) +{ + WREG32_P(CG_GIT, CG_GICST(t), ~CG_GICST_MASK); +} + +void r600_set_fctu(struct radeon_device *rdev, u32 u) +{ + WREG32_P(CG_FC_T, FC_TU(u), ~FC_TU_MASK); +} + +void r600_set_fct(struct radeon_device *rdev, u32 t) +{ + WREG32_P(CG_FC_T, FC_T(t), ~FC_T_MASK); +} + +void r600_set_ctxcgtt3d_rphc(struct radeon_device *rdev, u32 p) +{ + WREG32_P(CG_CTX_CGTT3D_R, PHC(p), ~PHC_MASK); +} + +void r600_set_ctxcgtt3d_rsdc(struct radeon_device *rdev, u32 s) +{ + WREG32_P(CG_CTX_CGTT3D_R, SDC(s), ~SDC_MASK); +} + +void r600_set_vddc3d_oorsu(struct radeon_device *rdev, u32 u) +{ + WREG32_P(CG_VDDC3D_OOR, SU(u), ~SU_MASK); +} + +void r600_set_vddc3d_oorphc(struct radeon_device *rdev, u32 p) +{ + WREG32_P(CG_VDDC3D_OOR, PHC(p), ~PHC_MASK); +} + +void r600_set_vddc3d_oorsdc(struct radeon_device *rdev, u32 s) +{ + WREG32_P(CG_VDDC3D_OOR, SDC(s), ~SDC_MASK); +} + +void r600_set_mpll_lock_time(struct radeon_device *rdev, u32 lock_time) +{ + WREG32_P(MPLL_TIME, MPLL_LOCK_TIME(lock_time), ~MPLL_LOCK_TIME_MASK); +} + +void r600_set_mpll_reset_time(struct radeon_device *rdev, u32 reset_time) +{ + WREG32_P(MPLL_TIME, MPLL_RESET_TIME(reset_time), ~MPLL_RESET_TIME_MASK); +} + +void r600_engine_clock_entry_enable(struct radeon_device *rdev, + u32 index, bool enable) +{ + if (enable) + WREG32_P(SCLK_FREQ_SETTING_STEP_0_PART2 + (index * 4 * 2), + STEP_0_SPLL_ENTRY_VALID, ~STEP_0_SPLL_ENTRY_VALID); + else + WREG32_P(SCLK_FREQ_SETTING_STEP_0_PART2 + (index * 4 * 2), + 0, ~STEP_0_SPLL_ENTRY_VALID); +} + +void r600_engine_clock_entry_enable_pulse_skipping(struct radeon_device *rdev, + u32 index, bool enable) +{ + if (enable) + WREG32_P(SCLK_FREQ_SETTING_STEP_0_PART2 + (index * 4 * 2), + STEP_0_SPLL_STEP_ENABLE, ~STEP_0_SPLL_STEP_ENABLE); + else + WREG32_P(SCLK_FREQ_SETTING_STEP_0_PART2 + (index * 4 * 2), + 0, ~STEP_0_SPLL_STEP_ENABLE); +} + +void r600_engine_clock_entry_enable_post_divider(struct radeon_device *rdev, + u32 index, bool enable) +{ + if (enable) + WREG32_P(SCLK_FREQ_SETTING_STEP_0_PART2 + (index * 4 * 2), + STEP_0_POST_DIV_EN, ~STEP_0_POST_DIV_EN); + else + WREG32_P(SCLK_FREQ_SETTING_STEP_0_PART2 + (index * 4 * 2), + 0, ~STEP_0_POST_DIV_EN); +} + +void r600_engine_clock_entry_set_post_divider(struct radeon_device *rdev, + u32 index, u32 divider) +{ + WREG32_P(SCLK_FREQ_SETTING_STEP_0_PART1 + (index * 4 * 2), + STEP_0_SPLL_POST_DIV(divider), ~STEP_0_SPLL_POST_DIV_MASK); +} + +void r600_engine_clock_entry_set_reference_divider(struct radeon_device *rdev, + u32 index, u32 divider) +{ + WREG32_P(SCLK_FREQ_SETTING_STEP_0_PART1 + (index * 4 * 2), + STEP_0_SPLL_REF_DIV(divider), ~STEP_0_SPLL_REF_DIV_MASK); +} + +void r600_engine_clock_entry_set_feedback_divider(struct radeon_device *rdev, + u32 index, u32 divider) +{ + WREG32_P(SCLK_FREQ_SETTING_STEP_0_PART1 + (index * 4 * 2), + STEP_0_SPLL_FB_DIV(divider), ~STEP_0_SPLL_FB_DIV_MASK); +} + +void r600_engine_clock_entry_set_step_time(struct radeon_device *rdev, + u32 index, u32 step_time) +{ + WREG32_P(SCLK_FREQ_SETTING_STEP_0_PART1 + (index * 4 * 2), + STEP_0_SPLL_STEP_TIME(step_time), ~STEP_0_SPLL_STEP_TIME_MASK); +} + +void r600_vid_rt_set_ssu(struct radeon_device *rdev, u32 u) +{ + WREG32_P(VID_RT, SSTU(u), ~SSTU_MASK); +} + +void r600_vid_rt_set_vru(struct radeon_device *rdev, u32 u) +{ + WREG32_P(VID_RT, VID_CRTU(u), ~VID_CRTU_MASK); +} + +void r600_vid_rt_set_vrt(struct radeon_device *rdev, u32 rt) +{ + WREG32_P(VID_RT, VID_CRT(rt), ~VID_CRT_MASK); +} + +void r600_voltage_control_enable_pins(struct radeon_device *rdev, + u64 mask) +{ + WREG32(LOWER_GPIO_ENABLE, mask & 0xffffffff); + WREG32(UPPER_GPIO_ENABLE, upper_32_bits(mask)); +} + + +void r600_voltage_control_program_voltages(struct radeon_device *rdev, + enum r600_power_level index, u64 pins) +{ + u32 tmp, mask; + u32 ix = 3 - (3 & index); + + WREG32(CTXSW_VID_LOWER_GPIO_CNTL + (ix * 4), pins & 0xffffffff); + + mask = 7 << (3 * ix); + tmp = RREG32(VID_UPPER_GPIO_CNTL); + tmp = (tmp & ~mask) | ((pins >> (32 - (3 * ix))) & mask); + WREG32(VID_UPPER_GPIO_CNTL, tmp); +} + +void r600_voltage_control_deactivate_static_control(struct radeon_device *rdev, + u64 mask) +{ + u32 gpio; + + gpio = RREG32(GPIOPAD_MASK); + gpio &= ~mask; + WREG32(GPIOPAD_MASK, gpio); + + gpio = RREG32(GPIOPAD_EN); + gpio &= ~mask; + WREG32(GPIOPAD_EN, gpio); + + gpio = RREG32(GPIOPAD_A); + gpio &= ~mask; + WREG32(GPIOPAD_A, gpio); +} + +void r600_power_level_enable(struct radeon_device *rdev, + enum r600_power_level index, bool enable) +{ + u32 ix = 3 - (3 & index); + + if (enable) + WREG32_P(CTXSW_PROFILE_INDEX + (ix * 4), CTXSW_FREQ_STATE_ENABLE, + ~CTXSW_FREQ_STATE_ENABLE); + else + WREG32_P(CTXSW_PROFILE_INDEX + (ix * 4), 0, + ~CTXSW_FREQ_STATE_ENABLE); +} + +void r600_power_level_set_voltage_index(struct radeon_device *rdev, + enum r600_power_level index, u32 voltage_index) +{ + u32 ix = 3 - (3 & index); + + WREG32_P(CTXSW_PROFILE_INDEX + (ix * 4), + CTXSW_FREQ_VIDS_CFG_INDEX(voltage_index), ~CTXSW_FREQ_VIDS_CFG_INDEX_MASK); +} + +void r600_power_level_set_mem_clock_index(struct radeon_device *rdev, + enum r600_power_level index, u32 mem_clock_index) +{ + u32 ix = 3 - (3 & index); + + WREG32_P(CTXSW_PROFILE_INDEX + (ix * 4), + CTXSW_FREQ_MCLK_CFG_INDEX(mem_clock_index), ~CTXSW_FREQ_MCLK_CFG_INDEX_MASK); +} + +void r600_power_level_set_eng_clock_index(struct radeon_device *rdev, + enum r600_power_level index, u32 eng_clock_index) +{ + u32 ix = 3 - (3 & index); + + WREG32_P(CTXSW_PROFILE_INDEX + (ix * 4), + CTXSW_FREQ_SCLK_CFG_INDEX(eng_clock_index), ~CTXSW_FREQ_SCLK_CFG_INDEX_MASK); +} + +void r600_power_level_set_watermark_id(struct radeon_device *rdev, + enum r600_power_level index, + enum r600_display_watermark watermark_id) +{ + u32 ix = 3 - (3 & index); + u32 tmp = 0; + + if (watermark_id == R600_DISPLAY_WATERMARK_HIGH) + tmp = CTXSW_FREQ_DISPLAY_WATERMARK; + WREG32_P(CTXSW_PROFILE_INDEX + (ix * 4), tmp, ~CTXSW_FREQ_DISPLAY_WATERMARK); +} + +void r600_power_level_set_pcie_gen2(struct radeon_device *rdev, + enum r600_power_level index, bool compatible) +{ + u32 ix = 3 - (3 & index); + u32 tmp = 0; + + if (compatible) + tmp = CTXSW_FREQ_GEN2PCIE_VOLT; + WREG32_P(CTXSW_PROFILE_INDEX + (ix * 4), tmp, ~CTXSW_FREQ_GEN2PCIE_VOLT); +} + +enum r600_power_level r600_power_level_get_current_index(struct radeon_device *rdev) +{ + u32 tmp; + + tmp = RREG32(TARGET_AND_CURRENT_PROFILE_INDEX) & CURRENT_PROFILE_INDEX_MASK; + tmp >>= CURRENT_PROFILE_INDEX_SHIFT; + return tmp; +} + +enum r600_power_level r600_power_level_get_target_index(struct radeon_device *rdev) +{ + u32 tmp; + + tmp = RREG32(TARGET_AND_CURRENT_PROFILE_INDEX) & TARGET_PROFILE_INDEX_MASK; + tmp >>= TARGET_PROFILE_INDEX_SHIFT; + return tmp; +} + +void r600_power_level_set_enter_index(struct radeon_device *rdev, + enum r600_power_level index) +{ + WREG32_P(TARGET_AND_CURRENT_PROFILE_INDEX, DYN_PWR_ENTER_INDEX(index), + ~DYN_PWR_ENTER_INDEX_MASK); +} + +void r600_wait_for_power_level_unequal(struct radeon_device *rdev, + enum r600_power_level index) +{ + int i; + + for (i = 0; i < rdev->usec_timeout; i++) { + if (r600_power_level_get_target_index(rdev) != index) + break; + udelay(1); + } + + for (i = 0; i < rdev->usec_timeout; i++) { + if (r600_power_level_get_current_index(rdev) != index) + break; + udelay(1); + } +} + +void r600_wait_for_power_level(struct radeon_device *rdev, + enum r600_power_level index) +{ + int i; + + for (i = 0; i < rdev->usec_timeout; i++) { + if (r600_power_level_get_target_index(rdev) == index) + break; + udelay(1); + } + + for (i = 0; i < rdev->usec_timeout; i++) { + if (r600_power_level_get_current_index(rdev) == index) + break; + udelay(1); + } +} + +void r600_start_dpm(struct radeon_device *rdev) +{ + r600_enable_sclk_control(rdev, false); + r600_enable_mclk_control(rdev, false); + + r600_dynamicpm_enable(rdev, true); + + radeon_wait_for_vblank(rdev, 0); + radeon_wait_for_vblank(rdev, 1); + + r600_enable_spll_bypass(rdev, true); + r600_wait_for_spll_change(rdev); + r600_enable_spll_bypass(rdev, false); + r600_wait_for_spll_change(rdev); + + r600_enable_spll_bypass(rdev, true); + r600_wait_for_spll_change(rdev); + r600_enable_spll_bypass(rdev, false); + r600_wait_for_spll_change(rdev); + + r600_enable_sclk_control(rdev, true); + r600_enable_mclk_control(rdev, true); +} + +void r600_stop_dpm(struct radeon_device *rdev) +{ + r600_dynamicpm_enable(rdev, false); +} + +bool r600_is_uvd_state(u32 class, u32 class2) +{ + if (class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE) + return true; + if (class & ATOM_PPLIB_CLASSIFICATION_HD2STATE) + return true; + if (class & ATOM_PPLIB_CLASSIFICATION_HDSTATE) + return true; + if (class & ATOM_PPLIB_CLASSIFICATION_SDSTATE) + return true; + if (class2 & ATOM_PPLIB_CLASSIFICATION2_MVC) + return true; + return false; +} diff --git a/drivers/gpu/drm/radeon/r600_dpm.h b/drivers/gpu/drm/radeon/r600_dpm.h new file mode 100644 index 0000000..240a7ed --- /dev/null +++ b/drivers/gpu/drm/radeon/r600_dpm.h @@ -0,0 +1,210 @@ +/* + * Copyright 2011 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ +#ifndef __R600_DPM_H__ +#define __R600_DPM_H__ + +#define R600_ASI_DFLT 10000 +#define R600_BSP_DFLT 0x41EB +#define R600_BSU_DFLT 0x2 +#define R600_AH_DFLT 5 +#define R600_RLP_DFLT 25 +#define R600_RMP_DFLT 65 +#define R600_LHP_DFLT 40 +#define R600_LMP_DFLT 15 +#define R600_TD_DFLT 0 +#define R600_UTC_DFLT_00 0x24 +#define R600_UTC_DFLT_01 0x22 +#define R600_UTC_DFLT_02 0x22 +#define R600_UTC_DFLT_03 0x22 +#define R600_UTC_DFLT_04 0x22 +#define R600_UTC_DFLT_05 0x22 +#define R600_UTC_DFLT_06 0x22 +#define R600_UTC_DFLT_07 0x22 +#define R600_UTC_DFLT_08 0x22 +#define R600_UTC_DFLT_09 0x22 +#define R600_UTC_DFLT_10 0x22 +#define R600_UTC_DFLT_11 0x22 +#define R600_UTC_DFLT_12 0x22 +#define R600_UTC_DFLT_13 0x22 +#define R600_UTC_DFLT_14 0x22 +#define R600_DTC_DFLT_00 0x24 +#define R600_DTC_DFLT_01 0x22 +#define R600_DTC_DFLT_02 0x22 +#define R600_DTC_DFLT_03 0x22 +#define R600_DTC_DFLT_04 0x22 +#define R600_DTC_DFLT_05 0x22 +#define R600_DTC_DFLT_06 0x22 +#define R600_DTC_DFLT_07 0x22 +#define R600_DTC_DFLT_08 0x22 +#define R600_DTC_DFLT_09 0x22 +#define R600_DTC_DFLT_10 0x22 +#define R600_DTC_DFLT_11 0x22 +#define R600_DTC_DFLT_12 0x22 +#define R600_DTC_DFLT_13 0x22 +#define R600_DTC_DFLT_14 0x22 +#define R600_VRC_DFLT 0x0000C003 +#define R600_VOLTAGERESPONSETIME_DFLT 1000 +#define R600_BACKBIASRESPONSETIME_DFLT 1000 +#define R600_VRU_DFLT 0x3 +#define R600_SPLLSTEPTIME_DFLT 0x1000 +#define R600_SPLLSTEPUNIT_DFLT 0x3 +#define R600_TPU_DFLT 0 +#define R600_TPC_DFLT 0x200 +#define R600_SSTU_DFLT 0 +#define R600_SST_DFLT 0x00C8 +#define R600_GICST_DFLT 0x200 +#define R600_FCT_DFLT 0x0400 +#define R600_FCTU_DFLT 0 +#define R600_CTXCGTT3DRPHC_DFLT 0x20 +#define R600_CTXCGTT3DRSDC_DFLT 0x40 +#define R600_VDDC3DOORPHC_DFLT 0x100 +#define R600_VDDC3DOORSDC_DFLT 0x7 +#define R600_VDDC3DOORSU_DFLT 0 +#define R600_MPLLLOCKTIME_DFLT 100 +#define R600_MPLLRESETTIME_DFLT 150 +#define R600_VCOSTEPPCT_DFLT 20 +#define R600_ENDINGVCOSTEPPCT_DFLT 5 +#define R600_REFERENCEDIVIDER_DFLT 4 + +#define R600_PM_NUMBER_OF_TC 15 +#define R600_PM_NUMBER_OF_SCLKS 20 +#define R600_PM_NUMBER_OF_MCLKS 4 +#define R600_PM_NUMBER_OF_VOLTAGE_LEVELS 4 +#define R600_PM_NUMBER_OF_ACTIVITY_LEVELS 3 + +enum r600_power_level { + R600_POWER_LEVEL_LOW = 0, + R600_POWER_LEVEL_MEDIUM = 1, + R600_POWER_LEVEL_HIGH = 2, + R600_POWER_LEVEL_CTXSW = 3, +}; + +enum r600_td { + R600_TD_AUTO, + R600_TD_UP, + R600_TD_DOWN, +}; + +enum r600_display_watermark { + R600_DISPLAY_WATERMARK_LOW = 0, + R600_DISPLAY_WATERMARK_HIGH = 1, +}; + +enum r600_display_gap +{ + R600_PM_DISPLAY_GAP_VBLANK_OR_WM = 0, + R600_PM_DISPLAY_GAP_VBLANK = 1, + R600_PM_DISPLAY_GAP_WATERMARK = 2, + R600_PM_DISPLAY_GAP_IGNORE = 3, +}; + +extern const u32 r600_utc[R600_PM_NUMBER_OF_TC]; +extern const u32 r600_dtc[R600_PM_NUMBER_OF_TC]; + +void r600_dpm_print_class_info(u32 class, u32 class2); +void r600_dpm_print_cap_info(u32 caps); +void r600_dpm_print_ps_status(struct radeon_device *rdev, + struct radeon_ps *rps); +bool r600_is_uvd_state(u32 class, u32 class2); +void r600_calculate_u_and_p(u32 i, u32 r_c, u32 p_b, + u32 *p, u32 *u); +int r600_calculate_at(u32 t, u32 h, u32 fh, u32 fl, u32 *tl, u32 *th); +void r600_gfx_clockgating_enable(struct radeon_device *rdev, bool enable); +void r600_dynamicpm_enable(struct radeon_device *rdev, bool enable); +void r600_enable_thermal_protection(struct radeon_device *rdev, bool enable); +void r600_enable_acpi_pm(struct radeon_device *rdev); +void r600_enable_dynamic_pcie_gen2(struct radeon_device *rdev, bool enable); +bool r600_dynamicpm_enabled(struct radeon_device *rdev); +void r600_enable_sclk_control(struct radeon_device *rdev, bool enable); +void r600_enable_mclk_control(struct radeon_device *rdev, bool enable); +void r600_enable_spll_bypass(struct radeon_device *rdev, bool enable); +void r600_wait_for_spll_change(struct radeon_device *rdev); +void r600_set_bsp(struct radeon_device *rdev, u32 u, u32 p); +void r600_set_at(struct radeon_device *rdev, + u32 l_to_m, u32 m_to_h, + u32 h_to_m, u32 m_to_l); +void r600_set_tc(struct radeon_device *rdev, u32 index, u32 u_t, u32 d_t); +void r600_select_td(struct radeon_device *rdev, enum r600_td td); +void r600_set_vrc(struct radeon_device *rdev, u32 vrv); +void r600_set_tpu(struct radeon_device *rdev, u32 u); +void r600_set_tpc(struct radeon_device *rdev, u32 c); +void r600_set_sstu(struct radeon_device *rdev, u32 u); +void r600_set_sst(struct radeon_device *rdev, u32 t); +void r600_set_git(struct radeon_device *rdev, u32 t); +void r600_set_fctu(struct radeon_device *rdev, u32 u); +void r600_set_fct(struct radeon_device *rdev, u32 t); +void r600_set_ctxcgtt3d_rphc(struct radeon_device *rdev, u32 p); +void r600_set_ctxcgtt3d_rsdc(struct radeon_device *rdev, u32 s); +void r600_set_vddc3d_oorsu(struct radeon_device *rdev, u32 u); +void r600_set_vddc3d_oorphc(struct radeon_device *rdev, u32 p); +void r600_set_vddc3d_oorsdc(struct radeon_device *rdev, u32 s); +void r600_set_mpll_lock_time(struct radeon_device *rdev, u32 lock_time); +void r600_set_mpll_reset_time(struct radeon_device *rdev, u32 reset_time); +void r600_engine_clock_entry_enable(struct radeon_device *rdev, + u32 index, bool enable); +void r600_engine_clock_entry_enable_pulse_skipping(struct radeon_device *rdev, + u32 index, bool enable); +void r600_engine_clock_entry_enable_post_divider(struct radeon_device *rdev, + u32 index, bool enable); +void r600_engine_clock_entry_set_post_divider(struct radeon_device *rdev, + u32 index, u32 divider); +void r600_engine_clock_entry_set_reference_divider(struct radeon_device *rdev, + u32 index, u32 divider); +void r600_engine_clock_entry_set_feedback_divider(struct radeon_device *rdev, + u32 index, u32 divider); +void r600_engine_clock_entry_set_step_time(struct radeon_device *rdev, + u32 index, u32 step_time); +void r600_vid_rt_set_ssu(struct radeon_device *rdev, u32 u); +void r600_vid_rt_set_vru(struct radeon_device *rdev, u32 u); +void r600_vid_rt_set_vrt(struct radeon_device *rdev, u32 rt); +void r600_voltage_control_enable_pins(struct radeon_device *rdev, + u64 mask); +void r600_voltage_control_program_voltages(struct radeon_device *rdev, + enum r600_power_level index, u64 pins); +void r600_voltage_control_deactivate_static_control(struct radeon_device *rdev, + u64 mask); +void r600_power_level_enable(struct radeon_device *rdev, + enum r600_power_level index, bool enable); +void r600_power_level_set_voltage_index(struct radeon_device *rdev, + enum r600_power_level index, u32 voltage_index); +void r600_power_level_set_mem_clock_index(struct radeon_device *rdev, + enum r600_power_level index, u32 mem_clock_index); +void r600_power_level_set_eng_clock_index(struct radeon_device *rdev, + enum r600_power_level index, u32 eng_clock_index); +void r600_power_level_set_watermark_id(struct radeon_device *rdev, + enum r600_power_level index, + enum r600_display_watermark watermark_id); +void r600_power_level_set_pcie_gen2(struct radeon_device *rdev, + enum r600_power_level index, bool compatible); +enum r600_power_level r600_power_level_get_current_index(struct radeon_device *rdev); +enum r600_power_level r600_power_level_get_target_index(struct radeon_device *rdev); +void r600_power_level_set_enter_index(struct radeon_device *rdev, + enum r600_power_level index); +void r600_wait_for_power_level_unequal(struct radeon_device *rdev, + enum r600_power_level index); +void r600_wait_for_power_level(struct radeon_device *rdev, + enum r600_power_level index); +void r600_start_dpm(struct radeon_device *rdev); +void r600_stop_dpm(struct radeon_device *rdev); + +#endif diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h index a3f926c..d6d385a 100644 --- a/drivers/gpu/drm/radeon/r600d.h +++ b/drivers/gpu/drm/radeon/r600d.h @@ -1144,6 +1144,219 @@ # define AFMT_AZ_FORMAT_WTRIG_ACK (1 << 29) # define AFMT_AZ_AUDIO_ENABLE_CHG_ACK (1 << 30)
+/* Power management */ +#define CG_SPLL_FUNC_CNTL 0x600 +# define SPLL_RESET (1 << 0) +# define SPLL_SLEEP (1 << 1) +# define SPLL_REF_DIV(x) ((x) << 2) +# define SPLL_REF_DIV_MASK (7 << 2) +# define SPLL_FB_DIV(x) ((x) << 5) +# define SPLL_FB_DIV_MASK (0xff << 5) +# define SPLL_PULSEEN (1 << 13) +# define SPLL_PULSENUM(x) ((x) << 14) +# define SPLL_PULSENUM_MASK (3 << 14) +# define SPLL_SW_HILEN(x) ((x) << 16) +# define SPLL_SW_HILEN_MASK (0xf << 16) +# define SPLL_SW_LOLEN(x) ((x) << 20) +# define SPLL_SW_LOLEN_MASK (0xf << 20) +# define SPLL_DIVEN (1 << 24) +# define SPLL_BYPASS_EN (1 << 25) +# define SPLL_CHG_STATUS (1 << 29) +# define SPLL_CTLREQ (1 << 30) +# define SPLL_CTLACK (1 << 31) + +#define GENERAL_PWRMGT 0x618 +# define GLOBAL_PWRMGT_EN (1 << 0) +# define STATIC_PM_EN (1 << 1) +# define MOBILE_SU (1 << 2) +# define THERMAL_PROTECTION_DIS (1 << 3) +# define THERMAL_PROTECTION_TYPE (1 << 4) +# define ENABLE_GEN2PCIE (1 << 5) +# define SW_GPIO_INDEX(x) ((x) << 6) +# define SW_GPIO_INDEX_MASK (3 << 6) +# define LOW_VOLT_D2_ACPI (1 << 8) +# define LOW_VOLT_D3_ACPI (1 << 9) +# define VOLT_PWRMGT_EN (1 << 10) +#define CG_TPC 0x61c +# define TPCC(x) ((x) << 0) +# define TPCC_MASK (0x7fffff << 0) +# define TPU(x) ((x) << 23) +# define TPU_MASK (0x1f << 23) +#define SCLK_PWRMGT_CNTL 0x620 +# define SCLK_PWRMGT_OFF (1 << 0) +# define SCLK_TURNOFF (1 << 1) +# define SPLL_TURNOFF (1 << 2) +# define SU_SCLK_USE_BCLK (1 << 3) +# define DYNAMIC_GFX_ISLAND_PWR_DOWN (1 << 4) +# define DYNAMIC_GFX_ISLAND_PWR_LP (1 << 5) +# define CLK_TURN_ON_STAGGER (1 << 6) +# define CLK_TURN_OFF_STAGGER (1 << 7) +# define FIR_FORCE_TREND_SEL (1 << 8) +# define FIR_TREND_MODE (1 << 9) +# define DYN_GFX_CLK_OFF_EN (1 << 10) +# define VDDC3D_TURNOFF_D1 (1 << 11) +# define VDDC3D_TURNOFF_D2 (1 << 12) +# define VDDC3D_TURNOFF_D3 (1 << 13) +# define SPLL_TURNOFF_D2 (1 << 14) +# define SCLK_LOW_D1 (1 << 15) +# define DYN_GFX_CLK_OFF_MC_EN (1 << 16) +#define MCLK_PWRMGT_CNTL 0x624 +# define MPLL_PWRMGT_OFF (1 << 0) +# define YCLK_TURNOFF (1 << 1) +# define MPLL_TURNOFF (1 << 2) +# define SU_MCLK_USE_BCLK (1 << 3) +# define DLL_READY (1 << 4) +# define MC_BUSY (1 << 5) +# define MC_INT_CNTL (1 << 7) +# define MRDCKA_SLEEP (1 << 8) +# define MRDCKB_SLEEP (1 << 9) +# define MRDCKC_SLEEP (1 << 10) +# define MRDCKD_SLEEP (1 << 11) +# define MRDCKE_SLEEP (1 << 12) +# define MRDCKF_SLEEP (1 << 13) +# define MRDCKG_SLEEP (1 << 14) +# define MRDCKH_SLEEP (1 << 15) +# define MRDCKA_RESET (1 << 16) +# define MRDCKB_RESET (1 << 17) +# define MRDCKC_RESET (1 << 18) +# define MRDCKD_RESET (1 << 19) +# define MRDCKE_RESET (1 << 20) +# define MRDCKF_RESET (1 << 21) +# define MRDCKG_RESET (1 << 22) +# define MRDCKH_RESET (1 << 23) +# define DLL_READY_READ (1 << 24) +# define USE_DISPLAY_GAP (1 << 25) +# define USE_DISPLAY_URGENT_NORMAL (1 << 26) +# define USE_DISPLAY_GAP_CTXSW (1 << 27) +# define MPLL_TURNOFF_D2 (1 << 28) +# define USE_DISPLAY_URGENT_CTXSW (1 << 29) + +#define MPLL_TIME 0x634 +# define MPLL_LOCK_TIME(x) ((x) << 0) +# define MPLL_LOCK_TIME_MASK (0xffff << 0) +# define MPLL_RESET_TIME(x) ((x) << 16) +# define MPLL_RESET_TIME_MASK (0xffff << 16) + +#define SCLK_FREQ_SETTING_STEP_0_PART1 0x648 +# define STEP_0_SPLL_POST_DIV(x) ((x) << 0) +# define STEP_0_SPLL_POST_DIV_MASK (0xff << 0) +# define STEP_0_SPLL_FB_DIV(x) ((x) << 8) +# define STEP_0_SPLL_FB_DIV_MASK (0xff << 8) +# define STEP_0_SPLL_REF_DIV(x) ((x) << 16) +# define STEP_0_SPLL_REF_DIV_MASK (7 << 16) +# define STEP_0_SPLL_STEP_TIME(x) ((x) << 19) +# define STEP_0_SPLL_STEP_TIME_MASK (0x1fff << 19) +#define SCLK_FREQ_SETTING_STEP_0_PART2 0x64c +# define STEP_0_PULSE_HIGH_CNT(x) ((x) << 0) +# define STEP_0_PULSE_HIGH_CNT_MASK (0x1ff << 0) +# define STEP_0_POST_DIV_EN (1 << 9) +# define STEP_0_SPLL_STEP_ENABLE (1 << 30) +# define STEP_0_SPLL_ENTRY_VALID (1 << 31) + +#define VID_RT 0x6f8 +# define VID_CRT(x) ((x) << 0) +# define VID_CRT_MASK (0x1fff << 0) +# define VID_CRTU(x) ((x) << 13) +# define VID_CRTU_MASK (7 << 13) +# define SSTU(x) ((x) << 16) +# define SSTU_MASK (7 << 16) +#define CTXSW_PROFILE_INDEX 0x6fc +# define CTXSW_FREQ_VIDS_CFG_INDEX(x) ((x) << 0) +# define CTXSW_FREQ_VIDS_CFG_INDEX_MASK (3 << 0) +# define CTXSW_FREQ_VIDS_CFG_INDEX_SHIFT 0 +# define CTXSW_FREQ_MCLK_CFG_INDEX(x) ((x) << 2) +# define CTXSW_FREQ_MCLK_CFG_INDEX_MASK (3 << 2) +# define CTXSW_FREQ_MCLK_CFG_INDEX_SHIFT 2 +# define CTXSW_FREQ_SCLK_CFG_INDEX(x) ((x) << 4) +# define CTXSW_FREQ_SCLK_CFG_INDEX_MASK (0x1f << 4) +# define CTXSW_FREQ_SCLK_CFG_INDEX_SHIFT 4 +# define CTXSW_FREQ_STATE_SPLL_RESET_EN (1 << 9) +# define CTXSW_FREQ_STATE_ENABLE (1 << 10) +# define CTXSW_FREQ_DISPLAY_WATERMARK (1 << 11) +# define CTXSW_FREQ_GEN2PCIE_VOLT (1 << 12) + +#define TARGET_AND_CURRENT_PROFILE_INDEX 0x70c +# define TARGET_PROFILE_INDEX_MASK (3 << 0) +# define TARGET_PROFILE_INDEX_SHIFT 0 +# define CURRENT_PROFILE_INDEX_MASK (3 << 2) +# define CURRENT_PROFILE_INDEX_SHIFT 2 +# define DYN_PWR_ENTER_INDEX(x) ((x) << 4) +# define DYN_PWR_ENTER_INDEX_MASK (3 << 4) +# define DYN_PWR_ENTER_INDEX_SHIFT 4 +# define CURR_MCLK_INDEX_MASK (3 << 6) +# define CURR_MCLK_INDEX_SHIFT 6 +# define CURR_SCLK_INDEX_MASK (0x1f << 8) +# define CURR_SCLK_INDEX_SHIFT 8 +# define CURR_VID_INDEX_MASK (3 << 13) +# define CURR_VID_INDEX_SHIFT 13 + +#define LOWER_GPIO_ENABLE 0x710 +#define UPPER_GPIO_ENABLE 0x714 +#define CTXSW_VID_LOWER_GPIO_CNTL 0x718 + +#define VID_UPPER_GPIO_CNTL 0x740 +#define CG_CTX_CGTT3D_R 0x744 +# define PHC(x) ((x) << 0) +# define PHC_MASK (0x1ff << 0) +# define SDC(x) ((x) << 9) +# define SDC_MASK (0x3fff << 9) +#define CG_VDDC3D_OOR 0x748 +# define SU(x) ((x) << 23) +# define SU_MASK (0xf << 23) +#define CG_FTV 0x74c +#define CG_FFCT_0 0x750 +# define UTC_0(x) ((x) << 0) +# define UTC_0_MASK (0x3ff << 0) +# define DTC_0(x) ((x) << 10) +# define DTC_0_MASK (0x3ff << 10) + +#define CG_BSP 0x78c +# define BSP(x) ((x) << 0) +# define BSP_MASK (0xffff << 0) +# define BSU(x) ((x) << 16) +# define BSU_MASK (0xf << 16) +#define CG_RT 0x790 +# define FLS(x) ((x) << 0) +# define FLS_MASK (0xffff << 0) +# define FMS(x) ((x) << 16) +# define FMS_MASK (0xffff << 16) +#define CG_LT 0x794 +# define FHS(x) ((x) << 0) +# define FHS_MASK (0xffff << 0) +#define CG_GIT 0x798 +# define CG_GICST(x) ((x) << 0) +# define CG_GICST_MASK (0xffff << 0) +# define CG_GIPOT(x) ((x) << 16) +# define CG_GIPOT_MASK (0xffff << 16) + +#define CG_SSP 0x7a8 +# define CG_SST(x) ((x) << 0) +# define CG_SST_MASK (0xffff << 0) +# define CG_SSTU(x) ((x) << 16) +# define CG_SSTU_MASK (0xf << 16) + +#define CG_RLC_REQ_AND_RSP 0x7c4 +# define RLC_CG_REQ_TYPE_MASK 0xf +# define RLC_CG_REQ_TYPE_SHIFT 0 +# define CG_RLC_RSP_TYPE_MASK 0xf0 +# define CG_RLC_RSP_TYPE_SHIFT 4 + +#define CG_FC_T 0x7cc +# define FC_T(x) ((x) << 0) +# define FC_T_MASK (0xffff << 0) +# define FC_TU(x) ((x) << 16) +# define FC_TU_MASK (0x1f << 16) + +#define GPIOPAD_MASK 0x1798 +#define GPIOPAD_A 0x179c +#define GPIOPAD_EN 0x17a0 + +#define GRBM_PWR_CNTL 0x800c +# define REQ_TYPE_MASK 0xf +# define REQ_TYPE_SHIFT 0 +# define RSP_TYPE_MASK 0xf0 +# define RSP_TYPE_SHIFT 4 + /* * UVD */ diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index c43673c..7adc7a1 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1184,6 +1184,19 @@ struct radeon_power_state { */ #define RADEON_MODE_OVERCLOCK_MARGIN 500 /* 5 MHz */
+enum radeon_dpm_auto_throttle_src { + RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL, + RADEON_DPM_AUTO_THROTTLE_SRC_EXTERNAL +}; + +enum radeon_dpm_event_src { + RADEON_DPM_EVENT_SRC_ANALOG = 0, + RADEON_DPM_EVENT_SRC_EXTERNAL = 1, + RADEON_DPM_EVENT_SRC_DIGITAL = 2, + RADEON_DPM_EVENT_SRC_ANALOG_OR_EXTERNAL = 3, + RADEON_DPM_EVENT_SRC_DIGIAL_OR_EXTERNAL = 4 +}; + struct radeon_ps { u32 caps; /* vbios flags */ u32 class; /* vbios flags */
From: Alex Deucher alexander.deucher@amd.com
This adds dpm support for rs780/rs880 asics. This includes: - clockgating - dynamic engine clock scaling - dynamic voltage scaling
set radeon.dpm=1 to enable it.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/Makefile | 2 +- drivers/gpu/drm/radeon/radeon_asic.c | 12 + drivers/gpu/drm/radeon/radeon_asic.h | 12 + drivers/gpu/drm/radeon/radeon_pm.c | 7 + drivers/gpu/drm/radeon/rs780_dpm.c | 894 ++++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/rs780_dpm.h | 109 ++++ drivers/gpu/drm/radeon/rs780d.h | 168 +++++++ 7 files changed, 1203 insertions(+), 1 deletions(-) create mode 100644 drivers/gpu/drm/radeon/rs780_dpm.c create mode 100644 drivers/gpu/drm/radeon/rs780_dpm.h create mode 100644 drivers/gpu/drm/radeon/rs780d.h
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index a131a13..e44b046 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile @@ -77,7 +77,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \ evergreen_hdmi.o radeon_trace_points.o ni.o cayman_blit_shaders.o \ atombios_encoders.o radeon_semaphore.o radeon_sa.o atombios_i2c.o si.o \ si_blit_shaders.o radeon_prime.o radeon_uvd.o cik.o cik_blit_shaders.o \ - r600_dpm.o + r600_dpm.o rs780_dpm.o
radeon-$(CONFIG_COMPAT) += radeon_ioc32.o radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index d9c8e9a..db3c930 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1194,6 +1194,18 @@ static struct radeon_asic rs780_asic = { .set_clock_gating = NULL, .get_temperature = &rv6xx_get_temp, }, + .dpm = { + .init = &rs780_dpm_init, + .setup_asic = &rs780_dpm_setup_asic, + .enable = &rs780_dpm_enable, + .disable = &rs780_dpm_disable, + .set_power_state = &rs780_dpm_set_power_state, + .display_configuration_changed = &rs780_dpm_display_configuration_changed, + .fini = &rs780_dpm_fini, + .get_sclk = &rs780_dpm_get_sclk, + .get_mclk = &rs780_dpm_get_mclk, + .print_power_state = &rs780_dpm_print_power_state, + }, .pflip = { .pre_page_flip = &rs600_pre_page_flip, .page_flip = &rs600_page_flip, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 8507cae..134bf57 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -396,6 +396,18 @@ int r600_mc_wait_for_idle(struct radeon_device *rdev); u32 r600_get_xclk(struct radeon_device *rdev); uint64_t r600_get_gpu_clock_counter(struct radeon_device *rdev); int rv6xx_get_temp(struct radeon_device *rdev); +/* rs780 dpm */ +int rs780_dpm_init(struct radeon_device *rdev); +int rs780_dpm_enable(struct radeon_device *rdev); +void rs780_dpm_disable(struct radeon_device *rdev); +int rs780_dpm_set_power_state(struct radeon_device *rdev); +void rs780_dpm_setup_asic(struct radeon_device *rdev); +void rs780_dpm_display_configuration_changed(struct radeon_device *rdev); +void rs780_dpm_fini(struct radeon_device *rdev); +u32 rs780_dpm_get_sclk(struct radeon_device *rdev, bool low); +u32 rs780_dpm_get_mclk(struct radeon_device *rdev, bool low); +void rs780_dpm_print_power_state(struct radeon_device *rdev, + struct radeon_ps *ps);
/* uvd */ int r600_uvd_init(struct radeon_device *rdev); diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 4f5422e..853a8a2 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -1030,6 +1030,13 @@ int radeon_pm_init(struct radeon_device *rdev) { /* enable dpm on rv6xx+ */ switch (rdev->family) { + case CHIP_RS780: + case CHIP_RS880: + if (radeon_dpm == 1) + rdev->pm.pm_method = PM_METHOD_DPM; + else + rdev->pm.pm_method = PM_METHOD_PROFILE; + break; default: /* default to profile method */ rdev->pm.pm_method = PM_METHOD_PROFILE; diff --git a/drivers/gpu/drm/radeon/rs780_dpm.c b/drivers/gpu/drm/radeon/rs780_dpm.c new file mode 100644 index 0000000..f594900 --- /dev/null +++ b/drivers/gpu/drm/radeon/rs780_dpm.c @@ -0,0 +1,894 @@ +/* + * Copyright 2011 Advanced Micro Devices, 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: Alex Deucher + */ + +#include "drmP.h" +#include "radeon.h" +#include "rs780d.h" +#include "r600_dpm.h" +#include "rs780_dpm.h" +#include "atom.h" + +static struct igp_ps *rs780_get_ps(struct radeon_ps *rps) +{ + struct igp_ps *ps = rps->ps_priv; + + return ps; +} + +static struct igp_power_info *rs780_get_pi(struct radeon_device *rdev) +{ + struct igp_power_info *pi = rdev->pm.dpm.priv; + + return pi; +} + +static void rs780_get_pm_mode_parameters(struct radeon_device *rdev) +{ + struct igp_power_info *pi = rs780_get_pi(rdev); + struct radeon_mode_info *minfo = &rdev->mode_info; + struct drm_crtc *crtc; + struct radeon_crtc *radeon_crtc; + int i; + + /* defaults */ + pi->crtc_id = 0; + pi->refresh_rate = 60; + + for (i = 0; i < rdev->num_crtc; i++) { + crtc = (struct drm_crtc *)minfo->crtcs[i]; + if (crtc && crtc->enabled) { + radeon_crtc = to_radeon_crtc(crtc); + pi->crtc_id = radeon_crtc->crtc_id; + if (crtc->mode.htotal && crtc->mode.vtotal) + pi->refresh_rate = + (crtc->mode.clock * 1000) / + (crtc->mode.htotal * crtc->mode.vtotal); + break; + } + } +} + +static void rs780_voltage_scaling_enable(struct radeon_device *rdev, bool enable); + +static int rs780_initialize_dpm_power_state(struct radeon_device *rdev) +{ + struct atom_clock_dividers dividers; + struct igp_ps *default_state = rs780_get_ps(rdev->pm.dpm.boot_ps); + int i, ret; + + ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM, + default_state->sclk_low, false, ÷rs); + if (ret) + return ret; + + r600_engine_clock_entry_set_reference_divider(rdev, 0, dividers.ref_div); + r600_engine_clock_entry_set_feedback_divider(rdev, 0, dividers.fb_div); + r600_engine_clock_entry_set_post_divider(rdev, 0, dividers.post_div); + + if (dividers.enable_post_div) + r600_engine_clock_entry_enable_post_divider(rdev, 0, true); + else + r600_engine_clock_entry_enable_post_divider(rdev, 0, false); + + r600_engine_clock_entry_set_step_time(rdev, 0, R600_SST_DFLT); + r600_engine_clock_entry_enable_pulse_skipping(rdev, 0, false); + + r600_engine_clock_entry_enable(rdev, 0, true); + for (i = 1; i < R600_PM_NUMBER_OF_SCLKS; i++) + r600_engine_clock_entry_enable(rdev, i, false); + + r600_enable_mclk_control(rdev, false); + r600_voltage_control_enable_pins(rdev, 0); + + return 0; +} + +static int rs780_initialize_dpm_parameters(struct radeon_device *rdev) +{ + int ret = 0; + int i; + + r600_set_bsp(rdev, R600_BSU_DFLT, R600_BSP_DFLT); + + r600_set_at(rdev, 0, 0, 0, 0); + + r600_set_git(rdev, R600_GICST_DFLT); + + for (i = 0; i < R600_PM_NUMBER_OF_TC; i++) + r600_set_tc(rdev, i, 0, 0); + + r600_select_td(rdev, R600_TD_DFLT); + r600_set_vrc(rdev, 0); + + r600_set_tpu(rdev, R600_TPU_DFLT); + r600_set_tpc(rdev, R600_TPC_DFLT); + + r600_set_sstu(rdev, R600_SSTU_DFLT); + r600_set_sst(rdev, R600_SST_DFLT); + + r600_set_fctu(rdev, R600_FCTU_DFLT); + r600_set_fct(rdev, R600_FCT_DFLT); + + r600_set_vddc3d_oorsu(rdev, R600_VDDC3DOORSU_DFLT); + r600_set_vddc3d_oorphc(rdev, R600_VDDC3DOORPHC_DFLT); + r600_set_vddc3d_oorsdc(rdev, R600_VDDC3DOORSDC_DFLT); + r600_set_ctxcgtt3d_rphc(rdev, R600_CTXCGTT3DRPHC_DFLT); + r600_set_ctxcgtt3d_rsdc(rdev, R600_CTXCGTT3DRSDC_DFLT); + + r600_vid_rt_set_vru(rdev, R600_VRU_DFLT); + r600_vid_rt_set_vrt(rdev, R600_VOLTAGERESPONSETIME_DFLT); + r600_vid_rt_set_ssu(rdev, R600_SPLLSTEPUNIT_DFLT); + + ret = rs780_initialize_dpm_power_state(rdev); + + r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_LOW, 0); + r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_MEDIUM, 0); + r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_HIGH, 0); + + r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_LOW, 0); + r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_MEDIUM, 0); + r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_HIGH, 0); + + r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_LOW, 0); + r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_MEDIUM, 0); + r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_HIGH, 0); + + r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_LOW, R600_DISPLAY_WATERMARK_HIGH); + r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_MEDIUM, R600_DISPLAY_WATERMARK_HIGH); + r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_HIGH, R600_DISPLAY_WATERMARK_HIGH); + + r600_power_level_enable(rdev, R600_POWER_LEVEL_CTXSW, false); + r600_power_level_enable(rdev, R600_POWER_LEVEL_HIGH, false); + r600_power_level_enable(rdev, R600_POWER_LEVEL_MEDIUM, false); + r600_power_level_enable(rdev, R600_POWER_LEVEL_LOW, true); + + r600_power_level_set_enter_index(rdev, R600_POWER_LEVEL_LOW); + + r600_set_vrc(rdev, RS780_CGFTV_DFLT); + + return ret; +} + +static void rs780_start_dpm(struct radeon_device *rdev) +{ + r600_enable_sclk_control(rdev, false); + r600_enable_mclk_control(rdev, false); + + r600_dynamicpm_enable(rdev, true); + + radeon_wait_for_vblank(rdev, 0); + radeon_wait_for_vblank(rdev, 1); + + r600_enable_spll_bypass(rdev, true); + r600_wait_for_spll_change(rdev); + r600_enable_spll_bypass(rdev, false); + r600_wait_for_spll_change(rdev); + + r600_enable_spll_bypass(rdev, true); + r600_wait_for_spll_change(rdev); + r600_enable_spll_bypass(rdev, false); + r600_wait_for_spll_change(rdev); + + r600_enable_sclk_control(rdev, true); +} + + +static void rs780_preset_ranges_slow_clk_fbdiv_en(struct radeon_device *rdev) +{ + WREG32_P(FVTHROT_SLOW_CLK_FEEDBACK_DIV_REG1, RANGE_SLOW_CLK_FEEDBACK_DIV_EN, + ~RANGE_SLOW_CLK_FEEDBACK_DIV_EN); + + WREG32_P(FVTHROT_SLOW_CLK_FEEDBACK_DIV_REG1, + RANGE0_SLOW_CLK_FEEDBACK_DIV(RS780_SLOWCLKFEEDBACKDIV_DFLT), + ~RANGE0_SLOW_CLK_FEEDBACK_DIV_MASK); +} + +static void rs780_preset_starting_fbdiv(struct radeon_device *rdev) +{ + u32 fbdiv = (RREG32(CG_SPLL_FUNC_CNTL) & SPLL_FB_DIV_MASK) >> SPLL_FB_DIV_SHIFT; + + WREG32_P(FVTHROT_FBDIV_REG1, STARTING_FEEDBACK_DIV(fbdiv), + ~STARTING_FEEDBACK_DIV_MASK); + + WREG32_P(FVTHROT_FBDIV_REG2, FORCED_FEEDBACK_DIV(fbdiv), + ~FORCED_FEEDBACK_DIV_MASK); + + WREG32_P(FVTHROT_FBDIV_REG1, FORCE_FEEDBACK_DIV, ~FORCE_FEEDBACK_DIV); +} + +static void rs780_voltage_scaling_init(struct radeon_device *rdev) +{ + struct igp_power_info *pi = rs780_get_pi(rdev); + struct drm_device *dev = rdev->ddev; + u32 fv_throt_pwm_fb_div_range[3]; + u32 fv_throt_pwm_range[4]; + + if (dev->pdev->device == 0x9614) { + fv_throt_pwm_fb_div_range[0] = RS780D_FVTHROTPWMFBDIVRANGEREG0_DFLT; + fv_throt_pwm_fb_div_range[1] = RS780D_FVTHROTPWMFBDIVRANGEREG1_DFLT; + fv_throt_pwm_fb_div_range[2] = RS780D_FVTHROTPWMFBDIVRANGEREG2_DFLT; + } else if ((dev->pdev->device == 0x9714) || + (dev->pdev->device == 0x9715)) { + fv_throt_pwm_fb_div_range[0] = RS880D_FVTHROTPWMFBDIVRANGEREG0_DFLT; + fv_throt_pwm_fb_div_range[1] = RS880D_FVTHROTPWMFBDIVRANGEREG1_DFLT; + fv_throt_pwm_fb_div_range[2] = RS880D_FVTHROTPWMFBDIVRANGEREG2_DFLT; + } else { + fv_throt_pwm_fb_div_range[0] = RS780_FVTHROTPWMFBDIVRANGEREG0_DFLT; + fv_throt_pwm_fb_div_range[1] = RS780_FVTHROTPWMFBDIVRANGEREG1_DFLT; + fv_throt_pwm_fb_div_range[2] = RS780_FVTHROTPWMFBDIVRANGEREG2_DFLT; + } + + if (pi->pwm_voltage_control) { + fv_throt_pwm_range[0] = pi->min_voltage; + fv_throt_pwm_range[1] = pi->min_voltage; + fv_throt_pwm_range[2] = pi->max_voltage; + fv_throt_pwm_range[3] = pi->max_voltage; + } else { + fv_throt_pwm_range[0] = pi->invert_pwm_required ? + RS780_FVTHROTPWMRANGE3_GPIO_DFLT : RS780_FVTHROTPWMRANGE0_GPIO_DFLT; + fv_throt_pwm_range[1] = pi->invert_pwm_required ? + RS780_FVTHROTPWMRANGE2_GPIO_DFLT : RS780_FVTHROTPWMRANGE1_GPIO_DFLT; + fv_throt_pwm_range[2] = pi->invert_pwm_required ? + RS780_FVTHROTPWMRANGE1_GPIO_DFLT : RS780_FVTHROTPWMRANGE2_GPIO_DFLT; + fv_throt_pwm_range[3] = pi->invert_pwm_required ? + RS780_FVTHROTPWMRANGE0_GPIO_DFLT : RS780_FVTHROTPWMRANGE3_GPIO_DFLT; + } + + WREG32_P(FVTHROT_PWM_CTRL_REG0, + STARTING_PWM_HIGHTIME(pi->max_voltage), + ~STARTING_PWM_HIGHTIME_MASK); + + WREG32_P(FVTHROT_PWM_CTRL_REG0, + NUMBER_OF_CYCLES_IN_PERIOD(pi->num_of_cycles_in_period), + ~NUMBER_OF_CYCLES_IN_PERIOD_MASK); + + WREG32_P(FVTHROT_PWM_CTRL_REG0, FORCE_STARTING_PWM_HIGHTIME, + ~FORCE_STARTING_PWM_HIGHTIME); + + if (pi->invert_pwm_required) + WREG32_P(FVTHROT_PWM_CTRL_REG0, INVERT_PWM_WAVEFORM, ~INVERT_PWM_WAVEFORM); + else + WREG32_P(FVTHROT_PWM_CTRL_REG0, 0, ~INVERT_PWM_WAVEFORM); + + rs780_voltage_scaling_enable(rdev, true); + + WREG32(FVTHROT_PWM_CTRL_REG1, + (MIN_PWM_HIGHTIME(pi->min_voltage) | + MAX_PWM_HIGHTIME(pi->max_voltage))); + + WREG32(FVTHROT_PWM_US_REG0, RS780_FVTHROTPWMUSREG0_DFLT); + WREG32(FVTHROT_PWM_US_REG1, RS780_FVTHROTPWMUSREG1_DFLT); + WREG32(FVTHROT_PWM_DS_REG0, RS780_FVTHROTPWMDSREG0_DFLT); + WREG32(FVTHROT_PWM_DS_REG1, RS780_FVTHROTPWMDSREG1_DFLT); + + WREG32_P(FVTHROT_PWM_FEEDBACK_DIV_REG1, + RANGE0_PWM_FEEDBACK_DIV(fv_throt_pwm_fb_div_range[0]), + ~RANGE0_PWM_FEEDBACK_DIV_MASK); + + WREG32(FVTHROT_PWM_FEEDBACK_DIV_REG2, + (RANGE1_PWM_FEEDBACK_DIV(fv_throt_pwm_fb_div_range[1]) | + RANGE2_PWM_FEEDBACK_DIV(fv_throt_pwm_fb_div_range[2]))); + + WREG32(FVTHROT_PWM_FEEDBACK_DIV_REG3, + (RANGE0_PWM(fv_throt_pwm_range[1]) | + RANGE1_PWM(fv_throt_pwm_range[2]))); + WREG32(FVTHROT_PWM_FEEDBACK_DIV_REG4, + (RANGE2_PWM(fv_throt_pwm_range[1]) | + RANGE3_PWM(fv_throt_pwm_range[2]))); +} + +static void rs780_clk_scaling_enable(struct radeon_device *rdev, bool enable) +{ + if (enable) + WREG32_P(FVTHROT_CNTRL_REG, ENABLE_FV_THROT | ENABLE_FV_UPDATE, + ~(ENABLE_FV_THROT | ENABLE_FV_UPDATE)); + else + WREG32_P(FVTHROT_CNTRL_REG, 0, + ~(ENABLE_FV_THROT | ENABLE_FV_UPDATE)); +} + +static void rs780_voltage_scaling_enable(struct radeon_device *rdev, bool enable) +{ + if (enable) + WREG32_P(FVTHROT_CNTRL_REG, ENABLE_FV_THROT_IO, ~ENABLE_FV_THROT_IO); + else + WREG32_P(FVTHROT_CNTRL_REG, 0, ~ENABLE_FV_THROT_IO); +} + +static void rs780_set_engine_clock_wfc(struct radeon_device *rdev) +{ + WREG32(FVTHROT_UTC0, RS780_FVTHROTUTC0_DFLT); + WREG32(FVTHROT_UTC1, RS780_FVTHROTUTC1_DFLT); + WREG32(FVTHROT_UTC2, RS780_FVTHROTUTC2_DFLT); + WREG32(FVTHROT_UTC3, RS780_FVTHROTUTC3_DFLT); + WREG32(FVTHROT_UTC4, RS780_FVTHROTUTC4_DFLT); + + WREG32(FVTHROT_DTC0, RS780_FVTHROTDTC0_DFLT); + WREG32(FVTHROT_DTC1, RS780_FVTHROTDTC1_DFLT); + WREG32(FVTHROT_DTC2, RS780_FVTHROTDTC2_DFLT); + WREG32(FVTHROT_DTC3, RS780_FVTHROTDTC3_DFLT); + WREG32(FVTHROT_DTC4, RS780_FVTHROTDTC4_DFLT); +} + +static void rs780_set_engine_clock_sc(struct radeon_device *rdev) +{ + WREG32_P(FVTHROT_FBDIV_REG2, + FB_DIV_TIMER_VAL(RS780_FBDIVTIMERVAL_DFLT), + ~FB_DIV_TIMER_VAL_MASK); + + WREG32_P(FVTHROT_CNTRL_REG, + REFRESH_RATE_DIVISOR(0) | MINIMUM_CIP(0xf), + ~(REFRESH_RATE_DIVISOR_MASK | MINIMUM_CIP_MASK)); +} + +static void rs780_set_engine_clock_tdc(struct radeon_device *rdev) +{ + WREG32_P(FVTHROT_CNTRL_REG, 0, ~(FORCE_TREND_SEL | TREND_SEL_MODE)); +} + +static void rs780_set_engine_clock_ssc(struct radeon_device *rdev) +{ + WREG32(FVTHROT_FB_US_REG0, RS780_FVTHROTFBUSREG0_DFLT); + WREG32(FVTHROT_FB_US_REG1, RS780_FVTHROTFBUSREG1_DFLT); + WREG32(FVTHROT_FB_DS_REG0, RS780_FVTHROTFBDSREG0_DFLT); + WREG32(FVTHROT_FB_DS_REG1, RS780_FVTHROTFBDSREG1_DFLT); + + WREG32_P(FVTHROT_FBDIV_REG1, MAX_FEEDBACK_STEP(1), ~MAX_FEEDBACK_STEP_MASK); +} + +static void rs780_program_at(struct radeon_device *rdev) +{ + struct igp_power_info *pi = rs780_get_pi(rdev); + + WREG32(FVTHROT_TARGET_REG, 30000000 / pi->refresh_rate); + WREG32(FVTHROT_CB1, 1000000 * 5 / pi->refresh_rate); + WREG32(FVTHROT_CB2, 1000000 * 10 / pi->refresh_rate); + WREG32(FVTHROT_CB3, 1000000 * 30 / pi->refresh_rate); + WREG32(FVTHROT_CB4, 1000000 * 50 / pi->refresh_rate); +} + +static void rs780_disable_vbios_powersaving(struct radeon_device *rdev) +{ + WREG32_P(CG_INTGFX_MISC, 0, ~0xFFF00000); +} + +static void rs780_force_voltage_to_high(struct radeon_device *rdev) +{ + struct igp_power_info *pi = rs780_get_pi(rdev); + struct igp_ps *current_state = rs780_get_ps(rdev->pm.dpm.current_ps); + + if ((current_state->max_voltage == RS780_VDDC_LEVEL_HIGH) && + (current_state->min_voltage == RS780_VDDC_LEVEL_HIGH)) + return; + + WREG32_P(GFX_MACRO_BYPASS_CNTL, SPLL_BYPASS_CNTL, ~SPLL_BYPASS_CNTL); + + udelay(1); + + WREG32_P(FVTHROT_PWM_CTRL_REG0, + STARTING_PWM_HIGHTIME(pi->max_voltage), + ~STARTING_PWM_HIGHTIME_MASK); + + WREG32_P(FVTHROT_PWM_CTRL_REG0, + FORCE_STARTING_PWM_HIGHTIME, ~FORCE_STARTING_PWM_HIGHTIME); + + WREG32_P(FVTHROT_PWM_FEEDBACK_DIV_REG1, 0, + ~RANGE_PWM_FEEDBACK_DIV_EN); + + udelay(1); + + WREG32_P(GFX_MACRO_BYPASS_CNTL, 0, ~SPLL_BYPASS_CNTL); +} + +static int rs780_set_engine_clock_scaling(struct radeon_device *rdev) +{ + struct atom_clock_dividers min_dividers, max_dividers, current_max_dividers; + struct igp_ps *new_state = rs780_get_ps(rdev->pm.dpm.requested_ps); + struct igp_ps *old_state = rs780_get_ps(rdev->pm.dpm.current_ps); + int ret; + + if ((new_state->sclk_high == old_state->sclk_high) && + (new_state->sclk_low == old_state->sclk_low)) + return 0; + + ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM, + new_state->sclk_low, false, &min_dividers); + if (ret) + return ret; + + ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM, + new_state->sclk_high, false, &max_dividers); + if (ret) + return ret; + + ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM, + old_state->sclk_high, false, ¤t_max_dividers); + if (ret) + return ret; + + WREG32_P(GFX_MACRO_BYPASS_CNTL, SPLL_BYPASS_CNTL, ~SPLL_BYPASS_CNTL); + + WREG32_P(FVTHROT_FBDIV_REG2, FORCED_FEEDBACK_DIV(max_dividers.fb_div), + ~FORCED_FEEDBACK_DIV_MASK); + WREG32_P(FVTHROT_FBDIV_REG1, STARTING_FEEDBACK_DIV(max_dividers.fb_div), + ~STARTING_FEEDBACK_DIV_MASK); + WREG32_P(FVTHROT_FBDIV_REG1, FORCE_FEEDBACK_DIV, ~FORCE_FEEDBACK_DIV); + + udelay(100); + + WREG32_P(GFX_MACRO_BYPASS_CNTL, 0, ~SPLL_BYPASS_CNTL); + + if (max_dividers.fb_div > min_dividers.fb_div) { + WREG32_P(FVTHROT_FBDIV_REG0, + MIN_FEEDBACK_DIV(min_dividers.fb_div) | + MAX_FEEDBACK_DIV(max_dividers.fb_div), + ~(MIN_FEEDBACK_DIV_MASK | MAX_FEEDBACK_DIV_MASK)); + + WREG32_P(FVTHROT_FBDIV_REG1, 0, ~FORCE_FEEDBACK_DIV); + } + + return 0; +} + +static void rs780_set_engine_clock_spc(struct radeon_device *rdev) +{ + struct igp_ps *new_state = rs780_get_ps(rdev->pm.dpm.requested_ps); + struct igp_ps *old_state = rs780_get_ps(rdev->pm.dpm.current_ps); + struct igp_power_info *pi = rs780_get_pi(rdev); + + if ((new_state->sclk_high == old_state->sclk_high) && + (new_state->sclk_low == old_state->sclk_low)) + return; + + if (pi->crtc_id == 0) + WREG32_P(CG_INTGFX_MISC, 0, ~FVTHROT_VBLANK_SEL); + else + WREG32_P(CG_INTGFX_MISC, FVTHROT_VBLANK_SEL, ~FVTHROT_VBLANK_SEL); + +} + +static void rs780_activate_engine_clk_scaling(struct radeon_device *rdev) +{ + struct igp_ps *new_state = rs780_get_ps(rdev->pm.dpm.requested_ps); + struct igp_ps *old_state = rs780_get_ps(rdev->pm.dpm.current_ps); + + if ((new_state->sclk_high == old_state->sclk_high) && + (new_state->sclk_low == old_state->sclk_low)) + return; + + rs780_clk_scaling_enable(rdev, true); +} + +static u32 rs780_get_voltage_for_vddc_level(struct radeon_device *rdev, + enum rs780_vddc_level vddc) +{ + struct igp_power_info *pi = rs780_get_pi(rdev); + + if (vddc == RS780_VDDC_LEVEL_HIGH) + return pi->max_voltage; + else if (vddc == RS780_VDDC_LEVEL_LOW) + return pi->min_voltage; + else + return pi->max_voltage; +} + +static void rs780_enable_voltage_scaling(struct radeon_device *rdev) +{ + struct igp_ps *new_state = rs780_get_ps(rdev->pm.dpm.requested_ps); + struct igp_power_info *pi = rs780_get_pi(rdev); + enum rs780_vddc_level vddc_high, vddc_low; + + udelay(100); + + if ((new_state->max_voltage == RS780_VDDC_LEVEL_HIGH) && + (new_state->min_voltage == RS780_VDDC_LEVEL_HIGH)) + return; + + vddc_high = rs780_get_voltage_for_vddc_level(rdev, + new_state->max_voltage); + vddc_low = rs780_get_voltage_for_vddc_level(rdev, + new_state->min_voltage); + + WREG32_P(GFX_MACRO_BYPASS_CNTL, SPLL_BYPASS_CNTL, ~SPLL_BYPASS_CNTL); + + udelay(1); + if (vddc_high > vddc_low) { + WREG32_P(FVTHROT_PWM_FEEDBACK_DIV_REG1, + RANGE_PWM_FEEDBACK_DIV_EN, ~RANGE_PWM_FEEDBACK_DIV_EN); + + WREG32_P(FVTHROT_PWM_CTRL_REG0, 0, ~FORCE_STARTING_PWM_HIGHTIME); + } else if (vddc_high == vddc_low) { + if (pi->max_voltage != vddc_high) { + WREG32_P(FVTHROT_PWM_CTRL_REG0, + STARTING_PWM_HIGHTIME(vddc_high), + ~STARTING_PWM_HIGHTIME_MASK); + + WREG32_P(FVTHROT_PWM_CTRL_REG0, + FORCE_STARTING_PWM_HIGHTIME, + ~FORCE_STARTING_PWM_HIGHTIME); + } + } + + WREG32_P(GFX_MACRO_BYPASS_CNTL, 0, ~SPLL_BYPASS_CNTL); +} + +int rs780_dpm_enable(struct radeon_device *rdev) +{ + struct igp_power_info *pi = rs780_get_pi(rdev); + + rs780_get_pm_mode_parameters(rdev); + rs780_disable_vbios_powersaving(rdev); + + if (r600_dynamicpm_enabled(rdev)) + return -EINVAL; + if (rs780_initialize_dpm_parameters(rdev)) + return -EINVAL; + rs780_start_dpm(rdev); + + rs780_preset_ranges_slow_clk_fbdiv_en(rdev); + rs780_preset_starting_fbdiv(rdev); + if (pi->voltage_control) + rs780_voltage_scaling_init(rdev); + rs780_clk_scaling_enable(rdev, true); + rs780_set_engine_clock_sc(rdev); + rs780_set_engine_clock_wfc(rdev); + rs780_program_at(rdev); + rs780_set_engine_clock_tdc(rdev); + rs780_set_engine_clock_ssc(rdev); + + if (pi->gfx_clock_gating) + r600_gfx_clockgating_enable(rdev, true); + + return 0; +} + +void rs780_dpm_disable(struct radeon_device *rdev) +{ + struct igp_power_info *pi = rs780_get_pi(rdev); + + r600_dynamicpm_enable(rdev, false); + + rs780_clk_scaling_enable(rdev, false); + rs780_voltage_scaling_enable(rdev, false); + + if (pi->gfx_clock_gating) + r600_gfx_clockgating_enable(rdev, false); +} + +int rs780_dpm_set_power_state(struct radeon_device *rdev) +{ + struct igp_power_info *pi = rs780_get_pi(rdev); + + rs780_get_pm_mode_parameters(rdev); + + if (pi->voltage_control) { + rs780_force_voltage_to_high(rdev); + mdelay(5); + } + + rs780_set_engine_clock_scaling(rdev); + rs780_set_engine_clock_spc(rdev); + + rs780_activate_engine_clk_scaling(rdev); + + if (pi->voltage_control) + rs780_enable_voltage_scaling(rdev); + + return 0; +} + +void rs780_dpm_setup_asic(struct radeon_device *rdev) +{ + +} + +void rs780_dpm_display_configuration_changed(struct radeon_device *rdev) +{ + rs780_get_pm_mode_parameters(rdev); + rs780_program_at(rdev); +} + +union igp_info { + struct _ATOM_INTEGRATED_SYSTEM_INFO info; + struct _ATOM_INTEGRATED_SYSTEM_INFO_V2 info_2; +}; + +union power_info { + struct _ATOM_POWERPLAY_INFO info; + struct _ATOM_POWERPLAY_INFO_V2 info_2; + struct _ATOM_POWERPLAY_INFO_V3 info_3; + struct _ATOM_PPLIB_POWERPLAYTABLE pplib; + struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2; + struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3; +}; + +union pplib_clock_info { + struct _ATOM_PPLIB_R600_CLOCK_INFO r600; + struct _ATOM_PPLIB_RS780_CLOCK_INFO rs780; + struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO evergreen; + struct _ATOM_PPLIB_SUMO_CLOCK_INFO sumo; +}; + +union pplib_power_state { + struct _ATOM_PPLIB_STATE v1; + struct _ATOM_PPLIB_STATE_V2 v2; +}; + +static void rs780_parse_pplib_non_clock_info(struct radeon_device *rdev, + struct radeon_ps *rps, + struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info, + u8 table_rev) +{ + rps->caps = le32_to_cpu(non_clock_info->ulCapsAndSettings); + rps->class = le16_to_cpu(non_clock_info->usClassification); + rps->class2 = le16_to_cpu(non_clock_info->usClassification2); + + if (ATOM_PPLIB_NONCLOCKINFO_VER1 < table_rev) { + rps->vclk = le32_to_cpu(non_clock_info->ulVCLK); + rps->dclk = le32_to_cpu(non_clock_info->ulDCLK); + } else if (r600_is_uvd_state(rps->class, rps->class2)) { + rps->vclk = RS780_DEFAULT_VCLK_FREQ; + rps->dclk = RS780_DEFAULT_DCLK_FREQ; + } else { + rps->vclk = 0; + rps->dclk = 0; + } + + if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT) + rdev->pm.dpm.boot_ps = rps; + if (rps->class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE) + rdev->pm.dpm.uvd_ps = rps; +} + +static void rs780_parse_pplib_clock_info(struct radeon_device *rdev, + struct radeon_ps *rps, + union pplib_clock_info *clock_info) +{ + struct igp_ps *ps = rs780_get_ps(rps); + u32 sclk; + + sclk = le16_to_cpu(clock_info->rs780.usLowEngineClockLow); + sclk |= clock_info->rs780.ucLowEngineClockHigh << 16; + ps->sclk_low = sclk; + sclk = le16_to_cpu(clock_info->rs780.usHighEngineClockLow); + sclk |= clock_info->rs780.ucHighEngineClockHigh << 16; + ps->sclk_high = sclk; + switch (le16_to_cpu(clock_info->rs780.usVDDC)) { + case ATOM_PPLIB_RS780_VOLTAGE_NONE: + default: + ps->min_voltage = RS780_VDDC_LEVEL_UNKNOWN; + ps->max_voltage = RS780_VDDC_LEVEL_UNKNOWN; + break; + case ATOM_PPLIB_RS780_VOLTAGE_LOW: + ps->min_voltage = RS780_VDDC_LEVEL_LOW; + ps->max_voltage = RS780_VDDC_LEVEL_LOW; + break; + case ATOM_PPLIB_RS780_VOLTAGE_HIGH: + ps->min_voltage = RS780_VDDC_LEVEL_HIGH; + ps->max_voltage = RS780_VDDC_LEVEL_HIGH; + break; + case ATOM_PPLIB_RS780_VOLTAGE_VARIABLE: + ps->min_voltage = RS780_VDDC_LEVEL_LOW; + ps->max_voltage = RS780_VDDC_LEVEL_HIGH; + break; + } + ps->flags = le32_to_cpu(clock_info->rs780.ulFlags); + + if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT) { + ps->sclk_low = rdev->clock.default_sclk; + ps->sclk_high = rdev->clock.default_sclk; + ps->min_voltage = RS780_VDDC_LEVEL_HIGH; + ps->max_voltage = RS780_VDDC_LEVEL_HIGH; + } +} + +static int rs780_parse_power_table(struct radeon_device *rdev) +{ + struct radeon_mode_info *mode_info = &rdev->mode_info; + struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info; + union pplib_power_state *power_state; + int i; + union pplib_clock_info *clock_info; + union power_info *power_info; + int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo); + u16 data_offset; + u8 frev, crev; + struct igp_ps *ps; + + if (!atom_parse_data_header(mode_info->atom_context, index, NULL, + &frev, &crev, &data_offset)) + return -EINVAL; + power_info = (union power_info *)(mode_info->atom_context->bios + data_offset); + + rdev->pm.dpm.ps = kzalloc(sizeof(struct radeon_ps) * + power_info->pplib.ucNumStates, GFP_KERNEL); + if (!rdev->pm.dpm.ps) + return -ENOMEM; + rdev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps); + rdev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime); + rdev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime); + + for (i = 0; i < power_info->pplib.ucNumStates; i++) { + power_state = (union pplib_power_state *) + (mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib.usStateArrayOffset) + + i * power_info->pplib.ucStateEntrySize); + non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *) + (mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset) + + (power_state->v1.ucNonClockStateIndex * + power_info->pplib.ucNonClockSize)); + if (power_info->pplib.ucStateEntrySize - 1) { + clock_info = (union pplib_clock_info *) + (mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib.usClockInfoArrayOffset) + + (power_state->v1.ucClockStateIndices[0] * + power_info->pplib.ucClockInfoSize)); + ps = kzalloc(sizeof(struct igp_ps), GFP_KERNEL); + if (ps == NULL) { + kfree(rdev->pm.dpm.ps); + return -ENOMEM; + } + rdev->pm.dpm.ps[i].ps_priv = ps; + rs780_parse_pplib_non_clock_info(rdev, &rdev->pm.dpm.ps[i], + non_clock_info, + power_info->pplib.ucNonClockSize); + rs780_parse_pplib_clock_info(rdev, + &rdev->pm.dpm.ps[i], + clock_info); + } + } + rdev->pm.dpm.num_ps = power_info->pplib.ucNumStates; + return 0; +} + +int rs780_dpm_init(struct radeon_device *rdev) +{ + struct igp_power_info *pi; + int index = GetIndexIntoMasterTable(DATA, IntegratedSystemInfo); + union igp_info *info; + u16 data_offset; + u8 frev, crev; + int ret; + + pi = kzalloc(sizeof(struct igp_power_info), GFP_KERNEL); + if (pi == NULL) + return -ENOMEM; + rdev->pm.dpm.priv = pi; + + ret = rs780_parse_power_table(rdev); + if (ret) + return ret; + + pi->voltage_control = false; + pi->gfx_clock_gating = true; + + if (atom_parse_data_header(rdev->mode_info.atom_context, index, NULL, + &frev, &crev, &data_offset)) { + info = (union igp_info *)(rdev->mode_info.atom_context->bios + data_offset); + + /* Get various system informations from bios */ + switch (crev) { + case 1: + pi->num_of_cycles_in_period = + info->info.ucNumberOfCyclesInPeriod; + pi->num_of_cycles_in_period |= + info->info.ucNumberOfCyclesInPeriodHi << 8; + pi->invert_pwm_required = + (pi->num_of_cycles_in_period & 0x8000) ? true : false; + pi->boot_voltage = info->info.ucStartingPWM_HighTime; + pi->max_voltage = info->info.ucMaxNBVoltage; + pi->max_voltage |= info->info.ucMaxNBVoltageHigh << 8; + pi->min_voltage = info->info.ucMinNBVoltage; + pi->min_voltage |= info->info.ucMinNBVoltageHigh << 8; + pi->inter_voltage_low = + le16_to_cpu(info->info.usInterNBVoltageLow); + pi->inter_voltage_high = + le16_to_cpu(info->info.usInterNBVoltageHigh); + pi->voltage_control = true; + pi->bootup_uma_clk = info->info.usK8MemoryClock * 100; + break; + case 2: + pi->num_of_cycles_in_period = + le16_to_cpu(info->info_2.usNumberOfCyclesInPeriod); + pi->invert_pwm_required = + (pi->num_of_cycles_in_period & 0x8000) ? true : false; + pi->boot_voltage = + le16_to_cpu(info->info_2.usBootUpNBVoltage); + pi->max_voltage = + le16_to_cpu(info->info_2.usMaxNBVoltage); + pi->min_voltage = + le16_to_cpu(info->info_2.usMinNBVoltage); + pi->system_config = + le32_to_cpu(info->info_2.ulSystemConfig); + pi->pwm_voltage_control = + (pi->system_config & 0x4) ? true : false; + pi->voltage_control = true; + pi->bootup_uma_clk = le32_to_cpu(info->info_2.ulBootUpUMAClock); + break; + default: + DRM_ERROR("No integrated system info for your GPU\n"); + return -EINVAL; + } + if (pi->min_voltage > pi->max_voltage) + pi->voltage_control = false; + if (pi->pwm_voltage_control) { + if ((pi->num_of_cycles_in_period == 0) || + (pi->max_voltage == 0) || + (pi->min_voltage == 0)) + pi->voltage_control = false; + } else { + if ((pi->num_of_cycles_in_period == 0) || + (pi->max_voltage == 0)) + pi->voltage_control = false; + } + + return 0; + } + radeon_dpm_fini(rdev); + return -EINVAL; +} + +void rs780_dpm_print_power_state(struct radeon_device *rdev, + struct radeon_ps *rps) +{ + struct igp_ps *ps = rs780_get_ps(rps); + + r600_dpm_print_class_info(rps->class, rps->class2); + r600_dpm_print_cap_info(rps->caps); + printk("\tuvd vclk: %d dclk: %d\n", rps->vclk, rps->dclk); + printk("\t\tpower level 0 sclk: %u vddc_index: %d\n", + ps->sclk_low, ps->min_voltage); + printk("\t\tpower level 1 sclk: %u vddc_index: %d\n", + ps->sclk_high, ps->max_voltage); + r600_dpm_print_ps_status(rdev, rps); +} + +void rs780_dpm_fini(struct radeon_device *rdev) +{ + int i; + + for (i = 0; i < rdev->pm.dpm.num_ps; i++) { + kfree(rdev->pm.dpm.ps[i].ps_priv); + } + kfree(rdev->pm.dpm.ps); + kfree(rdev->pm.dpm.priv); +} + +u32 rs780_dpm_get_sclk(struct radeon_device *rdev, bool low) +{ + struct igp_ps *requested_state = rs780_get_ps(rdev->pm.dpm.requested_ps); + + if (low) + return requested_state->sclk_low; + else + return requested_state->sclk_high; +} + +u32 rs780_dpm_get_mclk(struct radeon_device *rdev, bool low) +{ + struct igp_power_info *pi = rs780_get_pi(rdev); + + return pi->bootup_uma_clk; +} diff --git a/drivers/gpu/drm/radeon/rs780_dpm.h b/drivers/gpu/drm/radeon/rs780_dpm.h new file mode 100644 index 0000000..47a40b1 --- /dev/null +++ b/drivers/gpu/drm/radeon/rs780_dpm.h @@ -0,0 +1,109 @@ +/* + * Copyright 2011 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ +#ifndef __RS780_DPM_H__ +#define __RS780_DPM_H__ + +enum rs780_vddc_level { + RS780_VDDC_LEVEL_UNKNOWN = 0, + RS780_VDDC_LEVEL_LOW = 1, + RS780_VDDC_LEVEL_HIGH = 2, +}; + +struct igp_power_info { + /* flags */ + bool invert_pwm_required; + bool pwm_voltage_control; + bool voltage_control; + bool gfx_clock_gating; + /* stored values */ + u32 system_config; + u32 bootup_uma_clk; + u16 max_voltage; + u16 min_voltage; + u16 boot_voltage; + u16 inter_voltage_low; + u16 inter_voltage_high; + u16 num_of_cycles_in_period; + /* variable */ + int crtc_id; + int refresh_rate; +}; + +struct igp_ps { + enum rs780_vddc_level min_voltage; + enum rs780_vddc_level max_voltage; + u32 sclk_low; + u32 sclk_high; + u32 flags; +}; + +#define RS780_CGFTV_DFLT 0x0303000f +#define RS780_FBDIVTIMERVAL_DFLT 0x2710 + +#define RS780_FVTHROTUTC0_DFLT 0x04010040 +#define RS780_FVTHROTUTC1_DFLT 0x04010040 +#define RS780_FVTHROTUTC2_DFLT 0x04010040 +#define RS780_FVTHROTUTC3_DFLT 0x04010040 +#define RS780_FVTHROTUTC4_DFLT 0x04010040 + +#define RS780_FVTHROTDTC0_DFLT 0x04010040 +#define RS780_FVTHROTDTC1_DFLT 0x04010040 +#define RS780_FVTHROTDTC2_DFLT 0x04010040 +#define RS780_FVTHROTDTC3_DFLT 0x04010040 +#define RS780_FVTHROTDTC4_DFLT 0x04010040 + +#define RS780_FVTHROTFBUSREG0_DFLT 0x00001001 +#define RS780_FVTHROTFBUSREG1_DFLT 0x00002002 +#define RS780_FVTHROTFBDSREG0_DFLT 0x00004001 +#define RS780_FVTHROTFBDSREG1_DFLT 0x00020010 + +#define RS780_FVTHROTPWMUSREG0_DFLT 0x00002001 +#define RS780_FVTHROTPWMUSREG1_DFLT 0x00004003 +#define RS780_FVTHROTPWMDSREG0_DFLT 0x00002001 +#define RS780_FVTHROTPWMDSREG1_DFLT 0x00004003 + +#define RS780_FVTHROTPWMFBDIVRANGEREG0_DFLT 0x37 +#define RS780_FVTHROTPWMFBDIVRANGEREG1_DFLT 0x4b +#define RS780_FVTHROTPWMFBDIVRANGEREG2_DFLT 0x8b + +#define RS780D_FVTHROTPWMFBDIVRANGEREG0_DFLT 0x8b +#define RS780D_FVTHROTPWMFBDIVRANGEREG1_DFLT 0x8c +#define RS780D_FVTHROTPWMFBDIVRANGEREG2_DFLT 0xb5 + +#define RS880D_FVTHROTPWMFBDIVRANGEREG0_DFLT 0x8d +#define RS880D_FVTHROTPWMFBDIVRANGEREG1_DFLT 0x8e +#define RS880D_FVTHROTPWMFBDIVRANGEREG2_DFLT 0xBa + +#define RS780_FVTHROTPWMRANGE0_GPIO_DFLT 0x1a +#define RS780_FVTHROTPWMRANGE1_GPIO_DFLT 0x1a +#define RS780_FVTHROTPWMRANGE2_GPIO_DFLT 0x0 +#define RS780_FVTHROTPWMRANGE3_GPIO_DFLT 0x0 + +#define RS780_SLOWCLKFEEDBACKDIV_DFLT 110 + +#define RS780_CGCLKGATING_DFLT 0x0000E204 + +#define RS780_DEFAULT_VCLK_FREQ 53300 /* 10 khz */ +#define RS780_DEFAULT_DCLK_FREQ 40000 /* 10 khz */ + +#endif diff --git a/drivers/gpu/drm/radeon/rs780d.h b/drivers/gpu/drm/radeon/rs780d.h new file mode 100644 index 0000000..b1142ed --- /dev/null +++ b/drivers/gpu/drm/radeon/rs780d.h @@ -0,0 +1,168 @@ +/* + * Copyright 2011 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ +#ifndef __RS780D_H__ +#define __RS780D_H__ + +#define CG_SPLL_FUNC_CNTL 0x600 +# define SPLL_RESET (1 << 0) +# define SPLL_SLEEP (1 << 1) +# define SPLL_REF_DIV(x) ((x) << 2) +# define SPLL_REF_DIV_MASK (7 << 2) +# define SPLL_FB_DIV(x) ((x) << 5) +# define SPLL_FB_DIV_MASK (0xff << 2) +# define SPLL_FB_DIV_SHIFT 2 +# define SPLL_PULSEEN (1 << 13) +# define SPLL_PULSENUM(x) ((x) << 14) +# define SPLL_PULSENUM_MASK (3 << 14) +# define SPLL_SW_HILEN(x) ((x) << 16) +# define SPLL_SW_HILEN_MASK (0xf << 16) +# define SPLL_SW_LOLEN(x) ((x) << 20) +# define SPLL_SW_LOLEN_MASK (0xf << 20) +# define SPLL_DIVEN (1 << 24) +# define SPLL_BYPASS_EN (1 << 25) +# define SPLL_CHG_STATUS (1 << 29) +# define SPLL_CTLREQ (1 << 30) +# define SPLL_CTLACK (1 << 31) + +/* RS780/RS880 PM */ +#define FVTHROT_CNTRL_REG 0x3000 +#define DONT_WAIT_FOR_FBDIV_WRAP (1 << 0) +#define MINIMUM_CIP(x) ((x) << 1) +#define MINIMUM_CIP_SHIFT 1 +#define MINIMUM_CIP_MASK 0x1fffffe +#define REFRESH_RATE_DIVISOR(x) ((x) << 25) +#define REFRESH_RATE_DIVISOR_SHIFT 25 +#define REFRESH_RATE_DIVISOR_MASK (0x3 << 25) +#define ENABLE_FV_THROT (1 << 27) +#define ENABLE_FV_UPDATE (1 << 28) +#define TREND_SEL_MODE (1 << 29) +#define FORCE_TREND_SEL (1 << 30) +#define ENABLE_FV_THROT_IO (1 << 31) +#define FVTHROT_TARGET_REG 0x3004 +#define TARGET_IDLE_COUNT(x) ((x) << 0) +#define TARGET_IDLE_COUNT_MASK 0xffffff +#define TARGET_IDLE_COUNT_SHIFT 0 +#define FVTHROT_CB1 0x3008 +#define FVTHROT_CB2 0x300c +#define FVTHROT_CB3 0x3010 +#define FVTHROT_CB4 0x3014 +#define FVTHROT_UTC0 0x3018 +#define FVTHROT_UTC1 0x301c +#define FVTHROT_UTC2 0x3020 +#define FVTHROT_UTC3 0x3024 +#define FVTHROT_UTC4 0x3028 +#define FVTHROT_DTC0 0x302c +#define FVTHROT_DTC1 0x3030 +#define FVTHROT_DTC2 0x3034 +#define FVTHROT_DTC3 0x3038 +#define FVTHROT_DTC4 0x303c +#define FVTHROT_FBDIV_REG0 0x3040 +#define MIN_FEEDBACK_DIV(x) ((x) << 0) +#define MIN_FEEDBACK_DIV_MASK 0xfff +#define MIN_FEEDBACK_DIV_SHIFT 0 +#define MAX_FEEDBACK_DIV(x) ((x) << 12) +#define MAX_FEEDBACK_DIV_MASK (0xfff << 12) +#define MAX_FEEDBACK_DIV_SHIFT 12 +#define FVTHROT_FBDIV_REG1 0x3044 +#define MAX_FEEDBACK_STEP(x) ((x) << 0) +#define MAX_FEEDBACK_STEP_MASK 0xfff +#define MAX_FEEDBACK_STEP_SHIFT 0 +#define STARTING_FEEDBACK_DIV(x) ((x) << 12) +#define STARTING_FEEDBACK_DIV_MASK (0xfff << 12) +#define STARTING_FEEDBACK_DIV_SHIFT 12 +#define FORCE_FEEDBACK_DIV (1 << 24) +#define FVTHROT_FBDIV_REG2 0x3048 +#define FORCED_FEEDBACK_DIV(x) ((x) << 0) +#define FORCED_FEEDBACK_DIV_MASK 0xfff +#define FORCED_FEEDBACK_DIV_SHIFT 0 +#define FB_DIV_TIMER_VAL(x) ((x) << 12) +#define FB_DIV_TIMER_VAL_MASK (0xffff << 12) +#define FB_DIV_TIMER_VAL_SHIFT 12 +#define FVTHROT_FB_US_REG0 0x304c +#define FVTHROT_FB_US_REG1 0x3050 +#define FVTHROT_FB_DS_REG0 0x3054 +#define FVTHROT_FB_DS_REG1 0x3058 +#define FVTHROT_PWM_CTRL_REG0 0x305c +#define STARTING_PWM_HIGHTIME(x) ((x) << 0) +#define STARTING_PWM_HIGHTIME_MASK 0xfff +#define STARTING_PWM_HIGHTIME_SHIFT 0 +#define NUMBER_OF_CYCLES_IN_PERIOD(x) ((x) << 12) +#define NUMBER_OF_CYCLES_IN_PERIOD_MASK (0xfff << 12) +#define NUMBER_OF_CYCLES_IN_PERIOD_SHIFT 12 +#define FORCE_STARTING_PWM_HIGHTIME (1 << 24) +#define INVERT_PWM_WAVEFORM (1 << 25) +#define FVTHROT_PWM_CTRL_REG1 0x3060 +#define MIN_PWM_HIGHTIME(x) ((x) << 0) +#define MIN_PWM_HIGHTIME_MASK 0xfff +#define MIN_PWM_HIGHTIME_SHIFT 0 +#define MAX_PWM_HIGHTIME(x) ((x) << 12) +#define MAX_PWM_HIGHTIME_MASK (0xfff << 12) +#define MAX_PWM_HIGHTIME_SHIFT 12 +#define FVTHROT_PWM_US_REG0 0x3064 +#define FVTHROT_PWM_US_REG1 0x3068 +#define FVTHROT_PWM_DS_REG0 0x306c +#define FVTHROT_PWM_DS_REG1 0x3070 +#define FVTHROT_STATUS_REG0 0x3074 +#define CURRENT_FEEDBACK_DIV_MASK 0xfff +#define CURRENT_FEEDBACK_DIV_SHIFT 0 +#define FVTHROT_STATUS_REG1 0x3078 +#define FVTHROT_STATUS_REG2 0x307c +#define CG_INTGFX_MISC 0x3080 +#define FVTHROT_VBLANK_SEL (1 << 9) +#define FVTHROT_PWM_FEEDBACK_DIV_REG1 0x308c +#define RANGE0_PWM_FEEDBACK_DIV(x) ((x) << 0) +#define RANGE0_PWM_FEEDBACK_DIV_MASK 0xfff +#define RANGE0_PWM_FEEDBACK_DIV_SHIFT 0 +#define RANGE_PWM_FEEDBACK_DIV_EN (1 << 12) +#define FVTHROT_PWM_FEEDBACK_DIV_REG2 0x3090 +#define RANGE1_PWM_FEEDBACK_DIV(x) ((x) << 0) +#define RANGE1_PWM_FEEDBACK_DIV_MASK 0xfff +#define RANGE1_PWM_FEEDBACK_DIV_SHIFT 0 +#define RANGE2_PWM_FEEDBACK_DIV(x) ((x) << 12) +#define RANGE2_PWM_FEEDBACK_DIV_MASK (0xfff << 12) +#define RANGE2_PWM_FEEDBACK_DIV_SHIFT 12 +#define FVTHROT_PWM_FEEDBACK_DIV_REG3 0x3094 +#define RANGE0_PWM(x) ((x) << 0) +#define RANGE0_PWM_MASK 0xfff +#define RANGE0_PWM_SHIFT 0 +#define RANGE1_PWM(x) ((x) << 12) +#define RANGE1_PWM_MASK (0xfff << 12) +#define RANGE1_PWM_SHIFT 12 +#define FVTHROT_PWM_FEEDBACK_DIV_REG4 0x3098 +#define RANGE2_PWM(x) ((x) << 0) +#define RANGE2_PWM_MASK 0xfff +#define RANGE2_PWM_SHIFT 0 +#define RANGE3_PWM(x) ((x) << 12) +#define RANGE3_PWM_MASK (0xfff << 12) +#define RANGE3_PWM_SHIFT 12 +#define FVTHROT_SLOW_CLK_FEEDBACK_DIV_REG1 0x30ac +#define RANGE0_SLOW_CLK_FEEDBACK_DIV(x) ((x) << 0) +#define RANGE0_SLOW_CLK_FEEDBACK_DIV_MASK 0xfff +#define RANGE0_SLOW_CLK_FEEDBACK_DIV_SHIFT 0 +#define RANGE_SLOW_CLK_FEEDBACK_DIV_EN (1 << 12) + +#define GFX_MACRO_BYPASS_CNTL 0x30c0 +#define SPLL_BYPASS_CNTL (1 << 0) +#define UPLL_BYPASS_CNTL (1 << 1) + +#endif
On Wed, Jun 26, 2013 at 09:22:35AM -0400, alexdeucher@gmail.com wrote:
From: Alex Deucher alexander.deucher@amd.com
This adds dpm support for rs780/rs880 asics. This includes:
- clockgating
- dynamic engine clock scaling
- dynamic voltage scaling
set radeon.dpm=1 to enable it.
Signed-off-by: Alex Deucher alexander.deucher@amd.com
Depending on the answer to inline question :
Reviewed-by: Jerome Glisse jglisse@redhat.com
drivers/gpu/drm/radeon/Makefile | 2 +- drivers/gpu/drm/radeon/radeon_asic.c | 12 + drivers/gpu/drm/radeon/radeon_asic.h | 12 + drivers/gpu/drm/radeon/radeon_pm.c | 7 + drivers/gpu/drm/radeon/rs780_dpm.c | 894 ++++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/rs780_dpm.h | 109 ++++ drivers/gpu/drm/radeon/rs780d.h | 168 +++++++ 7 files changed, 1203 insertions(+), 1 deletions(-) create mode 100644 drivers/gpu/drm/radeon/rs780_dpm.c create mode 100644 drivers/gpu/drm/radeon/rs780_dpm.h create mode 100644 drivers/gpu/drm/radeon/rs780d.h
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index a131a13..e44b046 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile @@ -77,7 +77,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \ evergreen_hdmi.o radeon_trace_points.o ni.o cayman_blit_shaders.o \ atombios_encoders.o radeon_semaphore.o radeon_sa.o atombios_i2c.o si.o \ si_blit_shaders.o radeon_prime.o radeon_uvd.o cik.o cik_blit_shaders.o \
- r600_dpm.o
- r600_dpm.o rs780_dpm.o
radeon-$(CONFIG_COMPAT) += radeon_ioc32.o radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index d9c8e9a..db3c930 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1194,6 +1194,18 @@ static struct radeon_asic rs780_asic = { .set_clock_gating = NULL, .get_temperature = &rv6xx_get_temp, },
- .dpm = {
.init = &rs780_dpm_init,
.setup_asic = &rs780_dpm_setup_asic,
.enable = &rs780_dpm_enable,
.disable = &rs780_dpm_disable,
.set_power_state = &rs780_dpm_set_power_state,
.display_configuration_changed = &rs780_dpm_display_configuration_changed,
.fini = &rs780_dpm_fini,
.get_sclk = &rs780_dpm_get_sclk,
.get_mclk = &rs780_dpm_get_mclk,
.print_power_state = &rs780_dpm_print_power_state,
- }, .pflip = { .pre_page_flip = &rs600_pre_page_flip, .page_flip = &rs600_page_flip,
diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 8507cae..134bf57 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -396,6 +396,18 @@ int r600_mc_wait_for_idle(struct radeon_device *rdev); u32 r600_get_xclk(struct radeon_device *rdev); uint64_t r600_get_gpu_clock_counter(struct radeon_device *rdev); int rv6xx_get_temp(struct radeon_device *rdev); +/* rs780 dpm */ +int rs780_dpm_init(struct radeon_device *rdev); +int rs780_dpm_enable(struct radeon_device *rdev); +void rs780_dpm_disable(struct radeon_device *rdev); +int rs780_dpm_set_power_state(struct radeon_device *rdev); +void rs780_dpm_setup_asic(struct radeon_device *rdev); +void rs780_dpm_display_configuration_changed(struct radeon_device *rdev); +void rs780_dpm_fini(struct radeon_device *rdev); +u32 rs780_dpm_get_sclk(struct radeon_device *rdev, bool low); +u32 rs780_dpm_get_mclk(struct radeon_device *rdev, bool low); +void rs780_dpm_print_power_state(struct radeon_device *rdev,
struct radeon_ps *ps);
/* uvd */ int r600_uvd_init(struct radeon_device *rdev); diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 4f5422e..853a8a2 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -1030,6 +1030,13 @@ int radeon_pm_init(struct radeon_device *rdev) { /* enable dpm on rv6xx+ */ switch (rdev->family) {
- case CHIP_RS780:
- case CHIP_RS880:
if (radeon_dpm == 1)
rdev->pm.pm_method = PM_METHOD_DPM;
else
rdev->pm.pm_method = PM_METHOD_PROFILE;
default: /* default to profile method */ rdev->pm.pm_method = PM_METHOD_PROFILE;break;
diff --git a/drivers/gpu/drm/radeon/rs780_dpm.c b/drivers/gpu/drm/radeon/rs780_dpm.c new file mode 100644 index 0000000..f594900 --- /dev/null +++ b/drivers/gpu/drm/radeon/rs780_dpm.c @@ -0,0 +1,894 @@ +/*
- Copyright 2011 Advanced Micro Devices, 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: Alex Deucher
- */
+#include "drmP.h" +#include "radeon.h" +#include "rs780d.h" +#include "r600_dpm.h" +#include "rs780_dpm.h" +#include "atom.h"
+static struct igp_ps *rs780_get_ps(struct radeon_ps *rps) +{
- struct igp_ps *ps = rps->ps_priv;
- return ps;
+}
+static struct igp_power_info *rs780_get_pi(struct radeon_device *rdev) +{
- struct igp_power_info *pi = rdev->pm.dpm.priv;
- return pi;
+}
+static void rs780_get_pm_mode_parameters(struct radeon_device *rdev) +{
- struct igp_power_info *pi = rs780_get_pi(rdev);
- struct radeon_mode_info *minfo = &rdev->mode_info;
- struct drm_crtc *crtc;
- struct radeon_crtc *radeon_crtc;
- int i;
- /* defaults */
- pi->crtc_id = 0;
- pi->refresh_rate = 60;
- for (i = 0; i < rdev->num_crtc; i++) {
crtc = (struct drm_crtc *)minfo->crtcs[i];
if (crtc && crtc->enabled) {
radeon_crtc = to_radeon_crtc(crtc);
pi->crtc_id = radeon_crtc->crtc_id;
if (crtc->mode.htotal && crtc->mode.vtotal)
pi->refresh_rate =
(crtc->mode.clock * 1000) /
(crtc->mode.htotal * crtc->mode.vtotal);
break;
}
- }
Ok this looks wrong to me you look for the first enabled crtc but on those iirc there could be 2 so first one might be a low refresh rate and second one an higher one. Thus returning the first one might lead to PM decision that are wrong.
+}
+static void rs780_voltage_scaling_enable(struct radeon_device *rdev, bool enable);
+static int rs780_initialize_dpm_power_state(struct radeon_device *rdev) +{
- struct atom_clock_dividers dividers;
- struct igp_ps *default_state = rs780_get_ps(rdev->pm.dpm.boot_ps);
- int i, ret;
- ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
default_state->sclk_low, false, ÷rs);
- if (ret)
return ret;
- r600_engine_clock_entry_set_reference_divider(rdev, 0, dividers.ref_div);
- r600_engine_clock_entry_set_feedback_divider(rdev, 0, dividers.fb_div);
- r600_engine_clock_entry_set_post_divider(rdev, 0, dividers.post_div);
- if (dividers.enable_post_div)
r600_engine_clock_entry_enable_post_divider(rdev, 0, true);
- else
r600_engine_clock_entry_enable_post_divider(rdev, 0, false);
- r600_engine_clock_entry_set_step_time(rdev, 0, R600_SST_DFLT);
- r600_engine_clock_entry_enable_pulse_skipping(rdev, 0, false);
- r600_engine_clock_entry_enable(rdev, 0, true);
- for (i = 1; i < R600_PM_NUMBER_OF_SCLKS; i++)
r600_engine_clock_entry_enable(rdev, i, false);
- r600_enable_mclk_control(rdev, false);
- r600_voltage_control_enable_pins(rdev, 0);
- return 0;
+}
+static int rs780_initialize_dpm_parameters(struct radeon_device *rdev) +{
- int ret = 0;
- int i;
- r600_set_bsp(rdev, R600_BSU_DFLT, R600_BSP_DFLT);
- r600_set_at(rdev, 0, 0, 0, 0);
- r600_set_git(rdev, R600_GICST_DFLT);
- for (i = 0; i < R600_PM_NUMBER_OF_TC; i++)
r600_set_tc(rdev, i, 0, 0);
- r600_select_td(rdev, R600_TD_DFLT);
- r600_set_vrc(rdev, 0);
- r600_set_tpu(rdev, R600_TPU_DFLT);
- r600_set_tpc(rdev, R600_TPC_DFLT);
- r600_set_sstu(rdev, R600_SSTU_DFLT);
- r600_set_sst(rdev, R600_SST_DFLT);
- r600_set_fctu(rdev, R600_FCTU_DFLT);
- r600_set_fct(rdev, R600_FCT_DFLT);
- r600_set_vddc3d_oorsu(rdev, R600_VDDC3DOORSU_DFLT);
- r600_set_vddc3d_oorphc(rdev, R600_VDDC3DOORPHC_DFLT);
- r600_set_vddc3d_oorsdc(rdev, R600_VDDC3DOORSDC_DFLT);
- r600_set_ctxcgtt3d_rphc(rdev, R600_CTXCGTT3DRPHC_DFLT);
- r600_set_ctxcgtt3d_rsdc(rdev, R600_CTXCGTT3DRSDC_DFLT);
- r600_vid_rt_set_vru(rdev, R600_VRU_DFLT);
- r600_vid_rt_set_vrt(rdev, R600_VOLTAGERESPONSETIME_DFLT);
- r600_vid_rt_set_ssu(rdev, R600_SPLLSTEPUNIT_DFLT);
- ret = rs780_initialize_dpm_power_state(rdev);
- r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_LOW, 0);
- r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_MEDIUM, 0);
- r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_HIGH, 0);
- r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_LOW, 0);
- r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_MEDIUM, 0);
- r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_HIGH, 0);
- r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_LOW, 0);
- r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_MEDIUM, 0);
- r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_HIGH, 0);
- r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_LOW, R600_DISPLAY_WATERMARK_HIGH);
- r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_MEDIUM, R600_DISPLAY_WATERMARK_HIGH);
- r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_HIGH, R600_DISPLAY_WATERMARK_HIGH);
- r600_power_level_enable(rdev, R600_POWER_LEVEL_CTXSW, false);
- r600_power_level_enable(rdev, R600_POWER_LEVEL_HIGH, false);
- r600_power_level_enable(rdev, R600_POWER_LEVEL_MEDIUM, false);
- r600_power_level_enable(rdev, R600_POWER_LEVEL_LOW, true);
- r600_power_level_set_enter_index(rdev, R600_POWER_LEVEL_LOW);
- r600_set_vrc(rdev, RS780_CGFTV_DFLT);
- return ret;
+}
+static void rs780_start_dpm(struct radeon_device *rdev) +{
- r600_enable_sclk_control(rdev, false);
- r600_enable_mclk_control(rdev, false);
- r600_dynamicpm_enable(rdev, true);
- radeon_wait_for_vblank(rdev, 0);
- radeon_wait_for_vblank(rdev, 1);
- r600_enable_spll_bypass(rdev, true);
- r600_wait_for_spll_change(rdev);
- r600_enable_spll_bypass(rdev, false);
- r600_wait_for_spll_change(rdev);
- r600_enable_spll_bypass(rdev, true);
- r600_wait_for_spll_change(rdev);
- r600_enable_spll_bypass(rdev, false);
- r600_wait_for_spll_change(rdev);
- r600_enable_sclk_control(rdev, true);
+}
+static void rs780_preset_ranges_slow_clk_fbdiv_en(struct radeon_device *rdev) +{
- WREG32_P(FVTHROT_SLOW_CLK_FEEDBACK_DIV_REG1, RANGE_SLOW_CLK_FEEDBACK_DIV_EN,
~RANGE_SLOW_CLK_FEEDBACK_DIV_EN);
- WREG32_P(FVTHROT_SLOW_CLK_FEEDBACK_DIV_REG1,
RANGE0_SLOW_CLK_FEEDBACK_DIV(RS780_SLOWCLKFEEDBACKDIV_DFLT),
~RANGE0_SLOW_CLK_FEEDBACK_DIV_MASK);
+}
+static void rs780_preset_starting_fbdiv(struct radeon_device *rdev) +{
- u32 fbdiv = (RREG32(CG_SPLL_FUNC_CNTL) & SPLL_FB_DIV_MASK) >> SPLL_FB_DIV_SHIFT;
- WREG32_P(FVTHROT_FBDIV_REG1, STARTING_FEEDBACK_DIV(fbdiv),
~STARTING_FEEDBACK_DIV_MASK);
- WREG32_P(FVTHROT_FBDIV_REG2, FORCED_FEEDBACK_DIV(fbdiv),
~FORCED_FEEDBACK_DIV_MASK);
- WREG32_P(FVTHROT_FBDIV_REG1, FORCE_FEEDBACK_DIV, ~FORCE_FEEDBACK_DIV);
+}
+static void rs780_voltage_scaling_init(struct radeon_device *rdev) +{
- struct igp_power_info *pi = rs780_get_pi(rdev);
- struct drm_device *dev = rdev->ddev;
- u32 fv_throt_pwm_fb_div_range[3];
- u32 fv_throt_pwm_range[4];
- if (dev->pdev->device == 0x9614) {
fv_throt_pwm_fb_div_range[0] = RS780D_FVTHROTPWMFBDIVRANGEREG0_DFLT;
fv_throt_pwm_fb_div_range[1] = RS780D_FVTHROTPWMFBDIVRANGEREG1_DFLT;
fv_throt_pwm_fb_div_range[2] = RS780D_FVTHROTPWMFBDIVRANGEREG2_DFLT;
- } else if ((dev->pdev->device == 0x9714) ||
(dev->pdev->device == 0x9715)) {
fv_throt_pwm_fb_div_range[0] = RS880D_FVTHROTPWMFBDIVRANGEREG0_DFLT;
fv_throt_pwm_fb_div_range[1] = RS880D_FVTHROTPWMFBDIVRANGEREG1_DFLT;
fv_throt_pwm_fb_div_range[2] = RS880D_FVTHROTPWMFBDIVRANGEREG2_DFLT;
- } else {
fv_throt_pwm_fb_div_range[0] = RS780_FVTHROTPWMFBDIVRANGEREG0_DFLT;
fv_throt_pwm_fb_div_range[1] = RS780_FVTHROTPWMFBDIVRANGEREG1_DFLT;
fv_throt_pwm_fb_div_range[2] = RS780_FVTHROTPWMFBDIVRANGEREG2_DFLT;
- }
- if (pi->pwm_voltage_control) {
fv_throt_pwm_range[0] = pi->min_voltage;
fv_throt_pwm_range[1] = pi->min_voltage;
fv_throt_pwm_range[2] = pi->max_voltage;
fv_throt_pwm_range[3] = pi->max_voltage;
- } else {
fv_throt_pwm_range[0] = pi->invert_pwm_required ?
RS780_FVTHROTPWMRANGE3_GPIO_DFLT : RS780_FVTHROTPWMRANGE0_GPIO_DFLT;
fv_throt_pwm_range[1] = pi->invert_pwm_required ?
RS780_FVTHROTPWMRANGE2_GPIO_DFLT : RS780_FVTHROTPWMRANGE1_GPIO_DFLT;
fv_throt_pwm_range[2] = pi->invert_pwm_required ?
RS780_FVTHROTPWMRANGE1_GPIO_DFLT : RS780_FVTHROTPWMRANGE2_GPIO_DFLT;
fv_throt_pwm_range[3] = pi->invert_pwm_required ?
RS780_FVTHROTPWMRANGE0_GPIO_DFLT : RS780_FVTHROTPWMRANGE3_GPIO_DFLT;
- }
- WREG32_P(FVTHROT_PWM_CTRL_REG0,
STARTING_PWM_HIGHTIME(pi->max_voltage),
~STARTING_PWM_HIGHTIME_MASK);
- WREG32_P(FVTHROT_PWM_CTRL_REG0,
NUMBER_OF_CYCLES_IN_PERIOD(pi->num_of_cycles_in_period),
~NUMBER_OF_CYCLES_IN_PERIOD_MASK);
- WREG32_P(FVTHROT_PWM_CTRL_REG0, FORCE_STARTING_PWM_HIGHTIME,
~FORCE_STARTING_PWM_HIGHTIME);
- if (pi->invert_pwm_required)
WREG32_P(FVTHROT_PWM_CTRL_REG0, INVERT_PWM_WAVEFORM, ~INVERT_PWM_WAVEFORM);
- else
WREG32_P(FVTHROT_PWM_CTRL_REG0, 0, ~INVERT_PWM_WAVEFORM);
- rs780_voltage_scaling_enable(rdev, true);
- WREG32(FVTHROT_PWM_CTRL_REG1,
(MIN_PWM_HIGHTIME(pi->min_voltage) |
MAX_PWM_HIGHTIME(pi->max_voltage)));
- WREG32(FVTHROT_PWM_US_REG0, RS780_FVTHROTPWMUSREG0_DFLT);
- WREG32(FVTHROT_PWM_US_REG1, RS780_FVTHROTPWMUSREG1_DFLT);
- WREG32(FVTHROT_PWM_DS_REG0, RS780_FVTHROTPWMDSREG0_DFLT);
- WREG32(FVTHROT_PWM_DS_REG1, RS780_FVTHROTPWMDSREG1_DFLT);
- WREG32_P(FVTHROT_PWM_FEEDBACK_DIV_REG1,
RANGE0_PWM_FEEDBACK_DIV(fv_throt_pwm_fb_div_range[0]),
~RANGE0_PWM_FEEDBACK_DIV_MASK);
- WREG32(FVTHROT_PWM_FEEDBACK_DIV_REG2,
(RANGE1_PWM_FEEDBACK_DIV(fv_throt_pwm_fb_div_range[1]) |
RANGE2_PWM_FEEDBACK_DIV(fv_throt_pwm_fb_div_range[2])));
- WREG32(FVTHROT_PWM_FEEDBACK_DIV_REG3,
(RANGE0_PWM(fv_throt_pwm_range[1]) |
RANGE1_PWM(fv_throt_pwm_range[2])));
- WREG32(FVTHROT_PWM_FEEDBACK_DIV_REG4,
(RANGE2_PWM(fv_throt_pwm_range[1]) |
RANGE3_PWM(fv_throt_pwm_range[2])));
+}
+static void rs780_clk_scaling_enable(struct radeon_device *rdev, bool enable) +{
- if (enable)
WREG32_P(FVTHROT_CNTRL_REG, ENABLE_FV_THROT | ENABLE_FV_UPDATE,
~(ENABLE_FV_THROT | ENABLE_FV_UPDATE));
- else
WREG32_P(FVTHROT_CNTRL_REG, 0,
~(ENABLE_FV_THROT | ENABLE_FV_UPDATE));
+}
+static void rs780_voltage_scaling_enable(struct radeon_device *rdev, bool enable) +{
- if (enable)
WREG32_P(FVTHROT_CNTRL_REG, ENABLE_FV_THROT_IO, ~ENABLE_FV_THROT_IO);
- else
WREG32_P(FVTHROT_CNTRL_REG, 0, ~ENABLE_FV_THROT_IO);
+}
+static void rs780_set_engine_clock_wfc(struct radeon_device *rdev) +{
- WREG32(FVTHROT_UTC0, RS780_FVTHROTUTC0_DFLT);
- WREG32(FVTHROT_UTC1, RS780_FVTHROTUTC1_DFLT);
- WREG32(FVTHROT_UTC2, RS780_FVTHROTUTC2_DFLT);
- WREG32(FVTHROT_UTC3, RS780_FVTHROTUTC3_DFLT);
- WREG32(FVTHROT_UTC4, RS780_FVTHROTUTC4_DFLT);
- WREG32(FVTHROT_DTC0, RS780_FVTHROTDTC0_DFLT);
- WREG32(FVTHROT_DTC1, RS780_FVTHROTDTC1_DFLT);
- WREG32(FVTHROT_DTC2, RS780_FVTHROTDTC2_DFLT);
- WREG32(FVTHROT_DTC3, RS780_FVTHROTDTC3_DFLT);
- WREG32(FVTHROT_DTC4, RS780_FVTHROTDTC4_DFLT);
+}
+static void rs780_set_engine_clock_sc(struct radeon_device *rdev) +{
- WREG32_P(FVTHROT_FBDIV_REG2,
FB_DIV_TIMER_VAL(RS780_FBDIVTIMERVAL_DFLT),
~FB_DIV_TIMER_VAL_MASK);
- WREG32_P(FVTHROT_CNTRL_REG,
REFRESH_RATE_DIVISOR(0) | MINIMUM_CIP(0xf),
~(REFRESH_RATE_DIVISOR_MASK | MINIMUM_CIP_MASK));
+}
+static void rs780_set_engine_clock_tdc(struct radeon_device *rdev) +{
- WREG32_P(FVTHROT_CNTRL_REG, 0, ~(FORCE_TREND_SEL | TREND_SEL_MODE));
+}
+static void rs780_set_engine_clock_ssc(struct radeon_device *rdev) +{
- WREG32(FVTHROT_FB_US_REG0, RS780_FVTHROTFBUSREG0_DFLT);
- WREG32(FVTHROT_FB_US_REG1, RS780_FVTHROTFBUSREG1_DFLT);
- WREG32(FVTHROT_FB_DS_REG0, RS780_FVTHROTFBDSREG0_DFLT);
- WREG32(FVTHROT_FB_DS_REG1, RS780_FVTHROTFBDSREG1_DFLT);
- WREG32_P(FVTHROT_FBDIV_REG1, MAX_FEEDBACK_STEP(1), ~MAX_FEEDBACK_STEP_MASK);
+}
+static void rs780_program_at(struct radeon_device *rdev) +{
- struct igp_power_info *pi = rs780_get_pi(rdev);
- WREG32(FVTHROT_TARGET_REG, 30000000 / pi->refresh_rate);
- WREG32(FVTHROT_CB1, 1000000 * 5 / pi->refresh_rate);
- WREG32(FVTHROT_CB2, 1000000 * 10 / pi->refresh_rate);
- WREG32(FVTHROT_CB3, 1000000 * 30 / pi->refresh_rate);
- WREG32(FVTHROT_CB4, 1000000 * 50 / pi->refresh_rate);
+}
+static void rs780_disable_vbios_powersaving(struct radeon_device *rdev) +{
- WREG32_P(CG_INTGFX_MISC, 0, ~0xFFF00000);
+}
+static void rs780_force_voltage_to_high(struct radeon_device *rdev) +{
- struct igp_power_info *pi = rs780_get_pi(rdev);
- struct igp_ps *current_state = rs780_get_ps(rdev->pm.dpm.current_ps);
- if ((current_state->max_voltage == RS780_VDDC_LEVEL_HIGH) &&
(current_state->min_voltage == RS780_VDDC_LEVEL_HIGH))
return;
- WREG32_P(GFX_MACRO_BYPASS_CNTL, SPLL_BYPASS_CNTL, ~SPLL_BYPASS_CNTL);
- udelay(1);
- WREG32_P(FVTHROT_PWM_CTRL_REG0,
STARTING_PWM_HIGHTIME(pi->max_voltage),
~STARTING_PWM_HIGHTIME_MASK);
- WREG32_P(FVTHROT_PWM_CTRL_REG0,
FORCE_STARTING_PWM_HIGHTIME, ~FORCE_STARTING_PWM_HIGHTIME);
- WREG32_P(FVTHROT_PWM_FEEDBACK_DIV_REG1, 0,
~RANGE_PWM_FEEDBACK_DIV_EN);
- udelay(1);
- WREG32_P(GFX_MACRO_BYPASS_CNTL, 0, ~SPLL_BYPASS_CNTL);
+}
+static int rs780_set_engine_clock_scaling(struct radeon_device *rdev) +{
- struct atom_clock_dividers min_dividers, max_dividers, current_max_dividers;
- struct igp_ps *new_state = rs780_get_ps(rdev->pm.dpm.requested_ps);
- struct igp_ps *old_state = rs780_get_ps(rdev->pm.dpm.current_ps);
- int ret;
- if ((new_state->sclk_high == old_state->sclk_high) &&
(new_state->sclk_low == old_state->sclk_low))
return 0;
- ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
new_state->sclk_low, false, &min_dividers);
- if (ret)
return ret;
- ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
new_state->sclk_high, false, &max_dividers);
- if (ret)
return ret;
- ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
old_state->sclk_high, false, ¤t_max_dividers);
- if (ret)
return ret;
- WREG32_P(GFX_MACRO_BYPASS_CNTL, SPLL_BYPASS_CNTL, ~SPLL_BYPASS_CNTL);
- WREG32_P(FVTHROT_FBDIV_REG2, FORCED_FEEDBACK_DIV(max_dividers.fb_div),
~FORCED_FEEDBACK_DIV_MASK);
- WREG32_P(FVTHROT_FBDIV_REG1, STARTING_FEEDBACK_DIV(max_dividers.fb_div),
~STARTING_FEEDBACK_DIV_MASK);
- WREG32_P(FVTHROT_FBDIV_REG1, FORCE_FEEDBACK_DIV, ~FORCE_FEEDBACK_DIV);
- udelay(100);
- WREG32_P(GFX_MACRO_BYPASS_CNTL, 0, ~SPLL_BYPASS_CNTL);
- if (max_dividers.fb_div > min_dividers.fb_div) {
WREG32_P(FVTHROT_FBDIV_REG0,
MIN_FEEDBACK_DIV(min_dividers.fb_div) |
MAX_FEEDBACK_DIV(max_dividers.fb_div),
~(MIN_FEEDBACK_DIV_MASK | MAX_FEEDBACK_DIV_MASK));
WREG32_P(FVTHROT_FBDIV_REG1, 0, ~FORCE_FEEDBACK_DIV);
- }
- return 0;
+}
+static void rs780_set_engine_clock_spc(struct radeon_device *rdev) +{
- struct igp_ps *new_state = rs780_get_ps(rdev->pm.dpm.requested_ps);
- struct igp_ps *old_state = rs780_get_ps(rdev->pm.dpm.current_ps);
- struct igp_power_info *pi = rs780_get_pi(rdev);
- if ((new_state->sclk_high == old_state->sclk_high) &&
(new_state->sclk_low == old_state->sclk_low))
return;
- if (pi->crtc_id == 0)
WREG32_P(CG_INTGFX_MISC, 0, ~FVTHROT_VBLANK_SEL);
- else
WREG32_P(CG_INTGFX_MISC, FVTHROT_VBLANK_SEL, ~FVTHROT_VBLANK_SEL);
+}
+static void rs780_activate_engine_clk_scaling(struct radeon_device *rdev) +{
- struct igp_ps *new_state = rs780_get_ps(rdev->pm.dpm.requested_ps);
- struct igp_ps *old_state = rs780_get_ps(rdev->pm.dpm.current_ps);
- if ((new_state->sclk_high == old_state->sclk_high) &&
(new_state->sclk_low == old_state->sclk_low))
return;
- rs780_clk_scaling_enable(rdev, true);
+}
+static u32 rs780_get_voltage_for_vddc_level(struct radeon_device *rdev,
enum rs780_vddc_level vddc)
+{
- struct igp_power_info *pi = rs780_get_pi(rdev);
- if (vddc == RS780_VDDC_LEVEL_HIGH)
return pi->max_voltage;
- else if (vddc == RS780_VDDC_LEVEL_LOW)
return pi->min_voltage;
- else
return pi->max_voltage;
+}
+static void rs780_enable_voltage_scaling(struct radeon_device *rdev) +{
- struct igp_ps *new_state = rs780_get_ps(rdev->pm.dpm.requested_ps);
- struct igp_power_info *pi = rs780_get_pi(rdev);
- enum rs780_vddc_level vddc_high, vddc_low;
- udelay(100);
- if ((new_state->max_voltage == RS780_VDDC_LEVEL_HIGH) &&
(new_state->min_voltage == RS780_VDDC_LEVEL_HIGH))
return;
- vddc_high = rs780_get_voltage_for_vddc_level(rdev,
new_state->max_voltage);
- vddc_low = rs780_get_voltage_for_vddc_level(rdev,
new_state->min_voltage);
- WREG32_P(GFX_MACRO_BYPASS_CNTL, SPLL_BYPASS_CNTL, ~SPLL_BYPASS_CNTL);
- udelay(1);
- if (vddc_high > vddc_low) {
WREG32_P(FVTHROT_PWM_FEEDBACK_DIV_REG1,
RANGE_PWM_FEEDBACK_DIV_EN, ~RANGE_PWM_FEEDBACK_DIV_EN);
WREG32_P(FVTHROT_PWM_CTRL_REG0, 0, ~FORCE_STARTING_PWM_HIGHTIME);
- } else if (vddc_high == vddc_low) {
if (pi->max_voltage != vddc_high) {
WREG32_P(FVTHROT_PWM_CTRL_REG0,
STARTING_PWM_HIGHTIME(vddc_high),
~STARTING_PWM_HIGHTIME_MASK);
WREG32_P(FVTHROT_PWM_CTRL_REG0,
FORCE_STARTING_PWM_HIGHTIME,
~FORCE_STARTING_PWM_HIGHTIME);
}
- }
- WREG32_P(GFX_MACRO_BYPASS_CNTL, 0, ~SPLL_BYPASS_CNTL);
+}
+int rs780_dpm_enable(struct radeon_device *rdev) +{
- struct igp_power_info *pi = rs780_get_pi(rdev);
- rs780_get_pm_mode_parameters(rdev);
- rs780_disable_vbios_powersaving(rdev);
- if (r600_dynamicpm_enabled(rdev))
return -EINVAL;
- if (rs780_initialize_dpm_parameters(rdev))
return -EINVAL;
- rs780_start_dpm(rdev);
- rs780_preset_ranges_slow_clk_fbdiv_en(rdev);
- rs780_preset_starting_fbdiv(rdev);
- if (pi->voltage_control)
rs780_voltage_scaling_init(rdev);
- rs780_clk_scaling_enable(rdev, true);
- rs780_set_engine_clock_sc(rdev);
- rs780_set_engine_clock_wfc(rdev);
- rs780_program_at(rdev);
- rs780_set_engine_clock_tdc(rdev);
- rs780_set_engine_clock_ssc(rdev);
- if (pi->gfx_clock_gating)
r600_gfx_clockgating_enable(rdev, true);
- return 0;
+}
+void rs780_dpm_disable(struct radeon_device *rdev) +{
- struct igp_power_info *pi = rs780_get_pi(rdev);
- r600_dynamicpm_enable(rdev, false);
- rs780_clk_scaling_enable(rdev, false);
- rs780_voltage_scaling_enable(rdev, false);
- if (pi->gfx_clock_gating)
r600_gfx_clockgating_enable(rdev, false);
+}
+int rs780_dpm_set_power_state(struct radeon_device *rdev) +{
- struct igp_power_info *pi = rs780_get_pi(rdev);
- rs780_get_pm_mode_parameters(rdev);
- if (pi->voltage_control) {
rs780_force_voltage_to_high(rdev);
mdelay(5);
- }
- rs780_set_engine_clock_scaling(rdev);
- rs780_set_engine_clock_spc(rdev);
- rs780_activate_engine_clk_scaling(rdev);
- if (pi->voltage_control)
rs780_enable_voltage_scaling(rdev);
- return 0;
+}
+void rs780_dpm_setup_asic(struct radeon_device *rdev) +{
+}
+void rs780_dpm_display_configuration_changed(struct radeon_device *rdev) +{
- rs780_get_pm_mode_parameters(rdev);
- rs780_program_at(rdev);
+}
+union igp_info {
- struct _ATOM_INTEGRATED_SYSTEM_INFO info;
- struct _ATOM_INTEGRATED_SYSTEM_INFO_V2 info_2;
+};
+union power_info {
- struct _ATOM_POWERPLAY_INFO info;
- struct _ATOM_POWERPLAY_INFO_V2 info_2;
- struct _ATOM_POWERPLAY_INFO_V3 info_3;
- struct _ATOM_PPLIB_POWERPLAYTABLE pplib;
- struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2;
- struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3;
+};
+union pplib_clock_info {
- struct _ATOM_PPLIB_R600_CLOCK_INFO r600;
- struct _ATOM_PPLIB_RS780_CLOCK_INFO rs780;
- struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO evergreen;
- struct _ATOM_PPLIB_SUMO_CLOCK_INFO sumo;
+};
+union pplib_power_state {
- struct _ATOM_PPLIB_STATE v1;
- struct _ATOM_PPLIB_STATE_V2 v2;
+};
+static void rs780_parse_pplib_non_clock_info(struct radeon_device *rdev,
struct radeon_ps *rps,
struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info,
u8 table_rev)
+{
- rps->caps = le32_to_cpu(non_clock_info->ulCapsAndSettings);
- rps->class = le16_to_cpu(non_clock_info->usClassification);
- rps->class2 = le16_to_cpu(non_clock_info->usClassification2);
- if (ATOM_PPLIB_NONCLOCKINFO_VER1 < table_rev) {
rps->vclk = le32_to_cpu(non_clock_info->ulVCLK);
rps->dclk = le32_to_cpu(non_clock_info->ulDCLK);
- } else if (r600_is_uvd_state(rps->class, rps->class2)) {
rps->vclk = RS780_DEFAULT_VCLK_FREQ;
rps->dclk = RS780_DEFAULT_DCLK_FREQ;
- } else {
rps->vclk = 0;
rps->dclk = 0;
- }
- if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT)
rdev->pm.dpm.boot_ps = rps;
- if (rps->class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE)
rdev->pm.dpm.uvd_ps = rps;
+}
+static void rs780_parse_pplib_clock_info(struct radeon_device *rdev,
struct radeon_ps *rps,
union pplib_clock_info *clock_info)
+{
- struct igp_ps *ps = rs780_get_ps(rps);
- u32 sclk;
- sclk = le16_to_cpu(clock_info->rs780.usLowEngineClockLow);
- sclk |= clock_info->rs780.ucLowEngineClockHigh << 16;
- ps->sclk_low = sclk;
- sclk = le16_to_cpu(clock_info->rs780.usHighEngineClockLow);
- sclk |= clock_info->rs780.ucHighEngineClockHigh << 16;
- ps->sclk_high = sclk;
- switch (le16_to_cpu(clock_info->rs780.usVDDC)) {
- case ATOM_PPLIB_RS780_VOLTAGE_NONE:
- default:
ps->min_voltage = RS780_VDDC_LEVEL_UNKNOWN;
ps->max_voltage = RS780_VDDC_LEVEL_UNKNOWN;
break;
- case ATOM_PPLIB_RS780_VOLTAGE_LOW:
ps->min_voltage = RS780_VDDC_LEVEL_LOW;
ps->max_voltage = RS780_VDDC_LEVEL_LOW;
break;
- case ATOM_PPLIB_RS780_VOLTAGE_HIGH:
ps->min_voltage = RS780_VDDC_LEVEL_HIGH;
ps->max_voltage = RS780_VDDC_LEVEL_HIGH;
break;
- case ATOM_PPLIB_RS780_VOLTAGE_VARIABLE:
ps->min_voltage = RS780_VDDC_LEVEL_LOW;
ps->max_voltage = RS780_VDDC_LEVEL_HIGH;
break;
- }
- ps->flags = le32_to_cpu(clock_info->rs780.ulFlags);
- if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT) {
ps->sclk_low = rdev->clock.default_sclk;
ps->sclk_high = rdev->clock.default_sclk;
ps->min_voltage = RS780_VDDC_LEVEL_HIGH;
ps->max_voltage = RS780_VDDC_LEVEL_HIGH;
- }
+}
+static int rs780_parse_power_table(struct radeon_device *rdev) +{
- struct radeon_mode_info *mode_info = &rdev->mode_info;
- struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info;
- union pplib_power_state *power_state;
- int i;
- union pplib_clock_info *clock_info;
- union power_info *power_info;
- int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo);
u16 data_offset;
- u8 frev, crev;
- struct igp_ps *ps;
- if (!atom_parse_data_header(mode_info->atom_context, index, NULL,
&frev, &crev, &data_offset))
return -EINVAL;
- power_info = (union power_info *)(mode_info->atom_context->bios + data_offset);
- rdev->pm.dpm.ps = kzalloc(sizeof(struct radeon_ps) *
power_info->pplib.ucNumStates, GFP_KERNEL);
- if (!rdev->pm.dpm.ps)
return -ENOMEM;
- rdev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps);
- rdev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime);
- rdev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime);
- for (i = 0; i < power_info->pplib.ucNumStates; i++) {
power_state = (union pplib_power_state *)
(mode_info->atom_context->bios + data_offset +
le16_to_cpu(power_info->pplib.usStateArrayOffset) +
i * power_info->pplib.ucStateEntrySize);
non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *)
(mode_info->atom_context->bios + data_offset +
le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset) +
(power_state->v1.ucNonClockStateIndex *
power_info->pplib.ucNonClockSize));
if (power_info->pplib.ucStateEntrySize - 1) {
clock_info = (union pplib_clock_info *)
(mode_info->atom_context->bios + data_offset +
le16_to_cpu(power_info->pplib.usClockInfoArrayOffset) +
(power_state->v1.ucClockStateIndices[0] *
power_info->pplib.ucClockInfoSize));
ps = kzalloc(sizeof(struct igp_ps), GFP_KERNEL);
if (ps == NULL) {
kfree(rdev->pm.dpm.ps);
return -ENOMEM;
}
rdev->pm.dpm.ps[i].ps_priv = ps;
rs780_parse_pplib_non_clock_info(rdev, &rdev->pm.dpm.ps[i],
non_clock_info,
power_info->pplib.ucNonClockSize);
rs780_parse_pplib_clock_info(rdev,
&rdev->pm.dpm.ps[i],
clock_info);
}
- }
- rdev->pm.dpm.num_ps = power_info->pplib.ucNumStates;
- return 0;
+}
+int rs780_dpm_init(struct radeon_device *rdev) +{
- struct igp_power_info *pi;
- int index = GetIndexIntoMasterTable(DATA, IntegratedSystemInfo);
- union igp_info *info;
- u16 data_offset;
- u8 frev, crev;
- int ret;
- pi = kzalloc(sizeof(struct igp_power_info), GFP_KERNEL);
- if (pi == NULL)
return -ENOMEM;
- rdev->pm.dpm.priv = pi;
- ret = rs780_parse_power_table(rdev);
- if (ret)
return ret;
- pi->voltage_control = false;
- pi->gfx_clock_gating = true;
- if (atom_parse_data_header(rdev->mode_info.atom_context, index, NULL,
&frev, &crev, &data_offset)) {
info = (union igp_info *)(rdev->mode_info.atom_context->bios + data_offset);
/* Get various system informations from bios */
switch (crev) {
case 1:
pi->num_of_cycles_in_period =
info->info.ucNumberOfCyclesInPeriod;
pi->num_of_cycles_in_period |=
info->info.ucNumberOfCyclesInPeriodHi << 8;
pi->invert_pwm_required =
(pi->num_of_cycles_in_period & 0x8000) ? true : false;
pi->boot_voltage = info->info.ucStartingPWM_HighTime;
pi->max_voltage = info->info.ucMaxNBVoltage;
pi->max_voltage |= info->info.ucMaxNBVoltageHigh << 8;
pi->min_voltage = info->info.ucMinNBVoltage;
pi->min_voltage |= info->info.ucMinNBVoltageHigh << 8;
pi->inter_voltage_low =
le16_to_cpu(info->info.usInterNBVoltageLow);
pi->inter_voltage_high =
le16_to_cpu(info->info.usInterNBVoltageHigh);
pi->voltage_control = true;
pi->bootup_uma_clk = info->info.usK8MemoryClock * 100;
break;
case 2:
pi->num_of_cycles_in_period =
le16_to_cpu(info->info_2.usNumberOfCyclesInPeriod);
pi->invert_pwm_required =
(pi->num_of_cycles_in_period & 0x8000) ? true : false;
pi->boot_voltage =
le16_to_cpu(info->info_2.usBootUpNBVoltage);
pi->max_voltage =
le16_to_cpu(info->info_2.usMaxNBVoltage);
pi->min_voltage =
le16_to_cpu(info->info_2.usMinNBVoltage);
pi->system_config =
le32_to_cpu(info->info_2.ulSystemConfig);
pi->pwm_voltage_control =
(pi->system_config & 0x4) ? true : false;
pi->voltage_control = true;
pi->bootup_uma_clk = le32_to_cpu(info->info_2.ulBootUpUMAClock);
break;
default:
DRM_ERROR("No integrated system info for your GPU\n");
return -EINVAL;
}
if (pi->min_voltage > pi->max_voltage)
pi->voltage_control = false;
if (pi->pwm_voltage_control) {
if ((pi->num_of_cycles_in_period == 0) ||
(pi->max_voltage == 0) ||
(pi->min_voltage == 0))
pi->voltage_control = false;
} else {
if ((pi->num_of_cycles_in_period == 0) ||
(pi->max_voltage == 0))
pi->voltage_control = false;
}
return 0;
- }
- radeon_dpm_fini(rdev);
- return -EINVAL;
+}
+void rs780_dpm_print_power_state(struct radeon_device *rdev,
struct radeon_ps *rps)
+{
- struct igp_ps *ps = rs780_get_ps(rps);
- r600_dpm_print_class_info(rps->class, rps->class2);
- r600_dpm_print_cap_info(rps->caps);
- printk("\tuvd vclk: %d dclk: %d\n", rps->vclk, rps->dclk);
- printk("\t\tpower level 0 sclk: %u vddc_index: %d\n",
ps->sclk_low, ps->min_voltage);
- printk("\t\tpower level 1 sclk: %u vddc_index: %d\n",
ps->sclk_high, ps->max_voltage);
- r600_dpm_print_ps_status(rdev, rps);
+}
+void rs780_dpm_fini(struct radeon_device *rdev) +{
- int i;
- for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
kfree(rdev->pm.dpm.ps[i].ps_priv);
- }
- kfree(rdev->pm.dpm.ps);
- kfree(rdev->pm.dpm.priv);
+}
+u32 rs780_dpm_get_sclk(struct radeon_device *rdev, bool low) +{
- struct igp_ps *requested_state = rs780_get_ps(rdev->pm.dpm.requested_ps);
- if (low)
return requested_state->sclk_low;
- else
return requested_state->sclk_high;
+}
+u32 rs780_dpm_get_mclk(struct radeon_device *rdev, bool low) +{
- struct igp_power_info *pi = rs780_get_pi(rdev);
- return pi->bootup_uma_clk;
+} diff --git a/drivers/gpu/drm/radeon/rs780_dpm.h b/drivers/gpu/drm/radeon/rs780_dpm.h new file mode 100644 index 0000000..47a40b1 --- /dev/null +++ b/drivers/gpu/drm/radeon/rs780_dpm.h @@ -0,0 +1,109 @@ +/*
- Copyright 2011 Advanced Micro Devices, Inc.
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- OTHER DEALINGS IN THE SOFTWARE.
- */
+#ifndef __RS780_DPM_H__ +#define __RS780_DPM_H__
+enum rs780_vddc_level {
- RS780_VDDC_LEVEL_UNKNOWN = 0,
- RS780_VDDC_LEVEL_LOW = 1,
- RS780_VDDC_LEVEL_HIGH = 2,
+};
+struct igp_power_info {
- /* flags */
- bool invert_pwm_required;
- bool pwm_voltage_control;
- bool voltage_control;
- bool gfx_clock_gating;
- /* stored values */
- u32 system_config;
- u32 bootup_uma_clk;
- u16 max_voltage;
- u16 min_voltage;
- u16 boot_voltage;
- u16 inter_voltage_low;
- u16 inter_voltage_high;
- u16 num_of_cycles_in_period;
- /* variable */
- int crtc_id;
- int refresh_rate;
+};
+struct igp_ps {
- enum rs780_vddc_level min_voltage;
- enum rs780_vddc_level max_voltage;
- u32 sclk_low;
- u32 sclk_high;
- u32 flags;
+};
+#define RS780_CGFTV_DFLT 0x0303000f +#define RS780_FBDIVTIMERVAL_DFLT 0x2710
+#define RS780_FVTHROTUTC0_DFLT 0x04010040 +#define RS780_FVTHROTUTC1_DFLT 0x04010040 +#define RS780_FVTHROTUTC2_DFLT 0x04010040 +#define RS780_FVTHROTUTC3_DFLT 0x04010040 +#define RS780_FVTHROTUTC4_DFLT 0x04010040
+#define RS780_FVTHROTDTC0_DFLT 0x04010040 +#define RS780_FVTHROTDTC1_DFLT 0x04010040 +#define RS780_FVTHROTDTC2_DFLT 0x04010040 +#define RS780_FVTHROTDTC3_DFLT 0x04010040 +#define RS780_FVTHROTDTC4_DFLT 0x04010040
+#define RS780_FVTHROTFBUSREG0_DFLT 0x00001001 +#define RS780_FVTHROTFBUSREG1_DFLT 0x00002002 +#define RS780_FVTHROTFBDSREG0_DFLT 0x00004001 +#define RS780_FVTHROTFBDSREG1_DFLT 0x00020010
+#define RS780_FVTHROTPWMUSREG0_DFLT 0x00002001 +#define RS780_FVTHROTPWMUSREG1_DFLT 0x00004003 +#define RS780_FVTHROTPWMDSREG0_DFLT 0x00002001 +#define RS780_FVTHROTPWMDSREG1_DFLT 0x00004003
+#define RS780_FVTHROTPWMFBDIVRANGEREG0_DFLT 0x37 +#define RS780_FVTHROTPWMFBDIVRANGEREG1_DFLT 0x4b +#define RS780_FVTHROTPWMFBDIVRANGEREG2_DFLT 0x8b
+#define RS780D_FVTHROTPWMFBDIVRANGEREG0_DFLT 0x8b +#define RS780D_FVTHROTPWMFBDIVRANGEREG1_DFLT 0x8c +#define RS780D_FVTHROTPWMFBDIVRANGEREG2_DFLT 0xb5
+#define RS880D_FVTHROTPWMFBDIVRANGEREG0_DFLT 0x8d +#define RS880D_FVTHROTPWMFBDIVRANGEREG1_DFLT 0x8e +#define RS880D_FVTHROTPWMFBDIVRANGEREG2_DFLT 0xBa
+#define RS780_FVTHROTPWMRANGE0_GPIO_DFLT 0x1a +#define RS780_FVTHROTPWMRANGE1_GPIO_DFLT 0x1a +#define RS780_FVTHROTPWMRANGE2_GPIO_DFLT 0x0 +#define RS780_FVTHROTPWMRANGE3_GPIO_DFLT 0x0
+#define RS780_SLOWCLKFEEDBACKDIV_DFLT 110
+#define RS780_CGCLKGATING_DFLT 0x0000E204
+#define RS780_DEFAULT_VCLK_FREQ 53300 /* 10 khz */ +#define RS780_DEFAULT_DCLK_FREQ 40000 /* 10 khz */
+#endif diff --git a/drivers/gpu/drm/radeon/rs780d.h b/drivers/gpu/drm/radeon/rs780d.h new file mode 100644 index 0000000..b1142ed --- /dev/null +++ b/drivers/gpu/drm/radeon/rs780d.h @@ -0,0 +1,168 @@ +/*
- Copyright 2011 Advanced Micro Devices, Inc.
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- OTHER DEALINGS IN THE SOFTWARE.
- */
+#ifndef __RS780D_H__ +#define __RS780D_H__
+#define CG_SPLL_FUNC_CNTL 0x600 +# define SPLL_RESET (1 << 0) +# define SPLL_SLEEP (1 << 1) +# define SPLL_REF_DIV(x) ((x) << 2) +# define SPLL_REF_DIV_MASK (7 << 2) +# define SPLL_FB_DIV(x) ((x) << 5) +# define SPLL_FB_DIV_MASK (0xff << 2) +# define SPLL_FB_DIV_SHIFT 2 +# define SPLL_PULSEEN (1 << 13) +# define SPLL_PULSENUM(x) ((x) << 14) +# define SPLL_PULSENUM_MASK (3 << 14) +# define SPLL_SW_HILEN(x) ((x) << 16) +# define SPLL_SW_HILEN_MASK (0xf << 16) +# define SPLL_SW_LOLEN(x) ((x) << 20) +# define SPLL_SW_LOLEN_MASK (0xf << 20) +# define SPLL_DIVEN (1 << 24) +# define SPLL_BYPASS_EN (1 << 25) +# define SPLL_CHG_STATUS (1 << 29) +# define SPLL_CTLREQ (1 << 30) +# define SPLL_CTLACK (1 << 31)
+/* RS780/RS880 PM */ +#define FVTHROT_CNTRL_REG 0x3000 +#define DONT_WAIT_FOR_FBDIV_WRAP (1 << 0) +#define MINIMUM_CIP(x) ((x) << 1) +#define MINIMUM_CIP_SHIFT 1 +#define MINIMUM_CIP_MASK 0x1fffffe +#define REFRESH_RATE_DIVISOR(x) ((x) << 25) +#define REFRESH_RATE_DIVISOR_SHIFT 25 +#define REFRESH_RATE_DIVISOR_MASK (0x3 << 25) +#define ENABLE_FV_THROT (1 << 27) +#define ENABLE_FV_UPDATE (1 << 28) +#define TREND_SEL_MODE (1 << 29) +#define FORCE_TREND_SEL (1 << 30) +#define ENABLE_FV_THROT_IO (1 << 31) +#define FVTHROT_TARGET_REG 0x3004 +#define TARGET_IDLE_COUNT(x) ((x) << 0) +#define TARGET_IDLE_COUNT_MASK 0xffffff +#define TARGET_IDLE_COUNT_SHIFT 0 +#define FVTHROT_CB1 0x3008 +#define FVTHROT_CB2 0x300c +#define FVTHROT_CB3 0x3010 +#define FVTHROT_CB4 0x3014 +#define FVTHROT_UTC0 0x3018 +#define FVTHROT_UTC1 0x301c +#define FVTHROT_UTC2 0x3020 +#define FVTHROT_UTC3 0x3024 +#define FVTHROT_UTC4 0x3028 +#define FVTHROT_DTC0 0x302c +#define FVTHROT_DTC1 0x3030 +#define FVTHROT_DTC2 0x3034 +#define FVTHROT_DTC3 0x3038 +#define FVTHROT_DTC4 0x303c +#define FVTHROT_FBDIV_REG0 0x3040 +#define MIN_FEEDBACK_DIV(x) ((x) << 0) +#define MIN_FEEDBACK_DIV_MASK 0xfff +#define MIN_FEEDBACK_DIV_SHIFT 0 +#define MAX_FEEDBACK_DIV(x) ((x) << 12) +#define MAX_FEEDBACK_DIV_MASK (0xfff << 12) +#define MAX_FEEDBACK_DIV_SHIFT 12 +#define FVTHROT_FBDIV_REG1 0x3044 +#define MAX_FEEDBACK_STEP(x) ((x) << 0) +#define MAX_FEEDBACK_STEP_MASK 0xfff +#define MAX_FEEDBACK_STEP_SHIFT 0 +#define STARTING_FEEDBACK_DIV(x) ((x) << 12) +#define STARTING_FEEDBACK_DIV_MASK (0xfff << 12) +#define STARTING_FEEDBACK_DIV_SHIFT 12 +#define FORCE_FEEDBACK_DIV (1 << 24) +#define FVTHROT_FBDIV_REG2 0x3048 +#define FORCED_FEEDBACK_DIV(x) ((x) << 0) +#define FORCED_FEEDBACK_DIV_MASK 0xfff +#define FORCED_FEEDBACK_DIV_SHIFT 0 +#define FB_DIV_TIMER_VAL(x) ((x) << 12) +#define FB_DIV_TIMER_VAL_MASK (0xffff << 12) +#define FB_DIV_TIMER_VAL_SHIFT 12 +#define FVTHROT_FB_US_REG0 0x304c +#define FVTHROT_FB_US_REG1 0x3050 +#define FVTHROT_FB_DS_REG0 0x3054 +#define FVTHROT_FB_DS_REG1 0x3058 +#define FVTHROT_PWM_CTRL_REG0 0x305c +#define STARTING_PWM_HIGHTIME(x) ((x) << 0) +#define STARTING_PWM_HIGHTIME_MASK 0xfff +#define STARTING_PWM_HIGHTIME_SHIFT 0 +#define NUMBER_OF_CYCLES_IN_PERIOD(x) ((x) << 12) +#define NUMBER_OF_CYCLES_IN_PERIOD_MASK (0xfff << 12) +#define NUMBER_OF_CYCLES_IN_PERIOD_SHIFT 12 +#define FORCE_STARTING_PWM_HIGHTIME (1 << 24) +#define INVERT_PWM_WAVEFORM (1 << 25) +#define FVTHROT_PWM_CTRL_REG1 0x3060 +#define MIN_PWM_HIGHTIME(x) ((x) << 0) +#define MIN_PWM_HIGHTIME_MASK 0xfff +#define MIN_PWM_HIGHTIME_SHIFT 0 +#define MAX_PWM_HIGHTIME(x) ((x) << 12) +#define MAX_PWM_HIGHTIME_MASK (0xfff << 12) +#define MAX_PWM_HIGHTIME_SHIFT 12 +#define FVTHROT_PWM_US_REG0 0x3064 +#define FVTHROT_PWM_US_REG1 0x3068 +#define FVTHROT_PWM_DS_REG0 0x306c +#define FVTHROT_PWM_DS_REG1 0x3070 +#define FVTHROT_STATUS_REG0 0x3074 +#define CURRENT_FEEDBACK_DIV_MASK 0xfff +#define CURRENT_FEEDBACK_DIV_SHIFT 0 +#define FVTHROT_STATUS_REG1 0x3078 +#define FVTHROT_STATUS_REG2 0x307c +#define CG_INTGFX_MISC 0x3080 +#define FVTHROT_VBLANK_SEL (1 << 9) +#define FVTHROT_PWM_FEEDBACK_DIV_REG1 0x308c +#define RANGE0_PWM_FEEDBACK_DIV(x) ((x) << 0) +#define RANGE0_PWM_FEEDBACK_DIV_MASK 0xfff +#define RANGE0_PWM_FEEDBACK_DIV_SHIFT 0 +#define RANGE_PWM_FEEDBACK_DIV_EN (1 << 12) +#define FVTHROT_PWM_FEEDBACK_DIV_REG2 0x3090 +#define RANGE1_PWM_FEEDBACK_DIV(x) ((x) << 0) +#define RANGE1_PWM_FEEDBACK_DIV_MASK 0xfff +#define RANGE1_PWM_FEEDBACK_DIV_SHIFT 0 +#define RANGE2_PWM_FEEDBACK_DIV(x) ((x) << 12) +#define RANGE2_PWM_FEEDBACK_DIV_MASK (0xfff << 12) +#define RANGE2_PWM_FEEDBACK_DIV_SHIFT 12 +#define FVTHROT_PWM_FEEDBACK_DIV_REG3 0x3094 +#define RANGE0_PWM(x) ((x) << 0) +#define RANGE0_PWM_MASK 0xfff +#define RANGE0_PWM_SHIFT 0 +#define RANGE1_PWM(x) ((x) << 12) +#define RANGE1_PWM_MASK (0xfff << 12) +#define RANGE1_PWM_SHIFT 12 +#define FVTHROT_PWM_FEEDBACK_DIV_REG4 0x3098 +#define RANGE2_PWM(x) ((x) << 0) +#define RANGE2_PWM_MASK 0xfff +#define RANGE2_PWM_SHIFT 0 +#define RANGE3_PWM(x) ((x) << 12) +#define RANGE3_PWM_MASK (0xfff << 12) +#define RANGE3_PWM_SHIFT 12 +#define FVTHROT_SLOW_CLK_FEEDBACK_DIV_REG1 0x30ac +#define RANGE0_SLOW_CLK_FEEDBACK_DIV(x) ((x) << 0) +#define RANGE0_SLOW_CLK_FEEDBACK_DIV_MASK 0xfff +#define RANGE0_SLOW_CLK_FEEDBACK_DIV_SHIFT 0 +#define RANGE_SLOW_CLK_FEEDBACK_DIV_EN (1 << 12)
+#define GFX_MACRO_BYPASS_CNTL 0x30c0 +#define SPLL_BYPASS_CNTL (1 << 0) +#define UPLL_BYPASS_CNTL (1 << 1)
+#endif
1.7.7.5
dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
On Wed, Jun 26, 2013 at 6:46 AM, Jerome Glisse j.glisse@gmail.com wrote:
On Wed, Jun 26, 2013 at 09:22:35AM -0400, alexdeucher@gmail.com wrote:
From: Alex Deucher alexander.deucher@amd.com
This adds dpm support for rs780/rs880 asics. This includes:
- clockgating
- dynamic engine clock scaling
- dynamic voltage scaling
set radeon.dpm=1 to enable it.
Signed-off-by: Alex Deucher alexander.deucher@amd.com
Depending on the answer to inline question :
Reviewed-by: Jerome Glisse jglisse@redhat.com
drivers/gpu/drm/radeon/Makefile | 2 +- drivers/gpu/drm/radeon/radeon_asic.c | 12 + drivers/gpu/drm/radeon/radeon_asic.h | 12 + drivers/gpu/drm/radeon/radeon_pm.c | 7 + drivers/gpu/drm/radeon/rs780_dpm.c | 894 ++++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/rs780_dpm.h | 109 ++++ drivers/gpu/drm/radeon/rs780d.h | 168 +++++++ 7 files changed, 1203 insertions(+), 1 deletions(-) create mode 100644 drivers/gpu/drm/radeon/rs780_dpm.c create mode 100644 drivers/gpu/drm/radeon/rs780_dpm.h create mode 100644 drivers/gpu/drm/radeon/rs780d.h
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index a131a13..e44b046 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile @@ -77,7 +77,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \ evergreen_hdmi.o radeon_trace_points.o ni.o cayman_blit_shaders.o \ atombios_encoders.o radeon_semaphore.o radeon_sa.o atombios_i2c.o si.o \ si_blit_shaders.o radeon_prime.o radeon_uvd.o cik.o cik_blit_shaders.o \
r600_dpm.o
r600_dpm.o rs780_dpm.o
radeon-$(CONFIG_COMPAT) += radeon_ioc32.o radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index d9c8e9a..db3c930 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1194,6 +1194,18 @@ static struct radeon_asic rs780_asic = { .set_clock_gating = NULL, .get_temperature = &rv6xx_get_temp, },
.dpm = {
.init = &rs780_dpm_init,
.setup_asic = &rs780_dpm_setup_asic,
.enable = &rs780_dpm_enable,
.disable = &rs780_dpm_disable,
.set_power_state = &rs780_dpm_set_power_state,
.display_configuration_changed = &rs780_dpm_display_configuration_changed,
.fini = &rs780_dpm_fini,
.get_sclk = &rs780_dpm_get_sclk,
.get_mclk = &rs780_dpm_get_mclk,
.print_power_state = &rs780_dpm_print_power_state,
}, .pflip = { .pre_page_flip = &rs600_pre_page_flip, .page_flip = &rs600_page_flip,
diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 8507cae..134bf57 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -396,6 +396,18 @@ int r600_mc_wait_for_idle(struct radeon_device *rdev); u32 r600_get_xclk(struct radeon_device *rdev); uint64_t r600_get_gpu_clock_counter(struct radeon_device *rdev); int rv6xx_get_temp(struct radeon_device *rdev); +/* rs780 dpm */ +int rs780_dpm_init(struct radeon_device *rdev); +int rs780_dpm_enable(struct radeon_device *rdev); +void rs780_dpm_disable(struct radeon_device *rdev); +int rs780_dpm_set_power_state(struct radeon_device *rdev); +void rs780_dpm_setup_asic(struct radeon_device *rdev); +void rs780_dpm_display_configuration_changed(struct radeon_device *rdev); +void rs780_dpm_fini(struct radeon_device *rdev); +u32 rs780_dpm_get_sclk(struct radeon_device *rdev, bool low); +u32 rs780_dpm_get_mclk(struct radeon_device *rdev, bool low); +void rs780_dpm_print_power_state(struct radeon_device *rdev,
struct radeon_ps *ps);
/* uvd */ int r600_uvd_init(struct radeon_device *rdev); diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 4f5422e..853a8a2 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -1030,6 +1030,13 @@ int radeon_pm_init(struct radeon_device *rdev) { /* enable dpm on rv6xx+ */ switch (rdev->family) {
case CHIP_RS780:
case CHIP_RS880:
if (radeon_dpm == 1)
rdev->pm.pm_method = PM_METHOD_DPM;
else
rdev->pm.pm_method = PM_METHOD_PROFILE;
break; default: /* default to profile method */ rdev->pm.pm_method = PM_METHOD_PROFILE;
diff --git a/drivers/gpu/drm/radeon/rs780_dpm.c b/drivers/gpu/drm/radeon/rs780_dpm.c new file mode 100644 index 0000000..f594900 --- /dev/null +++ b/drivers/gpu/drm/radeon/rs780_dpm.c @@ -0,0 +1,894 @@ +/*
- Copyright 2011 Advanced Micro Devices, 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: Alex Deucher
- */
+#include "drmP.h" +#include "radeon.h" +#include "rs780d.h" +#include "r600_dpm.h" +#include "rs780_dpm.h" +#include "atom.h"
+static struct igp_ps *rs780_get_ps(struct radeon_ps *rps) +{
struct igp_ps *ps = rps->ps_priv;
return ps;
+}
+static struct igp_power_info *rs780_get_pi(struct radeon_device *rdev) +{
struct igp_power_info *pi = rdev->pm.dpm.priv;
return pi;
+}
+static void rs780_get_pm_mode_parameters(struct radeon_device *rdev) +{
struct igp_power_info *pi = rs780_get_pi(rdev);
struct radeon_mode_info *minfo = &rdev->mode_info;
struct drm_crtc *crtc;
struct radeon_crtc *radeon_crtc;
int i;
/* defaults */
pi->crtc_id = 0;
pi->refresh_rate = 60;
for (i = 0; i < rdev->num_crtc; i++) {
crtc = (struct drm_crtc *)minfo->crtcs[i];
if (crtc && crtc->enabled) {
radeon_crtc = to_radeon_crtc(crtc);
pi->crtc_id = radeon_crtc->crtc_id;
if (crtc->mode.htotal && crtc->mode.vtotal)
pi->refresh_rate =
(crtc->mode.clock * 1000) /
(crtc->mode.htotal * crtc->mode.vtotal);
break;
}
}
Ok this looks wrong to me you look for the first enabled crtc but on those iirc there could be 2 so first one might be a low refresh rate and second one an higher one. Thus returning the first one might lead to PM decision that are wrong.
On RS780/RS880, the hardware can only synchronize with a single crtc, so it doesn't really matter which one we pick as long as it's active.
+}
+static void rs780_voltage_scaling_enable(struct radeon_device *rdev, bool enable);
+static int rs780_initialize_dpm_power_state(struct radeon_device *rdev) +{
struct atom_clock_dividers dividers;
struct igp_ps *default_state = rs780_get_ps(rdev->pm.dpm.boot_ps);
int i, ret;
ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
default_state->sclk_low, false, ÷rs);
if (ret)
return ret;
r600_engine_clock_entry_set_reference_divider(rdev, 0, dividers.ref_div);
r600_engine_clock_entry_set_feedback_divider(rdev, 0, dividers.fb_div);
r600_engine_clock_entry_set_post_divider(rdev, 0, dividers.post_div);
if (dividers.enable_post_div)
r600_engine_clock_entry_enable_post_divider(rdev, 0, true);
else
r600_engine_clock_entry_enable_post_divider(rdev, 0, false);
r600_engine_clock_entry_set_step_time(rdev, 0, R600_SST_DFLT);
r600_engine_clock_entry_enable_pulse_skipping(rdev, 0, false);
r600_engine_clock_entry_enable(rdev, 0, true);
for (i = 1; i < R600_PM_NUMBER_OF_SCLKS; i++)
r600_engine_clock_entry_enable(rdev, i, false);
r600_enable_mclk_control(rdev, false);
r600_voltage_control_enable_pins(rdev, 0);
return 0;
+}
+static int rs780_initialize_dpm_parameters(struct radeon_device *rdev) +{
int ret = 0;
int i;
r600_set_bsp(rdev, R600_BSU_DFLT, R600_BSP_DFLT);
r600_set_at(rdev, 0, 0, 0, 0);
r600_set_git(rdev, R600_GICST_DFLT);
for (i = 0; i < R600_PM_NUMBER_OF_TC; i++)
r600_set_tc(rdev, i, 0, 0);
r600_select_td(rdev, R600_TD_DFLT);
r600_set_vrc(rdev, 0);
r600_set_tpu(rdev, R600_TPU_DFLT);
r600_set_tpc(rdev, R600_TPC_DFLT);
r600_set_sstu(rdev, R600_SSTU_DFLT);
r600_set_sst(rdev, R600_SST_DFLT);
r600_set_fctu(rdev, R600_FCTU_DFLT);
r600_set_fct(rdev, R600_FCT_DFLT);
r600_set_vddc3d_oorsu(rdev, R600_VDDC3DOORSU_DFLT);
r600_set_vddc3d_oorphc(rdev, R600_VDDC3DOORPHC_DFLT);
r600_set_vddc3d_oorsdc(rdev, R600_VDDC3DOORSDC_DFLT);
r600_set_ctxcgtt3d_rphc(rdev, R600_CTXCGTT3DRPHC_DFLT);
r600_set_ctxcgtt3d_rsdc(rdev, R600_CTXCGTT3DRSDC_DFLT);
r600_vid_rt_set_vru(rdev, R600_VRU_DFLT);
r600_vid_rt_set_vrt(rdev, R600_VOLTAGERESPONSETIME_DFLT);
r600_vid_rt_set_ssu(rdev, R600_SPLLSTEPUNIT_DFLT);
ret = rs780_initialize_dpm_power_state(rdev);
r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_LOW, 0);
r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_MEDIUM, 0);
r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_HIGH, 0);
r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_LOW, 0);
r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_MEDIUM, 0);
r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_HIGH, 0);
r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_LOW, 0);
r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_MEDIUM, 0);
r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_HIGH, 0);
r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_LOW, R600_DISPLAY_WATERMARK_HIGH);
r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_MEDIUM, R600_DISPLAY_WATERMARK_HIGH);
r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_HIGH, R600_DISPLAY_WATERMARK_HIGH);
r600_power_level_enable(rdev, R600_POWER_LEVEL_CTXSW, false);
r600_power_level_enable(rdev, R600_POWER_LEVEL_HIGH, false);
r600_power_level_enable(rdev, R600_POWER_LEVEL_MEDIUM, false);
r600_power_level_enable(rdev, R600_POWER_LEVEL_LOW, true);
r600_power_level_set_enter_index(rdev, R600_POWER_LEVEL_LOW);
r600_set_vrc(rdev, RS780_CGFTV_DFLT);
return ret;
+}
+static void rs780_start_dpm(struct radeon_device *rdev) +{
r600_enable_sclk_control(rdev, false);
r600_enable_mclk_control(rdev, false);
r600_dynamicpm_enable(rdev, true);
radeon_wait_for_vblank(rdev, 0);
radeon_wait_for_vblank(rdev, 1);
r600_enable_spll_bypass(rdev, true);
r600_wait_for_spll_change(rdev);
r600_enable_spll_bypass(rdev, false);
r600_wait_for_spll_change(rdev);
r600_enable_spll_bypass(rdev, true);
r600_wait_for_spll_change(rdev);
r600_enable_spll_bypass(rdev, false);
r600_wait_for_spll_change(rdev);
r600_enable_sclk_control(rdev, true);
+}
+static void rs780_preset_ranges_slow_clk_fbdiv_en(struct radeon_device *rdev) +{
WREG32_P(FVTHROT_SLOW_CLK_FEEDBACK_DIV_REG1, RANGE_SLOW_CLK_FEEDBACK_DIV_EN,
~RANGE_SLOW_CLK_FEEDBACK_DIV_EN);
WREG32_P(FVTHROT_SLOW_CLK_FEEDBACK_DIV_REG1,
RANGE0_SLOW_CLK_FEEDBACK_DIV(RS780_SLOWCLKFEEDBACKDIV_DFLT),
~RANGE0_SLOW_CLK_FEEDBACK_DIV_MASK);
+}
+static void rs780_preset_starting_fbdiv(struct radeon_device *rdev) +{
u32 fbdiv = (RREG32(CG_SPLL_FUNC_CNTL) & SPLL_FB_DIV_MASK) >> SPLL_FB_DIV_SHIFT;
WREG32_P(FVTHROT_FBDIV_REG1, STARTING_FEEDBACK_DIV(fbdiv),
~STARTING_FEEDBACK_DIV_MASK);
WREG32_P(FVTHROT_FBDIV_REG2, FORCED_FEEDBACK_DIV(fbdiv),
~FORCED_FEEDBACK_DIV_MASK);
WREG32_P(FVTHROT_FBDIV_REG1, FORCE_FEEDBACK_DIV, ~FORCE_FEEDBACK_DIV);
+}
+static void rs780_voltage_scaling_init(struct radeon_device *rdev) +{
struct igp_power_info *pi = rs780_get_pi(rdev);
struct drm_device *dev = rdev->ddev;
u32 fv_throt_pwm_fb_div_range[3];
u32 fv_throt_pwm_range[4];
if (dev->pdev->device == 0x9614) {
fv_throt_pwm_fb_div_range[0] = RS780D_FVTHROTPWMFBDIVRANGEREG0_DFLT;
fv_throt_pwm_fb_div_range[1] = RS780D_FVTHROTPWMFBDIVRANGEREG1_DFLT;
fv_throt_pwm_fb_div_range[2] = RS780D_FVTHROTPWMFBDIVRANGEREG2_DFLT;
} else if ((dev->pdev->device == 0x9714) ||
(dev->pdev->device == 0x9715)) {
fv_throt_pwm_fb_div_range[0] = RS880D_FVTHROTPWMFBDIVRANGEREG0_DFLT;
fv_throt_pwm_fb_div_range[1] = RS880D_FVTHROTPWMFBDIVRANGEREG1_DFLT;
fv_throt_pwm_fb_div_range[2] = RS880D_FVTHROTPWMFBDIVRANGEREG2_DFLT;
} else {
fv_throt_pwm_fb_div_range[0] = RS780_FVTHROTPWMFBDIVRANGEREG0_DFLT;
fv_throt_pwm_fb_div_range[1] = RS780_FVTHROTPWMFBDIVRANGEREG1_DFLT;
fv_throt_pwm_fb_div_range[2] = RS780_FVTHROTPWMFBDIVRANGEREG2_DFLT;
}
if (pi->pwm_voltage_control) {
fv_throt_pwm_range[0] = pi->min_voltage;
fv_throt_pwm_range[1] = pi->min_voltage;
fv_throt_pwm_range[2] = pi->max_voltage;
fv_throt_pwm_range[3] = pi->max_voltage;
} else {
fv_throt_pwm_range[0] = pi->invert_pwm_required ?
RS780_FVTHROTPWMRANGE3_GPIO_DFLT : RS780_FVTHROTPWMRANGE0_GPIO_DFLT;
fv_throt_pwm_range[1] = pi->invert_pwm_required ?
RS780_FVTHROTPWMRANGE2_GPIO_DFLT : RS780_FVTHROTPWMRANGE1_GPIO_DFLT;
fv_throt_pwm_range[2] = pi->invert_pwm_required ?
RS780_FVTHROTPWMRANGE1_GPIO_DFLT : RS780_FVTHROTPWMRANGE2_GPIO_DFLT;
fv_throt_pwm_range[3] = pi->invert_pwm_required ?
RS780_FVTHROTPWMRANGE0_GPIO_DFLT : RS780_FVTHROTPWMRANGE3_GPIO_DFLT;
}
WREG32_P(FVTHROT_PWM_CTRL_REG0,
STARTING_PWM_HIGHTIME(pi->max_voltage),
~STARTING_PWM_HIGHTIME_MASK);
WREG32_P(FVTHROT_PWM_CTRL_REG0,
NUMBER_OF_CYCLES_IN_PERIOD(pi->num_of_cycles_in_period),
~NUMBER_OF_CYCLES_IN_PERIOD_MASK);
WREG32_P(FVTHROT_PWM_CTRL_REG0, FORCE_STARTING_PWM_HIGHTIME,
~FORCE_STARTING_PWM_HIGHTIME);
if (pi->invert_pwm_required)
WREG32_P(FVTHROT_PWM_CTRL_REG0, INVERT_PWM_WAVEFORM, ~INVERT_PWM_WAVEFORM);
else
WREG32_P(FVTHROT_PWM_CTRL_REG0, 0, ~INVERT_PWM_WAVEFORM);
rs780_voltage_scaling_enable(rdev, true);
WREG32(FVTHROT_PWM_CTRL_REG1,
(MIN_PWM_HIGHTIME(pi->min_voltage) |
MAX_PWM_HIGHTIME(pi->max_voltage)));
WREG32(FVTHROT_PWM_US_REG0, RS780_FVTHROTPWMUSREG0_DFLT);
WREG32(FVTHROT_PWM_US_REG1, RS780_FVTHROTPWMUSREG1_DFLT);
WREG32(FVTHROT_PWM_DS_REG0, RS780_FVTHROTPWMDSREG0_DFLT);
WREG32(FVTHROT_PWM_DS_REG1, RS780_FVTHROTPWMDSREG1_DFLT);
WREG32_P(FVTHROT_PWM_FEEDBACK_DIV_REG1,
RANGE0_PWM_FEEDBACK_DIV(fv_throt_pwm_fb_div_range[0]),
~RANGE0_PWM_FEEDBACK_DIV_MASK);
WREG32(FVTHROT_PWM_FEEDBACK_DIV_REG2,
(RANGE1_PWM_FEEDBACK_DIV(fv_throt_pwm_fb_div_range[1]) |
RANGE2_PWM_FEEDBACK_DIV(fv_throt_pwm_fb_div_range[2])));
WREG32(FVTHROT_PWM_FEEDBACK_DIV_REG3,
(RANGE0_PWM(fv_throt_pwm_range[1]) |
RANGE1_PWM(fv_throt_pwm_range[2])));
WREG32(FVTHROT_PWM_FEEDBACK_DIV_REG4,
(RANGE2_PWM(fv_throt_pwm_range[1]) |
RANGE3_PWM(fv_throt_pwm_range[2])));
+}
+static void rs780_clk_scaling_enable(struct radeon_device *rdev, bool enable) +{
if (enable)
WREG32_P(FVTHROT_CNTRL_REG, ENABLE_FV_THROT | ENABLE_FV_UPDATE,
~(ENABLE_FV_THROT | ENABLE_FV_UPDATE));
else
WREG32_P(FVTHROT_CNTRL_REG, 0,
~(ENABLE_FV_THROT | ENABLE_FV_UPDATE));
+}
+static void rs780_voltage_scaling_enable(struct radeon_device *rdev, bool enable) +{
if (enable)
WREG32_P(FVTHROT_CNTRL_REG, ENABLE_FV_THROT_IO, ~ENABLE_FV_THROT_IO);
else
WREG32_P(FVTHROT_CNTRL_REG, 0, ~ENABLE_FV_THROT_IO);
+}
+static void rs780_set_engine_clock_wfc(struct radeon_device *rdev) +{
WREG32(FVTHROT_UTC0, RS780_FVTHROTUTC0_DFLT);
WREG32(FVTHROT_UTC1, RS780_FVTHROTUTC1_DFLT);
WREG32(FVTHROT_UTC2, RS780_FVTHROTUTC2_DFLT);
WREG32(FVTHROT_UTC3, RS780_FVTHROTUTC3_DFLT);
WREG32(FVTHROT_UTC4, RS780_FVTHROTUTC4_DFLT);
WREG32(FVTHROT_DTC0, RS780_FVTHROTDTC0_DFLT);
WREG32(FVTHROT_DTC1, RS780_FVTHROTDTC1_DFLT);
WREG32(FVTHROT_DTC2, RS780_FVTHROTDTC2_DFLT);
WREG32(FVTHROT_DTC3, RS780_FVTHROTDTC3_DFLT);
WREG32(FVTHROT_DTC4, RS780_FVTHROTDTC4_DFLT);
+}
+static void rs780_set_engine_clock_sc(struct radeon_device *rdev) +{
WREG32_P(FVTHROT_FBDIV_REG2,
FB_DIV_TIMER_VAL(RS780_FBDIVTIMERVAL_DFLT),
~FB_DIV_TIMER_VAL_MASK);
WREG32_P(FVTHROT_CNTRL_REG,
REFRESH_RATE_DIVISOR(0) | MINIMUM_CIP(0xf),
~(REFRESH_RATE_DIVISOR_MASK | MINIMUM_CIP_MASK));
+}
+static void rs780_set_engine_clock_tdc(struct radeon_device *rdev) +{
WREG32_P(FVTHROT_CNTRL_REG, 0, ~(FORCE_TREND_SEL | TREND_SEL_MODE));
+}
+static void rs780_set_engine_clock_ssc(struct radeon_device *rdev) +{
WREG32(FVTHROT_FB_US_REG0, RS780_FVTHROTFBUSREG0_DFLT);
WREG32(FVTHROT_FB_US_REG1, RS780_FVTHROTFBUSREG1_DFLT);
WREG32(FVTHROT_FB_DS_REG0, RS780_FVTHROTFBDSREG0_DFLT);
WREG32(FVTHROT_FB_DS_REG1, RS780_FVTHROTFBDSREG1_DFLT);
WREG32_P(FVTHROT_FBDIV_REG1, MAX_FEEDBACK_STEP(1), ~MAX_FEEDBACK_STEP_MASK);
+}
+static void rs780_program_at(struct radeon_device *rdev) +{
struct igp_power_info *pi = rs780_get_pi(rdev);
WREG32(FVTHROT_TARGET_REG, 30000000 / pi->refresh_rate);
WREG32(FVTHROT_CB1, 1000000 * 5 / pi->refresh_rate);
WREG32(FVTHROT_CB2, 1000000 * 10 / pi->refresh_rate);
WREG32(FVTHROT_CB3, 1000000 * 30 / pi->refresh_rate);
WREG32(FVTHROT_CB4, 1000000 * 50 / pi->refresh_rate);
+}
+static void rs780_disable_vbios_powersaving(struct radeon_device *rdev) +{
WREG32_P(CG_INTGFX_MISC, 0, ~0xFFF00000);
+}
+static void rs780_force_voltage_to_high(struct radeon_device *rdev) +{
struct igp_power_info *pi = rs780_get_pi(rdev);
struct igp_ps *current_state = rs780_get_ps(rdev->pm.dpm.current_ps);
if ((current_state->max_voltage == RS780_VDDC_LEVEL_HIGH) &&
(current_state->min_voltage == RS780_VDDC_LEVEL_HIGH))
return;
WREG32_P(GFX_MACRO_BYPASS_CNTL, SPLL_BYPASS_CNTL, ~SPLL_BYPASS_CNTL);
udelay(1);
WREG32_P(FVTHROT_PWM_CTRL_REG0,
STARTING_PWM_HIGHTIME(pi->max_voltage),
~STARTING_PWM_HIGHTIME_MASK);
WREG32_P(FVTHROT_PWM_CTRL_REG0,
FORCE_STARTING_PWM_HIGHTIME, ~FORCE_STARTING_PWM_HIGHTIME);
WREG32_P(FVTHROT_PWM_FEEDBACK_DIV_REG1, 0,
~RANGE_PWM_FEEDBACK_DIV_EN);
udelay(1);
WREG32_P(GFX_MACRO_BYPASS_CNTL, 0, ~SPLL_BYPASS_CNTL);
+}
+static int rs780_set_engine_clock_scaling(struct radeon_device *rdev) +{
struct atom_clock_dividers min_dividers, max_dividers, current_max_dividers;
struct igp_ps *new_state = rs780_get_ps(rdev->pm.dpm.requested_ps);
struct igp_ps *old_state = rs780_get_ps(rdev->pm.dpm.current_ps);
int ret;
if ((new_state->sclk_high == old_state->sclk_high) &&
(new_state->sclk_low == old_state->sclk_low))
return 0;
ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
new_state->sclk_low, false, &min_dividers);
if (ret)
return ret;
ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
new_state->sclk_high, false, &max_dividers);
if (ret)
return ret;
ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
old_state->sclk_high, false, ¤t_max_dividers);
if (ret)
return ret;
WREG32_P(GFX_MACRO_BYPASS_CNTL, SPLL_BYPASS_CNTL, ~SPLL_BYPASS_CNTL);
WREG32_P(FVTHROT_FBDIV_REG2, FORCED_FEEDBACK_DIV(max_dividers.fb_div),
~FORCED_FEEDBACK_DIV_MASK);
WREG32_P(FVTHROT_FBDIV_REG1, STARTING_FEEDBACK_DIV(max_dividers.fb_div),
~STARTING_FEEDBACK_DIV_MASK);
WREG32_P(FVTHROT_FBDIV_REG1, FORCE_FEEDBACK_DIV, ~FORCE_FEEDBACK_DIV);
udelay(100);
WREG32_P(GFX_MACRO_BYPASS_CNTL, 0, ~SPLL_BYPASS_CNTL);
if (max_dividers.fb_div > min_dividers.fb_div) {
WREG32_P(FVTHROT_FBDIV_REG0,
MIN_FEEDBACK_DIV(min_dividers.fb_div) |
MAX_FEEDBACK_DIV(max_dividers.fb_div),
~(MIN_FEEDBACK_DIV_MASK | MAX_FEEDBACK_DIV_MASK));
WREG32_P(FVTHROT_FBDIV_REG1, 0, ~FORCE_FEEDBACK_DIV);
}
return 0;
+}
+static void rs780_set_engine_clock_spc(struct radeon_device *rdev) +{
struct igp_ps *new_state = rs780_get_ps(rdev->pm.dpm.requested_ps);
struct igp_ps *old_state = rs780_get_ps(rdev->pm.dpm.current_ps);
struct igp_power_info *pi = rs780_get_pi(rdev);
if ((new_state->sclk_high == old_state->sclk_high) &&
(new_state->sclk_low == old_state->sclk_low))
return;
if (pi->crtc_id == 0)
WREG32_P(CG_INTGFX_MISC, 0, ~FVTHROT_VBLANK_SEL);
else
WREG32_P(CG_INTGFX_MISC, FVTHROT_VBLANK_SEL, ~FVTHROT_VBLANK_SEL);
+}
+static void rs780_activate_engine_clk_scaling(struct radeon_device *rdev) +{
struct igp_ps *new_state = rs780_get_ps(rdev->pm.dpm.requested_ps);
struct igp_ps *old_state = rs780_get_ps(rdev->pm.dpm.current_ps);
if ((new_state->sclk_high == old_state->sclk_high) &&
(new_state->sclk_low == old_state->sclk_low))
return;
rs780_clk_scaling_enable(rdev, true);
+}
+static u32 rs780_get_voltage_for_vddc_level(struct radeon_device *rdev,
enum rs780_vddc_level vddc)
+{
struct igp_power_info *pi = rs780_get_pi(rdev);
if (vddc == RS780_VDDC_LEVEL_HIGH)
return pi->max_voltage;
else if (vddc == RS780_VDDC_LEVEL_LOW)
return pi->min_voltage;
else
return pi->max_voltage;
+}
+static void rs780_enable_voltage_scaling(struct radeon_device *rdev) +{
struct igp_ps *new_state = rs780_get_ps(rdev->pm.dpm.requested_ps);
struct igp_power_info *pi = rs780_get_pi(rdev);
enum rs780_vddc_level vddc_high, vddc_low;
udelay(100);
if ((new_state->max_voltage == RS780_VDDC_LEVEL_HIGH) &&
(new_state->min_voltage == RS780_VDDC_LEVEL_HIGH))
return;
vddc_high = rs780_get_voltage_for_vddc_level(rdev,
new_state->max_voltage);
vddc_low = rs780_get_voltage_for_vddc_level(rdev,
new_state->min_voltage);
WREG32_P(GFX_MACRO_BYPASS_CNTL, SPLL_BYPASS_CNTL, ~SPLL_BYPASS_CNTL);
udelay(1);
if (vddc_high > vddc_low) {
WREG32_P(FVTHROT_PWM_FEEDBACK_DIV_REG1,
RANGE_PWM_FEEDBACK_DIV_EN, ~RANGE_PWM_FEEDBACK_DIV_EN);
WREG32_P(FVTHROT_PWM_CTRL_REG0, 0, ~FORCE_STARTING_PWM_HIGHTIME);
} else if (vddc_high == vddc_low) {
if (pi->max_voltage != vddc_high) {
WREG32_P(FVTHROT_PWM_CTRL_REG0,
STARTING_PWM_HIGHTIME(vddc_high),
~STARTING_PWM_HIGHTIME_MASK);
WREG32_P(FVTHROT_PWM_CTRL_REG0,
FORCE_STARTING_PWM_HIGHTIME,
~FORCE_STARTING_PWM_HIGHTIME);
}
}
WREG32_P(GFX_MACRO_BYPASS_CNTL, 0, ~SPLL_BYPASS_CNTL);
+}
+int rs780_dpm_enable(struct radeon_device *rdev) +{
struct igp_power_info *pi = rs780_get_pi(rdev);
rs780_get_pm_mode_parameters(rdev);
rs780_disable_vbios_powersaving(rdev);
if (r600_dynamicpm_enabled(rdev))
return -EINVAL;
if (rs780_initialize_dpm_parameters(rdev))
return -EINVAL;
rs780_start_dpm(rdev);
rs780_preset_ranges_slow_clk_fbdiv_en(rdev);
rs780_preset_starting_fbdiv(rdev);
if (pi->voltage_control)
rs780_voltage_scaling_init(rdev);
rs780_clk_scaling_enable(rdev, true);
rs780_set_engine_clock_sc(rdev);
rs780_set_engine_clock_wfc(rdev);
rs780_program_at(rdev);
rs780_set_engine_clock_tdc(rdev);
rs780_set_engine_clock_ssc(rdev);
if (pi->gfx_clock_gating)
r600_gfx_clockgating_enable(rdev, true);
return 0;
+}
+void rs780_dpm_disable(struct radeon_device *rdev) +{
struct igp_power_info *pi = rs780_get_pi(rdev);
r600_dynamicpm_enable(rdev, false);
rs780_clk_scaling_enable(rdev, false);
rs780_voltage_scaling_enable(rdev, false);
if (pi->gfx_clock_gating)
r600_gfx_clockgating_enable(rdev, false);
+}
+int rs780_dpm_set_power_state(struct radeon_device *rdev) +{
struct igp_power_info *pi = rs780_get_pi(rdev);
rs780_get_pm_mode_parameters(rdev);
if (pi->voltage_control) {
rs780_force_voltage_to_high(rdev);
mdelay(5);
}
rs780_set_engine_clock_scaling(rdev);
rs780_set_engine_clock_spc(rdev);
rs780_activate_engine_clk_scaling(rdev);
if (pi->voltage_control)
rs780_enable_voltage_scaling(rdev);
return 0;
+}
+void rs780_dpm_setup_asic(struct radeon_device *rdev) +{
+}
+void rs780_dpm_display_configuration_changed(struct radeon_device *rdev) +{
rs780_get_pm_mode_parameters(rdev);
rs780_program_at(rdev);
+}
+union igp_info {
struct _ATOM_INTEGRATED_SYSTEM_INFO info;
struct _ATOM_INTEGRATED_SYSTEM_INFO_V2 info_2;
+};
+union power_info {
struct _ATOM_POWERPLAY_INFO info;
struct _ATOM_POWERPLAY_INFO_V2 info_2;
struct _ATOM_POWERPLAY_INFO_V3 info_3;
struct _ATOM_PPLIB_POWERPLAYTABLE pplib;
struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2;
struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3;
+};
+union pplib_clock_info {
struct _ATOM_PPLIB_R600_CLOCK_INFO r600;
struct _ATOM_PPLIB_RS780_CLOCK_INFO rs780;
struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO evergreen;
struct _ATOM_PPLIB_SUMO_CLOCK_INFO sumo;
+};
+union pplib_power_state {
struct _ATOM_PPLIB_STATE v1;
struct _ATOM_PPLIB_STATE_V2 v2;
+};
+static void rs780_parse_pplib_non_clock_info(struct radeon_device *rdev,
struct radeon_ps *rps,
struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info,
u8 table_rev)
+{
rps->caps = le32_to_cpu(non_clock_info->ulCapsAndSettings);
rps->class = le16_to_cpu(non_clock_info->usClassification);
rps->class2 = le16_to_cpu(non_clock_info->usClassification2);
if (ATOM_PPLIB_NONCLOCKINFO_VER1 < table_rev) {
rps->vclk = le32_to_cpu(non_clock_info->ulVCLK);
rps->dclk = le32_to_cpu(non_clock_info->ulDCLK);
} else if (r600_is_uvd_state(rps->class, rps->class2)) {
rps->vclk = RS780_DEFAULT_VCLK_FREQ;
rps->dclk = RS780_DEFAULT_DCLK_FREQ;
} else {
rps->vclk = 0;
rps->dclk = 0;
}
if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT)
rdev->pm.dpm.boot_ps = rps;
if (rps->class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE)
rdev->pm.dpm.uvd_ps = rps;
+}
+static void rs780_parse_pplib_clock_info(struct radeon_device *rdev,
struct radeon_ps *rps,
union pplib_clock_info *clock_info)
+{
struct igp_ps *ps = rs780_get_ps(rps);
u32 sclk;
sclk = le16_to_cpu(clock_info->rs780.usLowEngineClockLow);
sclk |= clock_info->rs780.ucLowEngineClockHigh << 16;
ps->sclk_low = sclk;
sclk = le16_to_cpu(clock_info->rs780.usHighEngineClockLow);
sclk |= clock_info->rs780.ucHighEngineClockHigh << 16;
ps->sclk_high = sclk;
switch (le16_to_cpu(clock_info->rs780.usVDDC)) {
case ATOM_PPLIB_RS780_VOLTAGE_NONE:
default:
ps->min_voltage = RS780_VDDC_LEVEL_UNKNOWN;
ps->max_voltage = RS780_VDDC_LEVEL_UNKNOWN;
break;
case ATOM_PPLIB_RS780_VOLTAGE_LOW:
ps->min_voltage = RS780_VDDC_LEVEL_LOW;
ps->max_voltage = RS780_VDDC_LEVEL_LOW;
break;
case ATOM_PPLIB_RS780_VOLTAGE_HIGH:
ps->min_voltage = RS780_VDDC_LEVEL_HIGH;
ps->max_voltage = RS780_VDDC_LEVEL_HIGH;
break;
case ATOM_PPLIB_RS780_VOLTAGE_VARIABLE:
ps->min_voltage = RS780_VDDC_LEVEL_LOW;
ps->max_voltage = RS780_VDDC_LEVEL_HIGH;
break;
}
ps->flags = le32_to_cpu(clock_info->rs780.ulFlags);
if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT) {
ps->sclk_low = rdev->clock.default_sclk;
ps->sclk_high = rdev->clock.default_sclk;
ps->min_voltage = RS780_VDDC_LEVEL_HIGH;
ps->max_voltage = RS780_VDDC_LEVEL_HIGH;
}
+}
+static int rs780_parse_power_table(struct radeon_device *rdev) +{
struct radeon_mode_info *mode_info = &rdev->mode_info;
struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info;
union pplib_power_state *power_state;
int i;
union pplib_clock_info *clock_info;
union power_info *power_info;
int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo);
u16 data_offset;
u8 frev, crev;
struct igp_ps *ps;
if (!atom_parse_data_header(mode_info->atom_context, index, NULL,
&frev, &crev, &data_offset))
return -EINVAL;
power_info = (union power_info *)(mode_info->atom_context->bios + data_offset);
rdev->pm.dpm.ps = kzalloc(sizeof(struct radeon_ps) *
power_info->pplib.ucNumStates, GFP_KERNEL);
if (!rdev->pm.dpm.ps)
return -ENOMEM;
rdev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps);
rdev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime);
rdev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime);
for (i = 0; i < power_info->pplib.ucNumStates; i++) {
power_state = (union pplib_power_state *)
(mode_info->atom_context->bios + data_offset +
le16_to_cpu(power_info->pplib.usStateArrayOffset) +
i * power_info->pplib.ucStateEntrySize);
non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *)
(mode_info->atom_context->bios + data_offset +
le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset) +
(power_state->v1.ucNonClockStateIndex *
power_info->pplib.ucNonClockSize));
if (power_info->pplib.ucStateEntrySize - 1) {
clock_info = (union pplib_clock_info *)
(mode_info->atom_context->bios + data_offset +
le16_to_cpu(power_info->pplib.usClockInfoArrayOffset) +
(power_state->v1.ucClockStateIndices[0] *
power_info->pplib.ucClockInfoSize));
ps = kzalloc(sizeof(struct igp_ps), GFP_KERNEL);
if (ps == NULL) {
kfree(rdev->pm.dpm.ps);
return -ENOMEM;
}
rdev->pm.dpm.ps[i].ps_priv = ps;
rs780_parse_pplib_non_clock_info(rdev, &rdev->pm.dpm.ps[i],
non_clock_info,
power_info->pplib.ucNonClockSize);
rs780_parse_pplib_clock_info(rdev,
&rdev->pm.dpm.ps[i],
clock_info);
}
}
rdev->pm.dpm.num_ps = power_info->pplib.ucNumStates;
return 0;
+}
+int rs780_dpm_init(struct radeon_device *rdev) +{
struct igp_power_info *pi;
int index = GetIndexIntoMasterTable(DATA, IntegratedSystemInfo);
union igp_info *info;
u16 data_offset;
u8 frev, crev;
int ret;
pi = kzalloc(sizeof(struct igp_power_info), GFP_KERNEL);
if (pi == NULL)
return -ENOMEM;
rdev->pm.dpm.priv = pi;
ret = rs780_parse_power_table(rdev);
if (ret)
return ret;
pi->voltage_control = false;
pi->gfx_clock_gating = true;
if (atom_parse_data_header(rdev->mode_info.atom_context, index, NULL,
&frev, &crev, &data_offset)) {
info = (union igp_info *)(rdev->mode_info.atom_context->bios + data_offset);
/* Get various system informations from bios */
switch (crev) {
case 1:
pi->num_of_cycles_in_period =
info->info.ucNumberOfCyclesInPeriod;
pi->num_of_cycles_in_period |=
info->info.ucNumberOfCyclesInPeriodHi << 8;
pi->invert_pwm_required =
(pi->num_of_cycles_in_period & 0x8000) ? true : false;
pi->boot_voltage = info->info.ucStartingPWM_HighTime;
pi->max_voltage = info->info.ucMaxNBVoltage;
pi->max_voltage |= info->info.ucMaxNBVoltageHigh << 8;
pi->min_voltage = info->info.ucMinNBVoltage;
pi->min_voltage |= info->info.ucMinNBVoltageHigh << 8;
pi->inter_voltage_low =
le16_to_cpu(info->info.usInterNBVoltageLow);
pi->inter_voltage_high =
le16_to_cpu(info->info.usInterNBVoltageHigh);
pi->voltage_control = true;
pi->bootup_uma_clk = info->info.usK8MemoryClock * 100;
break;
case 2:
pi->num_of_cycles_in_period =
le16_to_cpu(info->info_2.usNumberOfCyclesInPeriod);
pi->invert_pwm_required =
(pi->num_of_cycles_in_period & 0x8000) ? true : false;
pi->boot_voltage =
le16_to_cpu(info->info_2.usBootUpNBVoltage);
pi->max_voltage =
le16_to_cpu(info->info_2.usMaxNBVoltage);
pi->min_voltage =
le16_to_cpu(info->info_2.usMinNBVoltage);
pi->system_config =
le32_to_cpu(info->info_2.ulSystemConfig);
pi->pwm_voltage_control =
(pi->system_config & 0x4) ? true : false;
pi->voltage_control = true;
pi->bootup_uma_clk = le32_to_cpu(info->info_2.ulBootUpUMAClock);
break;
default:
DRM_ERROR("No integrated system info for your GPU\n");
return -EINVAL;
}
if (pi->min_voltage > pi->max_voltage)
pi->voltage_control = false;
if (pi->pwm_voltage_control) {
if ((pi->num_of_cycles_in_period == 0) ||
(pi->max_voltage == 0) ||
(pi->min_voltage == 0))
pi->voltage_control = false;
} else {
if ((pi->num_of_cycles_in_period == 0) ||
(pi->max_voltage == 0))
pi->voltage_control = false;
}
return 0;
}
radeon_dpm_fini(rdev);
return -EINVAL;
+}
+void rs780_dpm_print_power_state(struct radeon_device *rdev,
struct radeon_ps *rps)
+{
struct igp_ps *ps = rs780_get_ps(rps);
r600_dpm_print_class_info(rps->class, rps->class2);
r600_dpm_print_cap_info(rps->caps);
printk("\tuvd vclk: %d dclk: %d\n", rps->vclk, rps->dclk);
printk("\t\tpower level 0 sclk: %u vddc_index: %d\n",
ps->sclk_low, ps->min_voltage);
printk("\t\tpower level 1 sclk: %u vddc_index: %d\n",
ps->sclk_high, ps->max_voltage);
r600_dpm_print_ps_status(rdev, rps);
+}
+void rs780_dpm_fini(struct radeon_device *rdev) +{
int i;
for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
kfree(rdev->pm.dpm.ps[i].ps_priv);
}
kfree(rdev->pm.dpm.ps);
kfree(rdev->pm.dpm.priv);
+}
+u32 rs780_dpm_get_sclk(struct radeon_device *rdev, bool low) +{
struct igp_ps *requested_state = rs780_get_ps(rdev->pm.dpm.requested_ps);
if (low)
return requested_state->sclk_low;
else
return requested_state->sclk_high;
+}
+u32 rs780_dpm_get_mclk(struct radeon_device *rdev, bool low) +{
struct igp_power_info *pi = rs780_get_pi(rdev);
return pi->bootup_uma_clk;
+} diff --git a/drivers/gpu/drm/radeon/rs780_dpm.h b/drivers/gpu/drm/radeon/rs780_dpm.h new file mode 100644 index 0000000..47a40b1 --- /dev/null +++ b/drivers/gpu/drm/radeon/rs780_dpm.h @@ -0,0 +1,109 @@ +/*
- Copyright 2011 Advanced Micro Devices, Inc.
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- OTHER DEALINGS IN THE SOFTWARE.
- */
+#ifndef __RS780_DPM_H__ +#define __RS780_DPM_H__
+enum rs780_vddc_level {
RS780_VDDC_LEVEL_UNKNOWN = 0,
RS780_VDDC_LEVEL_LOW = 1,
RS780_VDDC_LEVEL_HIGH = 2,
+};
+struct igp_power_info {
/* flags */
bool invert_pwm_required;
bool pwm_voltage_control;
bool voltage_control;
bool gfx_clock_gating;
/* stored values */
u32 system_config;
u32 bootup_uma_clk;
u16 max_voltage;
u16 min_voltage;
u16 boot_voltage;
u16 inter_voltage_low;
u16 inter_voltage_high;
u16 num_of_cycles_in_period;
/* variable */
int crtc_id;
int refresh_rate;
+};
+struct igp_ps {
enum rs780_vddc_level min_voltage;
enum rs780_vddc_level max_voltage;
u32 sclk_low;
u32 sclk_high;
u32 flags;
+};
+#define RS780_CGFTV_DFLT 0x0303000f +#define RS780_FBDIVTIMERVAL_DFLT 0x2710
+#define RS780_FVTHROTUTC0_DFLT 0x04010040 +#define RS780_FVTHROTUTC1_DFLT 0x04010040 +#define RS780_FVTHROTUTC2_DFLT 0x04010040 +#define RS780_FVTHROTUTC3_DFLT 0x04010040 +#define RS780_FVTHROTUTC4_DFLT 0x04010040
+#define RS780_FVTHROTDTC0_DFLT 0x04010040 +#define RS780_FVTHROTDTC1_DFLT 0x04010040 +#define RS780_FVTHROTDTC2_DFLT 0x04010040 +#define RS780_FVTHROTDTC3_DFLT 0x04010040 +#define RS780_FVTHROTDTC4_DFLT 0x04010040
+#define RS780_FVTHROTFBUSREG0_DFLT 0x00001001 +#define RS780_FVTHROTFBUSREG1_DFLT 0x00002002 +#define RS780_FVTHROTFBDSREG0_DFLT 0x00004001 +#define RS780_FVTHROTFBDSREG1_DFLT 0x00020010
+#define RS780_FVTHROTPWMUSREG0_DFLT 0x00002001 +#define RS780_FVTHROTPWMUSREG1_DFLT 0x00004003 +#define RS780_FVTHROTPWMDSREG0_DFLT 0x00002001 +#define RS780_FVTHROTPWMDSREG1_DFLT 0x00004003
+#define RS780_FVTHROTPWMFBDIVRANGEREG0_DFLT 0x37 +#define RS780_FVTHROTPWMFBDIVRANGEREG1_DFLT 0x4b +#define RS780_FVTHROTPWMFBDIVRANGEREG2_DFLT 0x8b
+#define RS780D_FVTHROTPWMFBDIVRANGEREG0_DFLT 0x8b +#define RS780D_FVTHROTPWMFBDIVRANGEREG1_DFLT 0x8c +#define RS780D_FVTHROTPWMFBDIVRANGEREG2_DFLT 0xb5
+#define RS880D_FVTHROTPWMFBDIVRANGEREG0_DFLT 0x8d +#define RS880D_FVTHROTPWMFBDIVRANGEREG1_DFLT 0x8e +#define RS880D_FVTHROTPWMFBDIVRANGEREG2_DFLT 0xBa
+#define RS780_FVTHROTPWMRANGE0_GPIO_DFLT 0x1a +#define RS780_FVTHROTPWMRANGE1_GPIO_DFLT 0x1a +#define RS780_FVTHROTPWMRANGE2_GPIO_DFLT 0x0 +#define RS780_FVTHROTPWMRANGE3_GPIO_DFLT 0x0
+#define RS780_SLOWCLKFEEDBACKDIV_DFLT 110
+#define RS780_CGCLKGATING_DFLT 0x0000E204
+#define RS780_DEFAULT_VCLK_FREQ 53300 /* 10 khz */ +#define RS780_DEFAULT_DCLK_FREQ 40000 /* 10 khz */
+#endif diff --git a/drivers/gpu/drm/radeon/rs780d.h b/drivers/gpu/drm/radeon/rs780d.h new file mode 100644 index 0000000..b1142ed --- /dev/null +++ b/drivers/gpu/drm/radeon/rs780d.h @@ -0,0 +1,168 @@ +/*
- Copyright 2011 Advanced Micro Devices, Inc.
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- OTHER DEALINGS IN THE SOFTWARE.
- */
+#ifndef __RS780D_H__ +#define __RS780D_H__
+#define CG_SPLL_FUNC_CNTL 0x600 +# define SPLL_RESET (1 << 0) +# define SPLL_SLEEP (1 << 1) +# define SPLL_REF_DIV(x) ((x) << 2) +# define SPLL_REF_DIV_MASK (7 << 2) +# define SPLL_FB_DIV(x) ((x) << 5) +# define SPLL_FB_DIV_MASK (0xff << 2) +# define SPLL_FB_DIV_SHIFT 2 +# define SPLL_PULSEEN (1 << 13) +# define SPLL_PULSENUM(x) ((x) << 14) +# define SPLL_PULSENUM_MASK (3 << 14) +# define SPLL_SW_HILEN(x) ((x) << 16) +# define SPLL_SW_HILEN_MASK (0xf << 16) +# define SPLL_SW_LOLEN(x) ((x) << 20) +# define SPLL_SW_LOLEN_MASK (0xf << 20) +# define SPLL_DIVEN (1 << 24) +# define SPLL_BYPASS_EN (1 << 25) +# define SPLL_CHG_STATUS (1 << 29) +# define SPLL_CTLREQ (1 << 30) +# define SPLL_CTLACK (1 << 31)
+/* RS780/RS880 PM */ +#define FVTHROT_CNTRL_REG 0x3000 +#define DONT_WAIT_FOR_FBDIV_WRAP (1 << 0) +#define MINIMUM_CIP(x) ((x) << 1) +#define MINIMUM_CIP_SHIFT 1 +#define MINIMUM_CIP_MASK 0x1fffffe +#define REFRESH_RATE_DIVISOR(x) ((x) << 25) +#define REFRESH_RATE_DIVISOR_SHIFT 25 +#define REFRESH_RATE_DIVISOR_MASK (0x3 << 25) +#define ENABLE_FV_THROT (1 << 27) +#define ENABLE_FV_UPDATE (1 << 28) +#define TREND_SEL_MODE (1 << 29) +#define FORCE_TREND_SEL (1 << 30) +#define ENABLE_FV_THROT_IO (1 << 31) +#define FVTHROT_TARGET_REG 0x3004 +#define TARGET_IDLE_COUNT(x) ((x) << 0) +#define TARGET_IDLE_COUNT_MASK 0xffffff +#define TARGET_IDLE_COUNT_SHIFT 0 +#define FVTHROT_CB1 0x3008 +#define FVTHROT_CB2 0x300c +#define FVTHROT_CB3 0x3010 +#define FVTHROT_CB4 0x3014 +#define FVTHROT_UTC0 0x3018 +#define FVTHROT_UTC1 0x301c +#define FVTHROT_UTC2 0x3020 +#define FVTHROT_UTC3 0x3024 +#define FVTHROT_UTC4 0x3028 +#define FVTHROT_DTC0 0x302c +#define FVTHROT_DTC1 0x3030 +#define FVTHROT_DTC2 0x3034 +#define FVTHROT_DTC3 0x3038 +#define FVTHROT_DTC4 0x303c +#define FVTHROT_FBDIV_REG0 0x3040 +#define MIN_FEEDBACK_DIV(x) ((x) << 0) +#define MIN_FEEDBACK_DIV_MASK 0xfff +#define MIN_FEEDBACK_DIV_SHIFT 0 +#define MAX_FEEDBACK_DIV(x) ((x) << 12) +#define MAX_FEEDBACK_DIV_MASK (0xfff << 12) +#define MAX_FEEDBACK_DIV_SHIFT 12 +#define FVTHROT_FBDIV_REG1 0x3044 +#define MAX_FEEDBACK_STEP(x) ((x) << 0) +#define MAX_FEEDBACK_STEP_MASK 0xfff +#define MAX_FEEDBACK_STEP_SHIFT 0 +#define STARTING_FEEDBACK_DIV(x) ((x) << 12) +#define STARTING_FEEDBACK_DIV_MASK (0xfff << 12) +#define STARTING_FEEDBACK_DIV_SHIFT 12 +#define FORCE_FEEDBACK_DIV (1 << 24) +#define FVTHROT_FBDIV_REG2 0x3048 +#define FORCED_FEEDBACK_DIV(x) ((x) << 0) +#define FORCED_FEEDBACK_DIV_MASK 0xfff +#define FORCED_FEEDBACK_DIV_SHIFT 0 +#define FB_DIV_TIMER_VAL(x) ((x) << 12) +#define FB_DIV_TIMER_VAL_MASK (0xffff << 12) +#define FB_DIV_TIMER_VAL_SHIFT 12 +#define FVTHROT_FB_US_REG0 0x304c +#define FVTHROT_FB_US_REG1 0x3050 +#define FVTHROT_FB_DS_REG0 0x3054 +#define FVTHROT_FB_DS_REG1 0x3058 +#define FVTHROT_PWM_CTRL_REG0 0x305c +#define STARTING_PWM_HIGHTIME(x) ((x) << 0) +#define STARTING_PWM_HIGHTIME_MASK 0xfff +#define STARTING_PWM_HIGHTIME_SHIFT 0 +#define NUMBER_OF_CYCLES_IN_PERIOD(x) ((x) << 12) +#define NUMBER_OF_CYCLES_IN_PERIOD_MASK (0xfff << 12) +#define NUMBER_OF_CYCLES_IN_PERIOD_SHIFT 12 +#define FORCE_STARTING_PWM_HIGHTIME (1 << 24) +#define INVERT_PWM_WAVEFORM (1 << 25) +#define FVTHROT_PWM_CTRL_REG1 0x3060 +#define MIN_PWM_HIGHTIME(x) ((x) << 0) +#define MIN_PWM_HIGHTIME_MASK 0xfff +#define MIN_PWM_HIGHTIME_SHIFT 0 +#define MAX_PWM_HIGHTIME(x) ((x) << 12) +#define MAX_PWM_HIGHTIME_MASK (0xfff << 12) +#define MAX_PWM_HIGHTIME_SHIFT 12 +#define FVTHROT_PWM_US_REG0 0x3064 +#define FVTHROT_PWM_US_REG1 0x3068 +#define FVTHROT_PWM_DS_REG0 0x306c +#define FVTHROT_PWM_DS_REG1 0x3070 +#define FVTHROT_STATUS_REG0 0x3074 +#define CURRENT_FEEDBACK_DIV_MASK 0xfff +#define CURRENT_FEEDBACK_DIV_SHIFT 0 +#define FVTHROT_STATUS_REG1 0x3078 +#define FVTHROT_STATUS_REG2 0x307c +#define CG_INTGFX_MISC 0x3080 +#define FVTHROT_VBLANK_SEL (1 << 9) +#define FVTHROT_PWM_FEEDBACK_DIV_REG1 0x308c +#define RANGE0_PWM_FEEDBACK_DIV(x) ((x) << 0) +#define RANGE0_PWM_FEEDBACK_DIV_MASK 0xfff +#define RANGE0_PWM_FEEDBACK_DIV_SHIFT 0 +#define RANGE_PWM_FEEDBACK_DIV_EN (1 << 12) +#define FVTHROT_PWM_FEEDBACK_DIV_REG2 0x3090 +#define RANGE1_PWM_FEEDBACK_DIV(x) ((x) << 0) +#define RANGE1_PWM_FEEDBACK_DIV_MASK 0xfff +#define RANGE1_PWM_FEEDBACK_DIV_SHIFT 0 +#define RANGE2_PWM_FEEDBACK_DIV(x) ((x) << 12) +#define RANGE2_PWM_FEEDBACK_DIV_MASK (0xfff << 12) +#define RANGE2_PWM_FEEDBACK_DIV_SHIFT 12 +#define FVTHROT_PWM_FEEDBACK_DIV_REG3 0x3094 +#define RANGE0_PWM(x) ((x) << 0) +#define RANGE0_PWM_MASK 0xfff +#define RANGE0_PWM_SHIFT 0 +#define RANGE1_PWM(x) ((x) << 12) +#define RANGE1_PWM_MASK (0xfff << 12) +#define RANGE1_PWM_SHIFT 12 +#define FVTHROT_PWM_FEEDBACK_DIV_REG4 0x3098 +#define RANGE2_PWM(x) ((x) << 0) +#define RANGE2_PWM_MASK 0xfff +#define RANGE2_PWM_SHIFT 0 +#define RANGE3_PWM(x) ((x) << 12) +#define RANGE3_PWM_MASK (0xfff << 12) +#define RANGE3_PWM_SHIFT 12 +#define FVTHROT_SLOW_CLK_FEEDBACK_DIV_REG1 0x30ac +#define RANGE0_SLOW_CLK_FEEDBACK_DIV(x) ((x) << 0) +#define RANGE0_SLOW_CLK_FEEDBACK_DIV_MASK 0xfff +#define RANGE0_SLOW_CLK_FEEDBACK_DIV_SHIFT 0 +#define RANGE_SLOW_CLK_FEEDBACK_DIV_EN (1 << 12)
+#define GFX_MACRO_BYPASS_CNTL 0x30c0 +#define SPLL_BYPASS_CNTL (1 << 0) +#define UPLL_BYPASS_CNTL (1 << 1)
+#endif
1.7.7.5
dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
On Wed, Jun 26, 2013 at 02:19:23PM -0400, Alex Deucher wrote:
On Wed, Jun 26, 2013 at 6:46 AM, Jerome Glisse j.glisse@gmail.com wrote:
On Wed, Jun 26, 2013 at 09:22:35AM -0400, alexdeucher@gmail.com wrote:
From: Alex Deucher alexander.deucher@amd.com
This adds dpm support for rs780/rs880 asics. This includes:
- clockgating
- dynamic engine clock scaling
- dynamic voltage scaling
set radeon.dpm=1 to enable it.
Signed-off-by: Alex Deucher alexander.deucher@amd.com
Depending on the answer to inline question :
Reviewed-by: Jerome Glisse jglisse@redhat.com
drivers/gpu/drm/radeon/Makefile | 2 +- drivers/gpu/drm/radeon/radeon_asic.c | 12 + drivers/gpu/drm/radeon/radeon_asic.h | 12 + drivers/gpu/drm/radeon/radeon_pm.c | 7 + drivers/gpu/drm/radeon/rs780_dpm.c | 894 ++++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/rs780_dpm.h | 109 ++++ drivers/gpu/drm/radeon/rs780d.h | 168 +++++++ 7 files changed, 1203 insertions(+), 1 deletions(-) create mode 100644 drivers/gpu/drm/radeon/rs780_dpm.c create mode 100644 drivers/gpu/drm/radeon/rs780_dpm.h create mode 100644 drivers/gpu/drm/radeon/rs780d.h
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index a131a13..e44b046 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile @@ -77,7 +77,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \ evergreen_hdmi.o radeon_trace_points.o ni.o cayman_blit_shaders.o \ atombios_encoders.o radeon_semaphore.o radeon_sa.o atombios_i2c.o si.o \ si_blit_shaders.o radeon_prime.o radeon_uvd.o cik.o cik_blit_shaders.o \
r600_dpm.o
r600_dpm.o rs780_dpm.o
radeon-$(CONFIG_COMPAT) += radeon_ioc32.o radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index d9c8e9a..db3c930 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1194,6 +1194,18 @@ static struct radeon_asic rs780_asic = { .set_clock_gating = NULL, .get_temperature = &rv6xx_get_temp, },
.dpm = {
.init = &rs780_dpm_init,
.setup_asic = &rs780_dpm_setup_asic,
.enable = &rs780_dpm_enable,
.disable = &rs780_dpm_disable,
.set_power_state = &rs780_dpm_set_power_state,
.display_configuration_changed = &rs780_dpm_display_configuration_changed,
.fini = &rs780_dpm_fini,
.get_sclk = &rs780_dpm_get_sclk,
.get_mclk = &rs780_dpm_get_mclk,
.print_power_state = &rs780_dpm_print_power_state,
}, .pflip = { .pre_page_flip = &rs600_pre_page_flip, .page_flip = &rs600_page_flip,
diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 8507cae..134bf57 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -396,6 +396,18 @@ int r600_mc_wait_for_idle(struct radeon_device *rdev); u32 r600_get_xclk(struct radeon_device *rdev); uint64_t r600_get_gpu_clock_counter(struct radeon_device *rdev); int rv6xx_get_temp(struct radeon_device *rdev); +/* rs780 dpm */ +int rs780_dpm_init(struct radeon_device *rdev); +int rs780_dpm_enable(struct radeon_device *rdev); +void rs780_dpm_disable(struct radeon_device *rdev); +int rs780_dpm_set_power_state(struct radeon_device *rdev); +void rs780_dpm_setup_asic(struct radeon_device *rdev); +void rs780_dpm_display_configuration_changed(struct radeon_device *rdev); +void rs780_dpm_fini(struct radeon_device *rdev); +u32 rs780_dpm_get_sclk(struct radeon_device *rdev, bool low); +u32 rs780_dpm_get_mclk(struct radeon_device *rdev, bool low); +void rs780_dpm_print_power_state(struct radeon_device *rdev,
struct radeon_ps *ps);
/* uvd */ int r600_uvd_init(struct radeon_device *rdev); diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 4f5422e..853a8a2 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -1030,6 +1030,13 @@ int radeon_pm_init(struct radeon_device *rdev) { /* enable dpm on rv6xx+ */ switch (rdev->family) {
case CHIP_RS780:
case CHIP_RS880:
if (radeon_dpm == 1)
rdev->pm.pm_method = PM_METHOD_DPM;
else
rdev->pm.pm_method = PM_METHOD_PROFILE;
break; default: /* default to profile method */ rdev->pm.pm_method = PM_METHOD_PROFILE;
diff --git a/drivers/gpu/drm/radeon/rs780_dpm.c b/drivers/gpu/drm/radeon/rs780_dpm.c new file mode 100644 index 0000000..f594900 --- /dev/null +++ b/drivers/gpu/drm/radeon/rs780_dpm.c @@ -0,0 +1,894 @@ +/*
- Copyright 2011 Advanced Micro Devices, 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: Alex Deucher
- */
+#include "drmP.h" +#include "radeon.h" +#include "rs780d.h" +#include "r600_dpm.h" +#include "rs780_dpm.h" +#include "atom.h"
+static struct igp_ps *rs780_get_ps(struct radeon_ps *rps) +{
struct igp_ps *ps = rps->ps_priv;
return ps;
+}
+static struct igp_power_info *rs780_get_pi(struct radeon_device *rdev) +{
struct igp_power_info *pi = rdev->pm.dpm.priv;
return pi;
+}
+static void rs780_get_pm_mode_parameters(struct radeon_device *rdev) +{
struct igp_power_info *pi = rs780_get_pi(rdev);
struct radeon_mode_info *minfo = &rdev->mode_info;
struct drm_crtc *crtc;
struct radeon_crtc *radeon_crtc;
int i;
/* defaults */
pi->crtc_id = 0;
pi->refresh_rate = 60;
for (i = 0; i < rdev->num_crtc; i++) {
crtc = (struct drm_crtc *)minfo->crtcs[i];
if (crtc && crtc->enabled) {
radeon_crtc = to_radeon_crtc(crtc);
pi->crtc_id = radeon_crtc->crtc_id;
if (crtc->mode.htotal && crtc->mode.vtotal)
pi->refresh_rate =
(crtc->mode.clock * 1000) /
(crtc->mode.htotal * crtc->mode.vtotal);
break;
}
}
Ok this looks wrong to me you look for the first enabled crtc but on those iirc there could be 2 so first one might be a low refresh rate and second one an higher one. Thus returning the first one might lead to PM decision that are wrong.
So what happens when 2 are active ? Flickering on the unsync one ?
Cheers, Jerome
On RS780/RS880, the hardware can only synchronize with a single crtc, so it doesn't really matter which one we pick as long as it's active.
+}
+static void rs780_voltage_scaling_enable(struct radeon_device *rdev, bool enable);
+static int rs780_initialize_dpm_power_state(struct radeon_device *rdev) +{
struct atom_clock_dividers dividers;
struct igp_ps *default_state = rs780_get_ps(rdev->pm.dpm.boot_ps);
int i, ret;
ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
default_state->sclk_low, false, ÷rs);
if (ret)
return ret;
r600_engine_clock_entry_set_reference_divider(rdev, 0, dividers.ref_div);
r600_engine_clock_entry_set_feedback_divider(rdev, 0, dividers.fb_div);
r600_engine_clock_entry_set_post_divider(rdev, 0, dividers.post_div);
if (dividers.enable_post_div)
r600_engine_clock_entry_enable_post_divider(rdev, 0, true);
else
r600_engine_clock_entry_enable_post_divider(rdev, 0, false);
r600_engine_clock_entry_set_step_time(rdev, 0, R600_SST_DFLT);
r600_engine_clock_entry_enable_pulse_skipping(rdev, 0, false);
r600_engine_clock_entry_enable(rdev, 0, true);
for (i = 1; i < R600_PM_NUMBER_OF_SCLKS; i++)
r600_engine_clock_entry_enable(rdev, i, false);
r600_enable_mclk_control(rdev, false);
r600_voltage_control_enable_pins(rdev, 0);
return 0;
+}
+static int rs780_initialize_dpm_parameters(struct radeon_device *rdev) +{
int ret = 0;
int i;
r600_set_bsp(rdev, R600_BSU_DFLT, R600_BSP_DFLT);
r600_set_at(rdev, 0, 0, 0, 0);
r600_set_git(rdev, R600_GICST_DFLT);
for (i = 0; i < R600_PM_NUMBER_OF_TC; i++)
r600_set_tc(rdev, i, 0, 0);
r600_select_td(rdev, R600_TD_DFLT);
r600_set_vrc(rdev, 0);
r600_set_tpu(rdev, R600_TPU_DFLT);
r600_set_tpc(rdev, R600_TPC_DFLT);
r600_set_sstu(rdev, R600_SSTU_DFLT);
r600_set_sst(rdev, R600_SST_DFLT);
r600_set_fctu(rdev, R600_FCTU_DFLT);
r600_set_fct(rdev, R600_FCT_DFLT);
r600_set_vddc3d_oorsu(rdev, R600_VDDC3DOORSU_DFLT);
r600_set_vddc3d_oorphc(rdev, R600_VDDC3DOORPHC_DFLT);
r600_set_vddc3d_oorsdc(rdev, R600_VDDC3DOORSDC_DFLT);
r600_set_ctxcgtt3d_rphc(rdev, R600_CTXCGTT3DRPHC_DFLT);
r600_set_ctxcgtt3d_rsdc(rdev, R600_CTXCGTT3DRSDC_DFLT);
r600_vid_rt_set_vru(rdev, R600_VRU_DFLT);
r600_vid_rt_set_vrt(rdev, R600_VOLTAGERESPONSETIME_DFLT);
r600_vid_rt_set_ssu(rdev, R600_SPLLSTEPUNIT_DFLT);
ret = rs780_initialize_dpm_power_state(rdev);
r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_LOW, 0);
r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_MEDIUM, 0);
r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_HIGH, 0);
r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_LOW, 0);
r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_MEDIUM, 0);
r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_HIGH, 0);
r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_LOW, 0);
r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_MEDIUM, 0);
r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_HIGH, 0);
r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_LOW, R600_DISPLAY_WATERMARK_HIGH);
r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_MEDIUM, R600_DISPLAY_WATERMARK_HIGH);
r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_HIGH, R600_DISPLAY_WATERMARK_HIGH);
r600_power_level_enable(rdev, R600_POWER_LEVEL_CTXSW, false);
r600_power_level_enable(rdev, R600_POWER_LEVEL_HIGH, false);
r600_power_level_enable(rdev, R600_POWER_LEVEL_MEDIUM, false);
r600_power_level_enable(rdev, R600_POWER_LEVEL_LOW, true);
r600_power_level_set_enter_index(rdev, R600_POWER_LEVEL_LOW);
r600_set_vrc(rdev, RS780_CGFTV_DFLT);
return ret;
+}
+static void rs780_start_dpm(struct radeon_device *rdev) +{
r600_enable_sclk_control(rdev, false);
r600_enable_mclk_control(rdev, false);
r600_dynamicpm_enable(rdev, true);
radeon_wait_for_vblank(rdev, 0);
radeon_wait_for_vblank(rdev, 1);
r600_enable_spll_bypass(rdev, true);
r600_wait_for_spll_change(rdev);
r600_enable_spll_bypass(rdev, false);
r600_wait_for_spll_change(rdev);
r600_enable_spll_bypass(rdev, true);
r600_wait_for_spll_change(rdev);
r600_enable_spll_bypass(rdev, false);
r600_wait_for_spll_change(rdev);
r600_enable_sclk_control(rdev, true);
+}
+static void rs780_preset_ranges_slow_clk_fbdiv_en(struct radeon_device *rdev) +{
WREG32_P(FVTHROT_SLOW_CLK_FEEDBACK_DIV_REG1, RANGE_SLOW_CLK_FEEDBACK_DIV_EN,
~RANGE_SLOW_CLK_FEEDBACK_DIV_EN);
WREG32_P(FVTHROT_SLOW_CLK_FEEDBACK_DIV_REG1,
RANGE0_SLOW_CLK_FEEDBACK_DIV(RS780_SLOWCLKFEEDBACKDIV_DFLT),
~RANGE0_SLOW_CLK_FEEDBACK_DIV_MASK);
+}
+static void rs780_preset_starting_fbdiv(struct radeon_device *rdev) +{
u32 fbdiv = (RREG32(CG_SPLL_FUNC_CNTL) & SPLL_FB_DIV_MASK) >> SPLL_FB_DIV_SHIFT;
WREG32_P(FVTHROT_FBDIV_REG1, STARTING_FEEDBACK_DIV(fbdiv),
~STARTING_FEEDBACK_DIV_MASK);
WREG32_P(FVTHROT_FBDIV_REG2, FORCED_FEEDBACK_DIV(fbdiv),
~FORCED_FEEDBACK_DIV_MASK);
WREG32_P(FVTHROT_FBDIV_REG1, FORCE_FEEDBACK_DIV, ~FORCE_FEEDBACK_DIV);
+}
+static void rs780_voltage_scaling_init(struct radeon_device *rdev) +{
struct igp_power_info *pi = rs780_get_pi(rdev);
struct drm_device *dev = rdev->ddev;
u32 fv_throt_pwm_fb_div_range[3];
u32 fv_throt_pwm_range[4];
if (dev->pdev->device == 0x9614) {
fv_throt_pwm_fb_div_range[0] = RS780D_FVTHROTPWMFBDIVRANGEREG0_DFLT;
fv_throt_pwm_fb_div_range[1] = RS780D_FVTHROTPWMFBDIVRANGEREG1_DFLT;
fv_throt_pwm_fb_div_range[2] = RS780D_FVTHROTPWMFBDIVRANGEREG2_DFLT;
} else if ((dev->pdev->device == 0x9714) ||
(dev->pdev->device == 0x9715)) {
fv_throt_pwm_fb_div_range[0] = RS880D_FVTHROTPWMFBDIVRANGEREG0_DFLT;
fv_throt_pwm_fb_div_range[1] = RS880D_FVTHROTPWMFBDIVRANGEREG1_DFLT;
fv_throt_pwm_fb_div_range[2] = RS880D_FVTHROTPWMFBDIVRANGEREG2_DFLT;
} else {
fv_throt_pwm_fb_div_range[0] = RS780_FVTHROTPWMFBDIVRANGEREG0_DFLT;
fv_throt_pwm_fb_div_range[1] = RS780_FVTHROTPWMFBDIVRANGEREG1_DFLT;
fv_throt_pwm_fb_div_range[2] = RS780_FVTHROTPWMFBDIVRANGEREG2_DFLT;
}
if (pi->pwm_voltage_control) {
fv_throt_pwm_range[0] = pi->min_voltage;
fv_throt_pwm_range[1] = pi->min_voltage;
fv_throt_pwm_range[2] = pi->max_voltage;
fv_throt_pwm_range[3] = pi->max_voltage;
} else {
fv_throt_pwm_range[0] = pi->invert_pwm_required ?
RS780_FVTHROTPWMRANGE3_GPIO_DFLT : RS780_FVTHROTPWMRANGE0_GPIO_DFLT;
fv_throt_pwm_range[1] = pi->invert_pwm_required ?
RS780_FVTHROTPWMRANGE2_GPIO_DFLT : RS780_FVTHROTPWMRANGE1_GPIO_DFLT;
fv_throt_pwm_range[2] = pi->invert_pwm_required ?
RS780_FVTHROTPWMRANGE1_GPIO_DFLT : RS780_FVTHROTPWMRANGE2_GPIO_DFLT;
fv_throt_pwm_range[3] = pi->invert_pwm_required ?
RS780_FVTHROTPWMRANGE0_GPIO_DFLT : RS780_FVTHROTPWMRANGE3_GPIO_DFLT;
}
WREG32_P(FVTHROT_PWM_CTRL_REG0,
STARTING_PWM_HIGHTIME(pi->max_voltage),
~STARTING_PWM_HIGHTIME_MASK);
WREG32_P(FVTHROT_PWM_CTRL_REG0,
NUMBER_OF_CYCLES_IN_PERIOD(pi->num_of_cycles_in_period),
~NUMBER_OF_CYCLES_IN_PERIOD_MASK);
WREG32_P(FVTHROT_PWM_CTRL_REG0, FORCE_STARTING_PWM_HIGHTIME,
~FORCE_STARTING_PWM_HIGHTIME);
if (pi->invert_pwm_required)
WREG32_P(FVTHROT_PWM_CTRL_REG0, INVERT_PWM_WAVEFORM, ~INVERT_PWM_WAVEFORM);
else
WREG32_P(FVTHROT_PWM_CTRL_REG0, 0, ~INVERT_PWM_WAVEFORM);
rs780_voltage_scaling_enable(rdev, true);
WREG32(FVTHROT_PWM_CTRL_REG1,
(MIN_PWM_HIGHTIME(pi->min_voltage) |
MAX_PWM_HIGHTIME(pi->max_voltage)));
WREG32(FVTHROT_PWM_US_REG0, RS780_FVTHROTPWMUSREG0_DFLT);
WREG32(FVTHROT_PWM_US_REG1, RS780_FVTHROTPWMUSREG1_DFLT);
WREG32(FVTHROT_PWM_DS_REG0, RS780_FVTHROTPWMDSREG0_DFLT);
WREG32(FVTHROT_PWM_DS_REG1, RS780_FVTHROTPWMDSREG1_DFLT);
WREG32_P(FVTHROT_PWM_FEEDBACK_DIV_REG1,
RANGE0_PWM_FEEDBACK_DIV(fv_throt_pwm_fb_div_range[0]),
~RANGE0_PWM_FEEDBACK_DIV_MASK);
WREG32(FVTHROT_PWM_FEEDBACK_DIV_REG2,
(RANGE1_PWM_FEEDBACK_DIV(fv_throt_pwm_fb_div_range[1]) |
RANGE2_PWM_FEEDBACK_DIV(fv_throt_pwm_fb_div_range[2])));
WREG32(FVTHROT_PWM_FEEDBACK_DIV_REG3,
(RANGE0_PWM(fv_throt_pwm_range[1]) |
RANGE1_PWM(fv_throt_pwm_range[2])));
WREG32(FVTHROT_PWM_FEEDBACK_DIV_REG4,
(RANGE2_PWM(fv_throt_pwm_range[1]) |
RANGE3_PWM(fv_throt_pwm_range[2])));
+}
+static void rs780_clk_scaling_enable(struct radeon_device *rdev, bool enable) +{
if (enable)
WREG32_P(FVTHROT_CNTRL_REG, ENABLE_FV_THROT | ENABLE_FV_UPDATE,
~(ENABLE_FV_THROT | ENABLE_FV_UPDATE));
else
WREG32_P(FVTHROT_CNTRL_REG, 0,
~(ENABLE_FV_THROT | ENABLE_FV_UPDATE));
+}
+static void rs780_voltage_scaling_enable(struct radeon_device *rdev, bool enable) +{
if (enable)
WREG32_P(FVTHROT_CNTRL_REG, ENABLE_FV_THROT_IO, ~ENABLE_FV_THROT_IO);
else
WREG32_P(FVTHROT_CNTRL_REG, 0, ~ENABLE_FV_THROT_IO);
+}
+static void rs780_set_engine_clock_wfc(struct radeon_device *rdev) +{
WREG32(FVTHROT_UTC0, RS780_FVTHROTUTC0_DFLT);
WREG32(FVTHROT_UTC1, RS780_FVTHROTUTC1_DFLT);
WREG32(FVTHROT_UTC2, RS780_FVTHROTUTC2_DFLT);
WREG32(FVTHROT_UTC3, RS780_FVTHROTUTC3_DFLT);
WREG32(FVTHROT_UTC4, RS780_FVTHROTUTC4_DFLT);
WREG32(FVTHROT_DTC0, RS780_FVTHROTDTC0_DFLT);
WREG32(FVTHROT_DTC1, RS780_FVTHROTDTC1_DFLT);
WREG32(FVTHROT_DTC2, RS780_FVTHROTDTC2_DFLT);
WREG32(FVTHROT_DTC3, RS780_FVTHROTDTC3_DFLT);
WREG32(FVTHROT_DTC4, RS780_FVTHROTDTC4_DFLT);
+}
+static void rs780_set_engine_clock_sc(struct radeon_device *rdev) +{
WREG32_P(FVTHROT_FBDIV_REG2,
FB_DIV_TIMER_VAL(RS780_FBDIVTIMERVAL_DFLT),
~FB_DIV_TIMER_VAL_MASK);
WREG32_P(FVTHROT_CNTRL_REG,
REFRESH_RATE_DIVISOR(0) | MINIMUM_CIP(0xf),
~(REFRESH_RATE_DIVISOR_MASK | MINIMUM_CIP_MASK));
+}
+static void rs780_set_engine_clock_tdc(struct radeon_device *rdev) +{
WREG32_P(FVTHROT_CNTRL_REG, 0, ~(FORCE_TREND_SEL | TREND_SEL_MODE));
+}
+static void rs780_set_engine_clock_ssc(struct radeon_device *rdev) +{
WREG32(FVTHROT_FB_US_REG0, RS780_FVTHROTFBUSREG0_DFLT);
WREG32(FVTHROT_FB_US_REG1, RS780_FVTHROTFBUSREG1_DFLT);
WREG32(FVTHROT_FB_DS_REG0, RS780_FVTHROTFBDSREG0_DFLT);
WREG32(FVTHROT_FB_DS_REG1, RS780_FVTHROTFBDSREG1_DFLT);
WREG32_P(FVTHROT_FBDIV_REG1, MAX_FEEDBACK_STEP(1), ~MAX_FEEDBACK_STEP_MASK);
+}
+static void rs780_program_at(struct radeon_device *rdev) +{
struct igp_power_info *pi = rs780_get_pi(rdev);
WREG32(FVTHROT_TARGET_REG, 30000000 / pi->refresh_rate);
WREG32(FVTHROT_CB1, 1000000 * 5 / pi->refresh_rate);
WREG32(FVTHROT_CB2, 1000000 * 10 / pi->refresh_rate);
WREG32(FVTHROT_CB3, 1000000 * 30 / pi->refresh_rate);
WREG32(FVTHROT_CB4, 1000000 * 50 / pi->refresh_rate);
+}
+static void rs780_disable_vbios_powersaving(struct radeon_device *rdev) +{
WREG32_P(CG_INTGFX_MISC, 0, ~0xFFF00000);
+}
+static void rs780_force_voltage_to_high(struct radeon_device *rdev) +{
struct igp_power_info *pi = rs780_get_pi(rdev);
struct igp_ps *current_state = rs780_get_ps(rdev->pm.dpm.current_ps);
if ((current_state->max_voltage == RS780_VDDC_LEVEL_HIGH) &&
(current_state->min_voltage == RS780_VDDC_LEVEL_HIGH))
return;
WREG32_P(GFX_MACRO_BYPASS_CNTL, SPLL_BYPASS_CNTL, ~SPLL_BYPASS_CNTL);
udelay(1);
WREG32_P(FVTHROT_PWM_CTRL_REG0,
STARTING_PWM_HIGHTIME(pi->max_voltage),
~STARTING_PWM_HIGHTIME_MASK);
WREG32_P(FVTHROT_PWM_CTRL_REG0,
FORCE_STARTING_PWM_HIGHTIME, ~FORCE_STARTING_PWM_HIGHTIME);
WREG32_P(FVTHROT_PWM_FEEDBACK_DIV_REG1, 0,
~RANGE_PWM_FEEDBACK_DIV_EN);
udelay(1);
WREG32_P(GFX_MACRO_BYPASS_CNTL, 0, ~SPLL_BYPASS_CNTL);
+}
+static int rs780_set_engine_clock_scaling(struct radeon_device *rdev) +{
struct atom_clock_dividers min_dividers, max_dividers, current_max_dividers;
struct igp_ps *new_state = rs780_get_ps(rdev->pm.dpm.requested_ps);
struct igp_ps *old_state = rs780_get_ps(rdev->pm.dpm.current_ps);
int ret;
if ((new_state->sclk_high == old_state->sclk_high) &&
(new_state->sclk_low == old_state->sclk_low))
return 0;
ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
new_state->sclk_low, false, &min_dividers);
if (ret)
return ret;
ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
new_state->sclk_high, false, &max_dividers);
if (ret)
return ret;
ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
old_state->sclk_high, false, ¤t_max_dividers);
if (ret)
return ret;
WREG32_P(GFX_MACRO_BYPASS_CNTL, SPLL_BYPASS_CNTL, ~SPLL_BYPASS_CNTL);
WREG32_P(FVTHROT_FBDIV_REG2, FORCED_FEEDBACK_DIV(max_dividers.fb_div),
~FORCED_FEEDBACK_DIV_MASK);
WREG32_P(FVTHROT_FBDIV_REG1, STARTING_FEEDBACK_DIV(max_dividers.fb_div),
~STARTING_FEEDBACK_DIV_MASK);
WREG32_P(FVTHROT_FBDIV_REG1, FORCE_FEEDBACK_DIV, ~FORCE_FEEDBACK_DIV);
udelay(100);
WREG32_P(GFX_MACRO_BYPASS_CNTL, 0, ~SPLL_BYPASS_CNTL);
if (max_dividers.fb_div > min_dividers.fb_div) {
WREG32_P(FVTHROT_FBDIV_REG0,
MIN_FEEDBACK_DIV(min_dividers.fb_div) |
MAX_FEEDBACK_DIV(max_dividers.fb_div),
~(MIN_FEEDBACK_DIV_MASK | MAX_FEEDBACK_DIV_MASK));
WREG32_P(FVTHROT_FBDIV_REG1, 0, ~FORCE_FEEDBACK_DIV);
}
return 0;
+}
+static void rs780_set_engine_clock_spc(struct radeon_device *rdev) +{
struct igp_ps *new_state = rs780_get_ps(rdev->pm.dpm.requested_ps);
struct igp_ps *old_state = rs780_get_ps(rdev->pm.dpm.current_ps);
struct igp_power_info *pi = rs780_get_pi(rdev);
if ((new_state->sclk_high == old_state->sclk_high) &&
(new_state->sclk_low == old_state->sclk_low))
return;
if (pi->crtc_id == 0)
WREG32_P(CG_INTGFX_MISC, 0, ~FVTHROT_VBLANK_SEL);
else
WREG32_P(CG_INTGFX_MISC, FVTHROT_VBLANK_SEL, ~FVTHROT_VBLANK_SEL);
+}
+static void rs780_activate_engine_clk_scaling(struct radeon_device *rdev) +{
struct igp_ps *new_state = rs780_get_ps(rdev->pm.dpm.requested_ps);
struct igp_ps *old_state = rs780_get_ps(rdev->pm.dpm.current_ps);
if ((new_state->sclk_high == old_state->sclk_high) &&
(new_state->sclk_low == old_state->sclk_low))
return;
rs780_clk_scaling_enable(rdev, true);
+}
+static u32 rs780_get_voltage_for_vddc_level(struct radeon_device *rdev,
enum rs780_vddc_level vddc)
+{
struct igp_power_info *pi = rs780_get_pi(rdev);
if (vddc == RS780_VDDC_LEVEL_HIGH)
return pi->max_voltage;
else if (vddc == RS780_VDDC_LEVEL_LOW)
return pi->min_voltage;
else
return pi->max_voltage;
+}
+static void rs780_enable_voltage_scaling(struct radeon_device *rdev) +{
struct igp_ps *new_state = rs780_get_ps(rdev->pm.dpm.requested_ps);
struct igp_power_info *pi = rs780_get_pi(rdev);
enum rs780_vddc_level vddc_high, vddc_low;
udelay(100);
if ((new_state->max_voltage == RS780_VDDC_LEVEL_HIGH) &&
(new_state->min_voltage == RS780_VDDC_LEVEL_HIGH))
return;
vddc_high = rs780_get_voltage_for_vddc_level(rdev,
new_state->max_voltage);
vddc_low = rs780_get_voltage_for_vddc_level(rdev,
new_state->min_voltage);
WREG32_P(GFX_MACRO_BYPASS_CNTL, SPLL_BYPASS_CNTL, ~SPLL_BYPASS_CNTL);
udelay(1);
if (vddc_high > vddc_low) {
WREG32_P(FVTHROT_PWM_FEEDBACK_DIV_REG1,
RANGE_PWM_FEEDBACK_DIV_EN, ~RANGE_PWM_FEEDBACK_DIV_EN);
WREG32_P(FVTHROT_PWM_CTRL_REG0, 0, ~FORCE_STARTING_PWM_HIGHTIME);
} else if (vddc_high == vddc_low) {
if (pi->max_voltage != vddc_high) {
WREG32_P(FVTHROT_PWM_CTRL_REG0,
STARTING_PWM_HIGHTIME(vddc_high),
~STARTING_PWM_HIGHTIME_MASK);
WREG32_P(FVTHROT_PWM_CTRL_REG0,
FORCE_STARTING_PWM_HIGHTIME,
~FORCE_STARTING_PWM_HIGHTIME);
}
}
WREG32_P(GFX_MACRO_BYPASS_CNTL, 0, ~SPLL_BYPASS_CNTL);
+}
+int rs780_dpm_enable(struct radeon_device *rdev) +{
struct igp_power_info *pi = rs780_get_pi(rdev);
rs780_get_pm_mode_parameters(rdev);
rs780_disable_vbios_powersaving(rdev);
if (r600_dynamicpm_enabled(rdev))
return -EINVAL;
if (rs780_initialize_dpm_parameters(rdev))
return -EINVAL;
rs780_start_dpm(rdev);
rs780_preset_ranges_slow_clk_fbdiv_en(rdev);
rs780_preset_starting_fbdiv(rdev);
if (pi->voltage_control)
rs780_voltage_scaling_init(rdev);
rs780_clk_scaling_enable(rdev, true);
rs780_set_engine_clock_sc(rdev);
rs780_set_engine_clock_wfc(rdev);
rs780_program_at(rdev);
rs780_set_engine_clock_tdc(rdev);
rs780_set_engine_clock_ssc(rdev);
if (pi->gfx_clock_gating)
r600_gfx_clockgating_enable(rdev, true);
return 0;
+}
+void rs780_dpm_disable(struct radeon_device *rdev) +{
struct igp_power_info *pi = rs780_get_pi(rdev);
r600_dynamicpm_enable(rdev, false);
rs780_clk_scaling_enable(rdev, false);
rs780_voltage_scaling_enable(rdev, false);
if (pi->gfx_clock_gating)
r600_gfx_clockgating_enable(rdev, false);
+}
+int rs780_dpm_set_power_state(struct radeon_device *rdev) +{
struct igp_power_info *pi = rs780_get_pi(rdev);
rs780_get_pm_mode_parameters(rdev);
if (pi->voltage_control) {
rs780_force_voltage_to_high(rdev);
mdelay(5);
}
rs780_set_engine_clock_scaling(rdev);
rs780_set_engine_clock_spc(rdev);
rs780_activate_engine_clk_scaling(rdev);
if (pi->voltage_control)
rs780_enable_voltage_scaling(rdev);
return 0;
+}
+void rs780_dpm_setup_asic(struct radeon_device *rdev) +{
+}
+void rs780_dpm_display_configuration_changed(struct radeon_device *rdev) +{
rs780_get_pm_mode_parameters(rdev);
rs780_program_at(rdev);
+}
+union igp_info {
struct _ATOM_INTEGRATED_SYSTEM_INFO info;
struct _ATOM_INTEGRATED_SYSTEM_INFO_V2 info_2;
+};
+union power_info {
struct _ATOM_POWERPLAY_INFO info;
struct _ATOM_POWERPLAY_INFO_V2 info_2;
struct _ATOM_POWERPLAY_INFO_V3 info_3;
struct _ATOM_PPLIB_POWERPLAYTABLE pplib;
struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2;
struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3;
+};
+union pplib_clock_info {
struct _ATOM_PPLIB_R600_CLOCK_INFO r600;
struct _ATOM_PPLIB_RS780_CLOCK_INFO rs780;
struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO evergreen;
struct _ATOM_PPLIB_SUMO_CLOCK_INFO sumo;
+};
+union pplib_power_state {
struct _ATOM_PPLIB_STATE v1;
struct _ATOM_PPLIB_STATE_V2 v2;
+};
+static void rs780_parse_pplib_non_clock_info(struct radeon_device *rdev,
struct radeon_ps *rps,
struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info,
u8 table_rev)
+{
rps->caps = le32_to_cpu(non_clock_info->ulCapsAndSettings);
rps->class = le16_to_cpu(non_clock_info->usClassification);
rps->class2 = le16_to_cpu(non_clock_info->usClassification2);
if (ATOM_PPLIB_NONCLOCKINFO_VER1 < table_rev) {
rps->vclk = le32_to_cpu(non_clock_info->ulVCLK);
rps->dclk = le32_to_cpu(non_clock_info->ulDCLK);
} else if (r600_is_uvd_state(rps->class, rps->class2)) {
rps->vclk = RS780_DEFAULT_VCLK_FREQ;
rps->dclk = RS780_DEFAULT_DCLK_FREQ;
} else {
rps->vclk = 0;
rps->dclk = 0;
}
if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT)
rdev->pm.dpm.boot_ps = rps;
if (rps->class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE)
rdev->pm.dpm.uvd_ps = rps;
+}
+static void rs780_parse_pplib_clock_info(struct radeon_device *rdev,
struct radeon_ps *rps,
union pplib_clock_info *clock_info)
+{
struct igp_ps *ps = rs780_get_ps(rps);
u32 sclk;
sclk = le16_to_cpu(clock_info->rs780.usLowEngineClockLow);
sclk |= clock_info->rs780.ucLowEngineClockHigh << 16;
ps->sclk_low = sclk;
sclk = le16_to_cpu(clock_info->rs780.usHighEngineClockLow);
sclk |= clock_info->rs780.ucHighEngineClockHigh << 16;
ps->sclk_high = sclk;
switch (le16_to_cpu(clock_info->rs780.usVDDC)) {
case ATOM_PPLIB_RS780_VOLTAGE_NONE:
default:
ps->min_voltage = RS780_VDDC_LEVEL_UNKNOWN;
ps->max_voltage = RS780_VDDC_LEVEL_UNKNOWN;
break;
case ATOM_PPLIB_RS780_VOLTAGE_LOW:
ps->min_voltage = RS780_VDDC_LEVEL_LOW;
ps->max_voltage = RS780_VDDC_LEVEL_LOW;
break;
case ATOM_PPLIB_RS780_VOLTAGE_HIGH:
ps->min_voltage = RS780_VDDC_LEVEL_HIGH;
ps->max_voltage = RS780_VDDC_LEVEL_HIGH;
break;
case ATOM_PPLIB_RS780_VOLTAGE_VARIABLE:
ps->min_voltage = RS780_VDDC_LEVEL_LOW;
ps->max_voltage = RS780_VDDC_LEVEL_HIGH;
break;
}
ps->flags = le32_to_cpu(clock_info->rs780.ulFlags);
if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT) {
ps->sclk_low = rdev->clock.default_sclk;
ps->sclk_high = rdev->clock.default_sclk;
ps->min_voltage = RS780_VDDC_LEVEL_HIGH;
ps->max_voltage = RS780_VDDC_LEVEL_HIGH;
}
+}
+static int rs780_parse_power_table(struct radeon_device *rdev) +{
struct radeon_mode_info *mode_info = &rdev->mode_info;
struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info;
union pplib_power_state *power_state;
int i;
union pplib_clock_info *clock_info;
union power_info *power_info;
int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo);
u16 data_offset;
u8 frev, crev;
struct igp_ps *ps;
if (!atom_parse_data_header(mode_info->atom_context, index, NULL,
&frev, &crev, &data_offset))
return -EINVAL;
power_info = (union power_info *)(mode_info->atom_context->bios + data_offset);
rdev->pm.dpm.ps = kzalloc(sizeof(struct radeon_ps) *
power_info->pplib.ucNumStates, GFP_KERNEL);
if (!rdev->pm.dpm.ps)
return -ENOMEM;
rdev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps);
rdev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime);
rdev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime);
for (i = 0; i < power_info->pplib.ucNumStates; i++) {
power_state = (union pplib_power_state *)
(mode_info->atom_context->bios + data_offset +
le16_to_cpu(power_info->pplib.usStateArrayOffset) +
i * power_info->pplib.ucStateEntrySize);
non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *)
(mode_info->atom_context->bios + data_offset +
le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset) +
(power_state->v1.ucNonClockStateIndex *
power_info->pplib.ucNonClockSize));
if (power_info->pplib.ucStateEntrySize - 1) {
clock_info = (union pplib_clock_info *)
(mode_info->atom_context->bios + data_offset +
le16_to_cpu(power_info->pplib.usClockInfoArrayOffset) +
(power_state->v1.ucClockStateIndices[0] *
power_info->pplib.ucClockInfoSize));
ps = kzalloc(sizeof(struct igp_ps), GFP_KERNEL);
if (ps == NULL) {
kfree(rdev->pm.dpm.ps);
return -ENOMEM;
}
rdev->pm.dpm.ps[i].ps_priv = ps;
rs780_parse_pplib_non_clock_info(rdev, &rdev->pm.dpm.ps[i],
non_clock_info,
power_info->pplib.ucNonClockSize);
rs780_parse_pplib_clock_info(rdev,
&rdev->pm.dpm.ps[i],
clock_info);
}
}
rdev->pm.dpm.num_ps = power_info->pplib.ucNumStates;
return 0;
+}
+int rs780_dpm_init(struct radeon_device *rdev) +{
struct igp_power_info *pi;
int index = GetIndexIntoMasterTable(DATA, IntegratedSystemInfo);
union igp_info *info;
u16 data_offset;
u8 frev, crev;
int ret;
pi = kzalloc(sizeof(struct igp_power_info), GFP_KERNEL);
if (pi == NULL)
return -ENOMEM;
rdev->pm.dpm.priv = pi;
ret = rs780_parse_power_table(rdev);
if (ret)
return ret;
pi->voltage_control = false;
pi->gfx_clock_gating = true;
if (atom_parse_data_header(rdev->mode_info.atom_context, index, NULL,
&frev, &crev, &data_offset)) {
info = (union igp_info *)(rdev->mode_info.atom_context->bios + data_offset);
/* Get various system informations from bios */
switch (crev) {
case 1:
pi->num_of_cycles_in_period =
info->info.ucNumberOfCyclesInPeriod;
pi->num_of_cycles_in_period |=
info->info.ucNumberOfCyclesInPeriodHi << 8;
pi->invert_pwm_required =
(pi->num_of_cycles_in_period & 0x8000) ? true : false;
pi->boot_voltage = info->info.ucStartingPWM_HighTime;
pi->max_voltage = info->info.ucMaxNBVoltage;
pi->max_voltage |= info->info.ucMaxNBVoltageHigh << 8;
pi->min_voltage = info->info.ucMinNBVoltage;
pi->min_voltage |= info->info.ucMinNBVoltageHigh << 8;
pi->inter_voltage_low =
le16_to_cpu(info->info.usInterNBVoltageLow);
pi->inter_voltage_high =
le16_to_cpu(info->info.usInterNBVoltageHigh);
pi->voltage_control = true;
pi->bootup_uma_clk = info->info.usK8MemoryClock * 100;
break;
case 2:
pi->num_of_cycles_in_period =
le16_to_cpu(info->info_2.usNumberOfCyclesInPeriod);
pi->invert_pwm_required =
(pi->num_of_cycles_in_period & 0x8000) ? true : false;
pi->boot_voltage =
le16_to_cpu(info->info_2.usBootUpNBVoltage);
pi->max_voltage =
le16_to_cpu(info->info_2.usMaxNBVoltage);
pi->min_voltage =
le16_to_cpu(info->info_2.usMinNBVoltage);
pi->system_config =
le32_to_cpu(info->info_2.ulSystemConfig);
pi->pwm_voltage_control =
(pi->system_config & 0x4) ? true : false;
pi->voltage_control = true;
pi->bootup_uma_clk = le32_to_cpu(info->info_2.ulBootUpUMAClock);
break;
default:
DRM_ERROR("No integrated system info for your GPU\n");
return -EINVAL;
}
if (pi->min_voltage > pi->max_voltage)
pi->voltage_control = false;
if (pi->pwm_voltage_control) {
if ((pi->num_of_cycles_in_period == 0) ||
(pi->max_voltage == 0) ||
(pi->min_voltage == 0))
pi->voltage_control = false;
} else {
if ((pi->num_of_cycles_in_period == 0) ||
(pi->max_voltage == 0))
pi->voltage_control = false;
}
return 0;
}
radeon_dpm_fini(rdev);
return -EINVAL;
+}
+void rs780_dpm_print_power_state(struct radeon_device *rdev,
struct radeon_ps *rps)
+{
struct igp_ps *ps = rs780_get_ps(rps);
r600_dpm_print_class_info(rps->class, rps->class2);
r600_dpm_print_cap_info(rps->caps);
printk("\tuvd vclk: %d dclk: %d\n", rps->vclk, rps->dclk);
printk("\t\tpower level 0 sclk: %u vddc_index: %d\n",
ps->sclk_low, ps->min_voltage);
printk("\t\tpower level 1 sclk: %u vddc_index: %d\n",
ps->sclk_high, ps->max_voltage);
r600_dpm_print_ps_status(rdev, rps);
+}
+void rs780_dpm_fini(struct radeon_device *rdev) +{
int i;
for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
kfree(rdev->pm.dpm.ps[i].ps_priv);
}
kfree(rdev->pm.dpm.ps);
kfree(rdev->pm.dpm.priv);
+}
+u32 rs780_dpm_get_sclk(struct radeon_device *rdev, bool low) +{
struct igp_ps *requested_state = rs780_get_ps(rdev->pm.dpm.requested_ps);
if (low)
return requested_state->sclk_low;
else
return requested_state->sclk_high;
+}
+u32 rs780_dpm_get_mclk(struct radeon_device *rdev, bool low) +{
struct igp_power_info *pi = rs780_get_pi(rdev);
return pi->bootup_uma_clk;
+} diff --git a/drivers/gpu/drm/radeon/rs780_dpm.h b/drivers/gpu/drm/radeon/rs780_dpm.h new file mode 100644 index 0000000..47a40b1 --- /dev/null +++ b/drivers/gpu/drm/radeon/rs780_dpm.h @@ -0,0 +1,109 @@ +/*
- Copyright 2011 Advanced Micro Devices, Inc.
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- OTHER DEALINGS IN THE SOFTWARE.
- */
+#ifndef __RS780_DPM_H__ +#define __RS780_DPM_H__
+enum rs780_vddc_level {
RS780_VDDC_LEVEL_UNKNOWN = 0,
RS780_VDDC_LEVEL_LOW = 1,
RS780_VDDC_LEVEL_HIGH = 2,
+};
+struct igp_power_info {
/* flags */
bool invert_pwm_required;
bool pwm_voltage_control;
bool voltage_control;
bool gfx_clock_gating;
/* stored values */
u32 system_config;
u32 bootup_uma_clk;
u16 max_voltage;
u16 min_voltage;
u16 boot_voltage;
u16 inter_voltage_low;
u16 inter_voltage_high;
u16 num_of_cycles_in_period;
/* variable */
int crtc_id;
int refresh_rate;
+};
+struct igp_ps {
enum rs780_vddc_level min_voltage;
enum rs780_vddc_level max_voltage;
u32 sclk_low;
u32 sclk_high;
u32 flags;
+};
+#define RS780_CGFTV_DFLT 0x0303000f +#define RS780_FBDIVTIMERVAL_DFLT 0x2710
+#define RS780_FVTHROTUTC0_DFLT 0x04010040 +#define RS780_FVTHROTUTC1_DFLT 0x04010040 +#define RS780_FVTHROTUTC2_DFLT 0x04010040 +#define RS780_FVTHROTUTC3_DFLT 0x04010040 +#define RS780_FVTHROTUTC4_DFLT 0x04010040
+#define RS780_FVTHROTDTC0_DFLT 0x04010040 +#define RS780_FVTHROTDTC1_DFLT 0x04010040 +#define RS780_FVTHROTDTC2_DFLT 0x04010040 +#define RS780_FVTHROTDTC3_DFLT 0x04010040 +#define RS780_FVTHROTDTC4_DFLT 0x04010040
+#define RS780_FVTHROTFBUSREG0_DFLT 0x00001001 +#define RS780_FVTHROTFBUSREG1_DFLT 0x00002002 +#define RS780_FVTHROTFBDSREG0_DFLT 0x00004001 +#define RS780_FVTHROTFBDSREG1_DFLT 0x00020010
+#define RS780_FVTHROTPWMUSREG0_DFLT 0x00002001 +#define RS780_FVTHROTPWMUSREG1_DFLT 0x00004003 +#define RS780_FVTHROTPWMDSREG0_DFLT 0x00002001 +#define RS780_FVTHROTPWMDSREG1_DFLT 0x00004003
+#define RS780_FVTHROTPWMFBDIVRANGEREG0_DFLT 0x37 +#define RS780_FVTHROTPWMFBDIVRANGEREG1_DFLT 0x4b +#define RS780_FVTHROTPWMFBDIVRANGEREG2_DFLT 0x8b
+#define RS780D_FVTHROTPWMFBDIVRANGEREG0_DFLT 0x8b +#define RS780D_FVTHROTPWMFBDIVRANGEREG1_DFLT 0x8c +#define RS780D_FVTHROTPWMFBDIVRANGEREG2_DFLT 0xb5
+#define RS880D_FVTHROTPWMFBDIVRANGEREG0_DFLT 0x8d +#define RS880D_FVTHROTPWMFBDIVRANGEREG1_DFLT 0x8e +#define RS880D_FVTHROTPWMFBDIVRANGEREG2_DFLT 0xBa
+#define RS780_FVTHROTPWMRANGE0_GPIO_DFLT 0x1a +#define RS780_FVTHROTPWMRANGE1_GPIO_DFLT 0x1a +#define RS780_FVTHROTPWMRANGE2_GPIO_DFLT 0x0 +#define RS780_FVTHROTPWMRANGE3_GPIO_DFLT 0x0
+#define RS780_SLOWCLKFEEDBACKDIV_DFLT 110
+#define RS780_CGCLKGATING_DFLT 0x0000E204
+#define RS780_DEFAULT_VCLK_FREQ 53300 /* 10 khz */ +#define RS780_DEFAULT_DCLK_FREQ 40000 /* 10 khz */
+#endif diff --git a/drivers/gpu/drm/radeon/rs780d.h b/drivers/gpu/drm/radeon/rs780d.h new file mode 100644 index 0000000..b1142ed --- /dev/null +++ b/drivers/gpu/drm/radeon/rs780d.h @@ -0,0 +1,168 @@ +/*
- Copyright 2011 Advanced Micro Devices, Inc.
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- OTHER DEALINGS IN THE SOFTWARE.
- */
+#ifndef __RS780D_H__ +#define __RS780D_H__
+#define CG_SPLL_FUNC_CNTL 0x600 +# define SPLL_RESET (1 << 0) +# define SPLL_SLEEP (1 << 1) +# define SPLL_REF_DIV(x) ((x) << 2) +# define SPLL_REF_DIV_MASK (7 << 2) +# define SPLL_FB_DIV(x) ((x) << 5) +# define SPLL_FB_DIV_MASK (0xff << 2) +# define SPLL_FB_DIV_SHIFT 2 +# define SPLL_PULSEEN (1 << 13) +# define SPLL_PULSENUM(x) ((x) << 14) +# define SPLL_PULSENUM_MASK (3 << 14) +# define SPLL_SW_HILEN(x) ((x) << 16) +# define SPLL_SW_HILEN_MASK (0xf << 16) +# define SPLL_SW_LOLEN(x) ((x) << 20) +# define SPLL_SW_LOLEN_MASK (0xf << 20) +# define SPLL_DIVEN (1 << 24) +# define SPLL_BYPASS_EN (1 << 25) +# define SPLL_CHG_STATUS (1 << 29) +# define SPLL_CTLREQ (1 << 30) +# define SPLL_CTLACK (1 << 31)
+/* RS780/RS880 PM */ +#define FVTHROT_CNTRL_REG 0x3000 +#define DONT_WAIT_FOR_FBDIV_WRAP (1 << 0) +#define MINIMUM_CIP(x) ((x) << 1) +#define MINIMUM_CIP_SHIFT 1 +#define MINIMUM_CIP_MASK 0x1fffffe +#define REFRESH_RATE_DIVISOR(x) ((x) << 25) +#define REFRESH_RATE_DIVISOR_SHIFT 25 +#define REFRESH_RATE_DIVISOR_MASK (0x3 << 25) +#define ENABLE_FV_THROT (1 << 27) +#define ENABLE_FV_UPDATE (1 << 28) +#define TREND_SEL_MODE (1 << 29) +#define FORCE_TREND_SEL (1 << 30) +#define ENABLE_FV_THROT_IO (1 << 31) +#define FVTHROT_TARGET_REG 0x3004 +#define TARGET_IDLE_COUNT(x) ((x) << 0) +#define TARGET_IDLE_COUNT_MASK 0xffffff +#define TARGET_IDLE_COUNT_SHIFT 0 +#define FVTHROT_CB1 0x3008 +#define FVTHROT_CB2 0x300c +#define FVTHROT_CB3 0x3010 +#define FVTHROT_CB4 0x3014 +#define FVTHROT_UTC0 0x3018 +#define FVTHROT_UTC1 0x301c +#define FVTHROT_UTC2 0x3020 +#define FVTHROT_UTC3 0x3024 +#define FVTHROT_UTC4 0x3028 +#define FVTHROT_DTC0 0x302c +#define FVTHROT_DTC1 0x3030 +#define FVTHROT_DTC2 0x3034 +#define FVTHROT_DTC3 0x3038 +#define FVTHROT_DTC4 0x303c +#define FVTHROT_FBDIV_REG0 0x3040 +#define MIN_FEEDBACK_DIV(x) ((x) << 0) +#define MIN_FEEDBACK_DIV_MASK 0xfff +#define MIN_FEEDBACK_DIV_SHIFT 0 +#define MAX_FEEDBACK_DIV(x) ((x) << 12) +#define MAX_FEEDBACK_DIV_MASK (0xfff << 12) +#define MAX_FEEDBACK_DIV_SHIFT 12 +#define FVTHROT_FBDIV_REG1 0x3044 +#define MAX_FEEDBACK_STEP(x) ((x) << 0) +#define MAX_FEEDBACK_STEP_MASK 0xfff +#define MAX_FEEDBACK_STEP_SHIFT 0 +#define STARTING_FEEDBACK_DIV(x) ((x) << 12) +#define STARTING_FEEDBACK_DIV_MASK (0xfff << 12) +#define STARTING_FEEDBACK_DIV_SHIFT 12 +#define FORCE_FEEDBACK_DIV (1 << 24) +#define FVTHROT_FBDIV_REG2 0x3048 +#define FORCED_FEEDBACK_DIV(x) ((x) << 0) +#define FORCED_FEEDBACK_DIV_MASK 0xfff +#define FORCED_FEEDBACK_DIV_SHIFT 0 +#define FB_DIV_TIMER_VAL(x) ((x) << 12) +#define FB_DIV_TIMER_VAL_MASK (0xffff << 12) +#define FB_DIV_TIMER_VAL_SHIFT 12 +#define FVTHROT_FB_US_REG0 0x304c +#define FVTHROT_FB_US_REG1 0x3050 +#define FVTHROT_FB_DS_REG0 0x3054 +#define FVTHROT_FB_DS_REG1 0x3058 +#define FVTHROT_PWM_CTRL_REG0 0x305c +#define STARTING_PWM_HIGHTIME(x) ((x) << 0) +#define STARTING_PWM_HIGHTIME_MASK 0xfff +#define STARTING_PWM_HIGHTIME_SHIFT 0 +#define NUMBER_OF_CYCLES_IN_PERIOD(x) ((x) << 12) +#define NUMBER_OF_CYCLES_IN_PERIOD_MASK (0xfff << 12) +#define NUMBER_OF_CYCLES_IN_PERIOD_SHIFT 12 +#define FORCE_STARTING_PWM_HIGHTIME (1 << 24) +#define INVERT_PWM_WAVEFORM (1 << 25) +#define FVTHROT_PWM_CTRL_REG1 0x3060 +#define MIN_PWM_HIGHTIME(x) ((x) << 0) +#define MIN_PWM_HIGHTIME_MASK 0xfff +#define MIN_PWM_HIGHTIME_SHIFT 0 +#define MAX_PWM_HIGHTIME(x) ((x) << 12) +#define MAX_PWM_HIGHTIME_MASK (0xfff << 12) +#define MAX_PWM_HIGHTIME_SHIFT 12 +#define FVTHROT_PWM_US_REG0 0x3064 +#define FVTHROT_PWM_US_REG1 0x3068 +#define FVTHROT_PWM_DS_REG0 0x306c +#define FVTHROT_PWM_DS_REG1 0x3070 +#define FVTHROT_STATUS_REG0 0x3074 +#define CURRENT_FEEDBACK_DIV_MASK 0xfff +#define CURRENT_FEEDBACK_DIV_SHIFT 0 +#define FVTHROT_STATUS_REG1 0x3078 +#define FVTHROT_STATUS_REG2 0x307c +#define CG_INTGFX_MISC 0x3080 +#define FVTHROT_VBLANK_SEL (1 << 9) +#define FVTHROT_PWM_FEEDBACK_DIV_REG1 0x308c +#define RANGE0_PWM_FEEDBACK_DIV(x) ((x) << 0) +#define RANGE0_PWM_FEEDBACK_DIV_MASK 0xfff +#define RANGE0_PWM_FEEDBACK_DIV_SHIFT 0 +#define RANGE_PWM_FEEDBACK_DIV_EN (1 << 12) +#define FVTHROT_PWM_FEEDBACK_DIV_REG2 0x3090 +#define RANGE1_PWM_FEEDBACK_DIV(x) ((x) << 0) +#define RANGE1_PWM_FEEDBACK_DIV_MASK 0xfff +#define RANGE1_PWM_FEEDBACK_DIV_SHIFT 0 +#define RANGE2_PWM_FEEDBACK_DIV(x) ((x) << 12) +#define RANGE2_PWM_FEEDBACK_DIV_MASK (0xfff << 12) +#define RANGE2_PWM_FEEDBACK_DIV_SHIFT 12 +#define FVTHROT_PWM_FEEDBACK_DIV_REG3 0x3094 +#define RANGE0_PWM(x) ((x) << 0) +#define RANGE0_PWM_MASK 0xfff +#define RANGE0_PWM_SHIFT 0 +#define RANGE1_PWM(x) ((x) << 12) +#define RANGE1_PWM_MASK (0xfff << 12) +#define RANGE1_PWM_SHIFT 12 +#define FVTHROT_PWM_FEEDBACK_DIV_REG4 0x3098 +#define RANGE2_PWM(x) ((x) << 0) +#define RANGE2_PWM_MASK 0xfff +#define RANGE2_PWM_SHIFT 0 +#define RANGE3_PWM(x) ((x) << 12) +#define RANGE3_PWM_MASK (0xfff << 12) +#define RANGE3_PWM_SHIFT 12 +#define FVTHROT_SLOW_CLK_FEEDBACK_DIV_REG1 0x30ac +#define RANGE0_SLOW_CLK_FEEDBACK_DIV(x) ((x) << 0) +#define RANGE0_SLOW_CLK_FEEDBACK_DIV_MASK 0xfff +#define RANGE0_SLOW_CLK_FEEDBACK_DIV_SHIFT 0 +#define RANGE_SLOW_CLK_FEEDBACK_DIV_EN (1 << 12)
+#define GFX_MACRO_BYPASS_CNTL 0x30c0 +#define SPLL_BYPASS_CNTL (1 << 0) +#define UPLL_BYPASS_CNTL (1 << 1)
+#endif
1.7.7.5
dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
On Wed, Jun 26, 2013 at 9:18 AM, Jerome Glisse j.glisse@gmail.com wrote:
On Wed, Jun 26, 2013 at 02:19:23PM -0400, Alex Deucher wrote:
On Wed, Jun 26, 2013 at 6:46 AM, Jerome Glisse j.glisse@gmail.com wrote:
On Wed, Jun 26, 2013 at 09:22:35AM -0400, alexdeucher@gmail.com wrote:
From: Alex Deucher alexander.deucher@amd.com
This adds dpm support for rs780/rs880 asics. This includes:
- clockgating
- dynamic engine clock scaling
- dynamic voltage scaling
set radeon.dpm=1 to enable it.
Signed-off-by: Alex Deucher alexander.deucher@amd.com
Depending on the answer to inline question :
Reviewed-by: Jerome Glisse jglisse@redhat.com
drivers/gpu/drm/radeon/Makefile | 2 +- drivers/gpu/drm/radeon/radeon_asic.c | 12 + drivers/gpu/drm/radeon/radeon_asic.h | 12 + drivers/gpu/drm/radeon/radeon_pm.c | 7 + drivers/gpu/drm/radeon/rs780_dpm.c | 894 ++++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/rs780_dpm.h | 109 ++++ drivers/gpu/drm/radeon/rs780d.h | 168 +++++++ 7 files changed, 1203 insertions(+), 1 deletions(-) create mode 100644 drivers/gpu/drm/radeon/rs780_dpm.c create mode 100644 drivers/gpu/drm/radeon/rs780_dpm.h create mode 100644 drivers/gpu/drm/radeon/rs780d.h
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index a131a13..e44b046 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile @@ -77,7 +77,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \ evergreen_hdmi.o radeon_trace_points.o ni.o cayman_blit_shaders.o \ atombios_encoders.o radeon_semaphore.o radeon_sa.o atombios_i2c.o si.o \ si_blit_shaders.o radeon_prime.o radeon_uvd.o cik.o cik_blit_shaders.o \
r600_dpm.o
r600_dpm.o rs780_dpm.o
radeon-$(CONFIG_COMPAT) += radeon_ioc32.o radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index d9c8e9a..db3c930 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1194,6 +1194,18 @@ static struct radeon_asic rs780_asic = { .set_clock_gating = NULL, .get_temperature = &rv6xx_get_temp, },
.dpm = {
.init = &rs780_dpm_init,
.setup_asic = &rs780_dpm_setup_asic,
.enable = &rs780_dpm_enable,
.disable = &rs780_dpm_disable,
.set_power_state = &rs780_dpm_set_power_state,
.display_configuration_changed = &rs780_dpm_display_configuration_changed,
.fini = &rs780_dpm_fini,
.get_sclk = &rs780_dpm_get_sclk,
.get_mclk = &rs780_dpm_get_mclk,
.print_power_state = &rs780_dpm_print_power_state,
}, .pflip = { .pre_page_flip = &rs600_pre_page_flip, .page_flip = &rs600_page_flip,
diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 8507cae..134bf57 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -396,6 +396,18 @@ int r600_mc_wait_for_idle(struct radeon_device *rdev); u32 r600_get_xclk(struct radeon_device *rdev); uint64_t r600_get_gpu_clock_counter(struct radeon_device *rdev); int rv6xx_get_temp(struct radeon_device *rdev); +/* rs780 dpm */ +int rs780_dpm_init(struct radeon_device *rdev); +int rs780_dpm_enable(struct radeon_device *rdev); +void rs780_dpm_disable(struct radeon_device *rdev); +int rs780_dpm_set_power_state(struct radeon_device *rdev); +void rs780_dpm_setup_asic(struct radeon_device *rdev); +void rs780_dpm_display_configuration_changed(struct radeon_device *rdev); +void rs780_dpm_fini(struct radeon_device *rdev); +u32 rs780_dpm_get_sclk(struct radeon_device *rdev, bool low); +u32 rs780_dpm_get_mclk(struct radeon_device *rdev, bool low); +void rs780_dpm_print_power_state(struct radeon_device *rdev,
struct radeon_ps *ps);
/* uvd */ int r600_uvd_init(struct radeon_device *rdev); diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 4f5422e..853a8a2 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -1030,6 +1030,13 @@ int radeon_pm_init(struct radeon_device *rdev) { /* enable dpm on rv6xx+ */ switch (rdev->family) {
case CHIP_RS780:
case CHIP_RS880:
if (radeon_dpm == 1)
rdev->pm.pm_method = PM_METHOD_DPM;
else
rdev->pm.pm_method = PM_METHOD_PROFILE;
break; default: /* default to profile method */ rdev->pm.pm_method = PM_METHOD_PROFILE;
diff --git a/drivers/gpu/drm/radeon/rs780_dpm.c b/drivers/gpu/drm/radeon/rs780_dpm.c new file mode 100644 index 0000000..f594900 --- /dev/null +++ b/drivers/gpu/drm/radeon/rs780_dpm.c @@ -0,0 +1,894 @@ +/*
- Copyright 2011 Advanced Micro Devices, 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: Alex Deucher
- */
+#include "drmP.h" +#include "radeon.h" +#include "rs780d.h" +#include "r600_dpm.h" +#include "rs780_dpm.h" +#include "atom.h"
+static struct igp_ps *rs780_get_ps(struct radeon_ps *rps) +{
struct igp_ps *ps = rps->ps_priv;
return ps;
+}
+static struct igp_power_info *rs780_get_pi(struct radeon_device *rdev) +{
struct igp_power_info *pi = rdev->pm.dpm.priv;
return pi;
+}
+static void rs780_get_pm_mode_parameters(struct radeon_device *rdev) +{
struct igp_power_info *pi = rs780_get_pi(rdev);
struct radeon_mode_info *minfo = &rdev->mode_info;
struct drm_crtc *crtc;
struct radeon_crtc *radeon_crtc;
int i;
/* defaults */
pi->crtc_id = 0;
pi->refresh_rate = 60;
for (i = 0; i < rdev->num_crtc; i++) {
crtc = (struct drm_crtc *)minfo->crtcs[i];
if (crtc && crtc->enabled) {
radeon_crtc = to_radeon_crtc(crtc);
pi->crtc_id = radeon_crtc->crtc_id;
if (crtc->mode.htotal && crtc->mode.vtotal)
pi->refresh_rate =
(crtc->mode.clock * 1000) /
(crtc->mode.htotal * crtc->mode.vtotal);
break;
}
}
Ok this looks wrong to me you look for the first enabled crtc but on those iirc there could be 2 so first one might be a low refresh rate and second one an higher one. Thus returning the first one might lead to PM decision that are wrong.
So what happens when 2 are active ? Flickering on the unsync one ?
In theory, although changing sclk and voltage can be done outside of the vblank period on most asics. vblank is mainly needed for memory related operations. I'm not sure what exactly is affected on rs780. Too old. Hard to find docs on it.
Alex
Cheers, Jerome
On RS780/RS880, the hardware can only synchronize with a single crtc, so it doesn't really matter which one we pick as long as it's active.
+}
+static void rs780_voltage_scaling_enable(struct radeon_device *rdev, bool enable);
+static int rs780_initialize_dpm_power_state(struct radeon_device *rdev) +{
struct atom_clock_dividers dividers;
struct igp_ps *default_state = rs780_get_ps(rdev->pm.dpm.boot_ps);
int i, ret;
ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
default_state->sclk_low, false, ÷rs);
if (ret)
return ret;
r600_engine_clock_entry_set_reference_divider(rdev, 0, dividers.ref_div);
r600_engine_clock_entry_set_feedback_divider(rdev, 0, dividers.fb_div);
r600_engine_clock_entry_set_post_divider(rdev, 0, dividers.post_div);
if (dividers.enable_post_div)
r600_engine_clock_entry_enable_post_divider(rdev, 0, true);
else
r600_engine_clock_entry_enable_post_divider(rdev, 0, false);
r600_engine_clock_entry_set_step_time(rdev, 0, R600_SST_DFLT);
r600_engine_clock_entry_enable_pulse_skipping(rdev, 0, false);
r600_engine_clock_entry_enable(rdev, 0, true);
for (i = 1; i < R600_PM_NUMBER_OF_SCLKS; i++)
r600_engine_clock_entry_enable(rdev, i, false);
r600_enable_mclk_control(rdev, false);
r600_voltage_control_enable_pins(rdev, 0);
return 0;
+}
+static int rs780_initialize_dpm_parameters(struct radeon_device *rdev) +{
int ret = 0;
int i;
r600_set_bsp(rdev, R600_BSU_DFLT, R600_BSP_DFLT);
r600_set_at(rdev, 0, 0, 0, 0);
r600_set_git(rdev, R600_GICST_DFLT);
for (i = 0; i < R600_PM_NUMBER_OF_TC; i++)
r600_set_tc(rdev, i, 0, 0);
r600_select_td(rdev, R600_TD_DFLT);
r600_set_vrc(rdev, 0);
r600_set_tpu(rdev, R600_TPU_DFLT);
r600_set_tpc(rdev, R600_TPC_DFLT);
r600_set_sstu(rdev, R600_SSTU_DFLT);
r600_set_sst(rdev, R600_SST_DFLT);
r600_set_fctu(rdev, R600_FCTU_DFLT);
r600_set_fct(rdev, R600_FCT_DFLT);
r600_set_vddc3d_oorsu(rdev, R600_VDDC3DOORSU_DFLT);
r600_set_vddc3d_oorphc(rdev, R600_VDDC3DOORPHC_DFLT);
r600_set_vddc3d_oorsdc(rdev, R600_VDDC3DOORSDC_DFLT);
r600_set_ctxcgtt3d_rphc(rdev, R600_CTXCGTT3DRPHC_DFLT);
r600_set_ctxcgtt3d_rsdc(rdev, R600_CTXCGTT3DRSDC_DFLT);
r600_vid_rt_set_vru(rdev, R600_VRU_DFLT);
r600_vid_rt_set_vrt(rdev, R600_VOLTAGERESPONSETIME_DFLT);
r600_vid_rt_set_ssu(rdev, R600_SPLLSTEPUNIT_DFLT);
ret = rs780_initialize_dpm_power_state(rdev);
r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_LOW, 0);
r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_MEDIUM, 0);
r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_HIGH, 0);
r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_LOW, 0);
r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_MEDIUM, 0);
r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_HIGH, 0);
r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_LOW, 0);
r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_MEDIUM, 0);
r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_HIGH, 0);
r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_LOW, R600_DISPLAY_WATERMARK_HIGH);
r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_MEDIUM, R600_DISPLAY_WATERMARK_HIGH);
r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_HIGH, R600_DISPLAY_WATERMARK_HIGH);
r600_power_level_enable(rdev, R600_POWER_LEVEL_CTXSW, false);
r600_power_level_enable(rdev, R600_POWER_LEVEL_HIGH, false);
r600_power_level_enable(rdev, R600_POWER_LEVEL_MEDIUM, false);
r600_power_level_enable(rdev, R600_POWER_LEVEL_LOW, true);
r600_power_level_set_enter_index(rdev, R600_POWER_LEVEL_LOW);
r600_set_vrc(rdev, RS780_CGFTV_DFLT);
return ret;
+}
+static void rs780_start_dpm(struct radeon_device *rdev) +{
r600_enable_sclk_control(rdev, false);
r600_enable_mclk_control(rdev, false);
r600_dynamicpm_enable(rdev, true);
radeon_wait_for_vblank(rdev, 0);
radeon_wait_for_vblank(rdev, 1);
r600_enable_spll_bypass(rdev, true);
r600_wait_for_spll_change(rdev);
r600_enable_spll_bypass(rdev, false);
r600_wait_for_spll_change(rdev);
r600_enable_spll_bypass(rdev, true);
r600_wait_for_spll_change(rdev);
r600_enable_spll_bypass(rdev, false);
r600_wait_for_spll_change(rdev);
r600_enable_sclk_control(rdev, true);
+}
+static void rs780_preset_ranges_slow_clk_fbdiv_en(struct radeon_device *rdev) +{
WREG32_P(FVTHROT_SLOW_CLK_FEEDBACK_DIV_REG1, RANGE_SLOW_CLK_FEEDBACK_DIV_EN,
~RANGE_SLOW_CLK_FEEDBACK_DIV_EN);
WREG32_P(FVTHROT_SLOW_CLK_FEEDBACK_DIV_REG1,
RANGE0_SLOW_CLK_FEEDBACK_DIV(RS780_SLOWCLKFEEDBACKDIV_DFLT),
~RANGE0_SLOW_CLK_FEEDBACK_DIV_MASK);
+}
+static void rs780_preset_starting_fbdiv(struct radeon_device *rdev) +{
u32 fbdiv = (RREG32(CG_SPLL_FUNC_CNTL) & SPLL_FB_DIV_MASK) >> SPLL_FB_DIV_SHIFT;
WREG32_P(FVTHROT_FBDIV_REG1, STARTING_FEEDBACK_DIV(fbdiv),
~STARTING_FEEDBACK_DIV_MASK);
WREG32_P(FVTHROT_FBDIV_REG2, FORCED_FEEDBACK_DIV(fbdiv),
~FORCED_FEEDBACK_DIV_MASK);
WREG32_P(FVTHROT_FBDIV_REG1, FORCE_FEEDBACK_DIV, ~FORCE_FEEDBACK_DIV);
+}
+static void rs780_voltage_scaling_init(struct radeon_device *rdev) +{
struct igp_power_info *pi = rs780_get_pi(rdev);
struct drm_device *dev = rdev->ddev;
u32 fv_throt_pwm_fb_div_range[3];
u32 fv_throt_pwm_range[4];
if (dev->pdev->device == 0x9614) {
fv_throt_pwm_fb_div_range[0] = RS780D_FVTHROTPWMFBDIVRANGEREG0_DFLT;
fv_throt_pwm_fb_div_range[1] = RS780D_FVTHROTPWMFBDIVRANGEREG1_DFLT;
fv_throt_pwm_fb_div_range[2] = RS780D_FVTHROTPWMFBDIVRANGEREG2_DFLT;
} else if ((dev->pdev->device == 0x9714) ||
(dev->pdev->device == 0x9715)) {
fv_throt_pwm_fb_div_range[0] = RS880D_FVTHROTPWMFBDIVRANGEREG0_DFLT;
fv_throt_pwm_fb_div_range[1] = RS880D_FVTHROTPWMFBDIVRANGEREG1_DFLT;
fv_throt_pwm_fb_div_range[2] = RS880D_FVTHROTPWMFBDIVRANGEREG2_DFLT;
} else {
fv_throt_pwm_fb_div_range[0] = RS780_FVTHROTPWMFBDIVRANGEREG0_DFLT;
fv_throt_pwm_fb_div_range[1] = RS780_FVTHROTPWMFBDIVRANGEREG1_DFLT;
fv_throt_pwm_fb_div_range[2] = RS780_FVTHROTPWMFBDIVRANGEREG2_DFLT;
}
if (pi->pwm_voltage_control) {
fv_throt_pwm_range[0] = pi->min_voltage;
fv_throt_pwm_range[1] = pi->min_voltage;
fv_throt_pwm_range[2] = pi->max_voltage;
fv_throt_pwm_range[3] = pi->max_voltage;
} else {
fv_throt_pwm_range[0] = pi->invert_pwm_required ?
RS780_FVTHROTPWMRANGE3_GPIO_DFLT : RS780_FVTHROTPWMRANGE0_GPIO_DFLT;
fv_throt_pwm_range[1] = pi->invert_pwm_required ?
RS780_FVTHROTPWMRANGE2_GPIO_DFLT : RS780_FVTHROTPWMRANGE1_GPIO_DFLT;
fv_throt_pwm_range[2] = pi->invert_pwm_required ?
RS780_FVTHROTPWMRANGE1_GPIO_DFLT : RS780_FVTHROTPWMRANGE2_GPIO_DFLT;
fv_throt_pwm_range[3] = pi->invert_pwm_required ?
RS780_FVTHROTPWMRANGE0_GPIO_DFLT : RS780_FVTHROTPWMRANGE3_GPIO_DFLT;
}
WREG32_P(FVTHROT_PWM_CTRL_REG0,
STARTING_PWM_HIGHTIME(pi->max_voltage),
~STARTING_PWM_HIGHTIME_MASK);
WREG32_P(FVTHROT_PWM_CTRL_REG0,
NUMBER_OF_CYCLES_IN_PERIOD(pi->num_of_cycles_in_period),
~NUMBER_OF_CYCLES_IN_PERIOD_MASK);
WREG32_P(FVTHROT_PWM_CTRL_REG0, FORCE_STARTING_PWM_HIGHTIME,
~FORCE_STARTING_PWM_HIGHTIME);
if (pi->invert_pwm_required)
WREG32_P(FVTHROT_PWM_CTRL_REG0, INVERT_PWM_WAVEFORM, ~INVERT_PWM_WAVEFORM);
else
WREG32_P(FVTHROT_PWM_CTRL_REG0, 0, ~INVERT_PWM_WAVEFORM);
rs780_voltage_scaling_enable(rdev, true);
WREG32(FVTHROT_PWM_CTRL_REG1,
(MIN_PWM_HIGHTIME(pi->min_voltage) |
MAX_PWM_HIGHTIME(pi->max_voltage)));
WREG32(FVTHROT_PWM_US_REG0, RS780_FVTHROTPWMUSREG0_DFLT);
WREG32(FVTHROT_PWM_US_REG1, RS780_FVTHROTPWMUSREG1_DFLT);
WREG32(FVTHROT_PWM_DS_REG0, RS780_FVTHROTPWMDSREG0_DFLT);
WREG32(FVTHROT_PWM_DS_REG1, RS780_FVTHROTPWMDSREG1_DFLT);
WREG32_P(FVTHROT_PWM_FEEDBACK_DIV_REG1,
RANGE0_PWM_FEEDBACK_DIV(fv_throt_pwm_fb_div_range[0]),
~RANGE0_PWM_FEEDBACK_DIV_MASK);
WREG32(FVTHROT_PWM_FEEDBACK_DIV_REG2,
(RANGE1_PWM_FEEDBACK_DIV(fv_throt_pwm_fb_div_range[1]) |
RANGE2_PWM_FEEDBACK_DIV(fv_throt_pwm_fb_div_range[2])));
WREG32(FVTHROT_PWM_FEEDBACK_DIV_REG3,
(RANGE0_PWM(fv_throt_pwm_range[1]) |
RANGE1_PWM(fv_throt_pwm_range[2])));
WREG32(FVTHROT_PWM_FEEDBACK_DIV_REG4,
(RANGE2_PWM(fv_throt_pwm_range[1]) |
RANGE3_PWM(fv_throt_pwm_range[2])));
+}
+static void rs780_clk_scaling_enable(struct radeon_device *rdev, bool enable) +{
if (enable)
WREG32_P(FVTHROT_CNTRL_REG, ENABLE_FV_THROT | ENABLE_FV_UPDATE,
~(ENABLE_FV_THROT | ENABLE_FV_UPDATE));
else
WREG32_P(FVTHROT_CNTRL_REG, 0,
~(ENABLE_FV_THROT | ENABLE_FV_UPDATE));
+}
+static void rs780_voltage_scaling_enable(struct radeon_device *rdev, bool enable) +{
if (enable)
WREG32_P(FVTHROT_CNTRL_REG, ENABLE_FV_THROT_IO, ~ENABLE_FV_THROT_IO);
else
WREG32_P(FVTHROT_CNTRL_REG, 0, ~ENABLE_FV_THROT_IO);
+}
+static void rs780_set_engine_clock_wfc(struct radeon_device *rdev) +{
WREG32(FVTHROT_UTC0, RS780_FVTHROTUTC0_DFLT);
WREG32(FVTHROT_UTC1, RS780_FVTHROTUTC1_DFLT);
WREG32(FVTHROT_UTC2, RS780_FVTHROTUTC2_DFLT);
WREG32(FVTHROT_UTC3, RS780_FVTHROTUTC3_DFLT);
WREG32(FVTHROT_UTC4, RS780_FVTHROTUTC4_DFLT);
WREG32(FVTHROT_DTC0, RS780_FVTHROTDTC0_DFLT);
WREG32(FVTHROT_DTC1, RS780_FVTHROTDTC1_DFLT);
WREG32(FVTHROT_DTC2, RS780_FVTHROTDTC2_DFLT);
WREG32(FVTHROT_DTC3, RS780_FVTHROTDTC3_DFLT);
WREG32(FVTHROT_DTC4, RS780_FVTHROTDTC4_DFLT);
+}
+static void rs780_set_engine_clock_sc(struct radeon_device *rdev) +{
WREG32_P(FVTHROT_FBDIV_REG2,
FB_DIV_TIMER_VAL(RS780_FBDIVTIMERVAL_DFLT),
~FB_DIV_TIMER_VAL_MASK);
WREG32_P(FVTHROT_CNTRL_REG,
REFRESH_RATE_DIVISOR(0) | MINIMUM_CIP(0xf),
~(REFRESH_RATE_DIVISOR_MASK | MINIMUM_CIP_MASK));
+}
+static void rs780_set_engine_clock_tdc(struct radeon_device *rdev) +{
WREG32_P(FVTHROT_CNTRL_REG, 0, ~(FORCE_TREND_SEL | TREND_SEL_MODE));
+}
+static void rs780_set_engine_clock_ssc(struct radeon_device *rdev) +{
WREG32(FVTHROT_FB_US_REG0, RS780_FVTHROTFBUSREG0_DFLT);
WREG32(FVTHROT_FB_US_REG1, RS780_FVTHROTFBUSREG1_DFLT);
WREG32(FVTHROT_FB_DS_REG0, RS780_FVTHROTFBDSREG0_DFLT);
WREG32(FVTHROT_FB_DS_REG1, RS780_FVTHROTFBDSREG1_DFLT);
WREG32_P(FVTHROT_FBDIV_REG1, MAX_FEEDBACK_STEP(1), ~MAX_FEEDBACK_STEP_MASK);
+}
+static void rs780_program_at(struct radeon_device *rdev) +{
struct igp_power_info *pi = rs780_get_pi(rdev);
WREG32(FVTHROT_TARGET_REG, 30000000 / pi->refresh_rate);
WREG32(FVTHROT_CB1, 1000000 * 5 / pi->refresh_rate);
WREG32(FVTHROT_CB2, 1000000 * 10 / pi->refresh_rate);
WREG32(FVTHROT_CB3, 1000000 * 30 / pi->refresh_rate);
WREG32(FVTHROT_CB4, 1000000 * 50 / pi->refresh_rate);
+}
+static void rs780_disable_vbios_powersaving(struct radeon_device *rdev) +{
WREG32_P(CG_INTGFX_MISC, 0, ~0xFFF00000);
+}
+static void rs780_force_voltage_to_high(struct radeon_device *rdev) +{
struct igp_power_info *pi = rs780_get_pi(rdev);
struct igp_ps *current_state = rs780_get_ps(rdev->pm.dpm.current_ps);
if ((current_state->max_voltage == RS780_VDDC_LEVEL_HIGH) &&
(current_state->min_voltage == RS780_VDDC_LEVEL_HIGH))
return;
WREG32_P(GFX_MACRO_BYPASS_CNTL, SPLL_BYPASS_CNTL, ~SPLL_BYPASS_CNTL);
udelay(1);
WREG32_P(FVTHROT_PWM_CTRL_REG0,
STARTING_PWM_HIGHTIME(pi->max_voltage),
~STARTING_PWM_HIGHTIME_MASK);
WREG32_P(FVTHROT_PWM_CTRL_REG0,
FORCE_STARTING_PWM_HIGHTIME, ~FORCE_STARTING_PWM_HIGHTIME);
WREG32_P(FVTHROT_PWM_FEEDBACK_DIV_REG1, 0,
~RANGE_PWM_FEEDBACK_DIV_EN);
udelay(1);
WREG32_P(GFX_MACRO_BYPASS_CNTL, 0, ~SPLL_BYPASS_CNTL);
+}
+static int rs780_set_engine_clock_scaling(struct radeon_device *rdev) +{
struct atom_clock_dividers min_dividers, max_dividers, current_max_dividers;
struct igp_ps *new_state = rs780_get_ps(rdev->pm.dpm.requested_ps);
struct igp_ps *old_state = rs780_get_ps(rdev->pm.dpm.current_ps);
int ret;
if ((new_state->sclk_high == old_state->sclk_high) &&
(new_state->sclk_low == old_state->sclk_low))
return 0;
ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
new_state->sclk_low, false, &min_dividers);
if (ret)
return ret;
ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
new_state->sclk_high, false, &max_dividers);
if (ret)
return ret;
ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
old_state->sclk_high, false, ¤t_max_dividers);
if (ret)
return ret;
WREG32_P(GFX_MACRO_BYPASS_CNTL, SPLL_BYPASS_CNTL, ~SPLL_BYPASS_CNTL);
WREG32_P(FVTHROT_FBDIV_REG2, FORCED_FEEDBACK_DIV(max_dividers.fb_div),
~FORCED_FEEDBACK_DIV_MASK);
WREG32_P(FVTHROT_FBDIV_REG1, STARTING_FEEDBACK_DIV(max_dividers.fb_div),
~STARTING_FEEDBACK_DIV_MASK);
WREG32_P(FVTHROT_FBDIV_REG1, FORCE_FEEDBACK_DIV, ~FORCE_FEEDBACK_DIV);
udelay(100);
WREG32_P(GFX_MACRO_BYPASS_CNTL, 0, ~SPLL_BYPASS_CNTL);
if (max_dividers.fb_div > min_dividers.fb_div) {
WREG32_P(FVTHROT_FBDIV_REG0,
MIN_FEEDBACK_DIV(min_dividers.fb_div) |
MAX_FEEDBACK_DIV(max_dividers.fb_div),
~(MIN_FEEDBACK_DIV_MASK | MAX_FEEDBACK_DIV_MASK));
WREG32_P(FVTHROT_FBDIV_REG1, 0, ~FORCE_FEEDBACK_DIV);
}
return 0;
+}
+static void rs780_set_engine_clock_spc(struct radeon_device *rdev) +{
struct igp_ps *new_state = rs780_get_ps(rdev->pm.dpm.requested_ps);
struct igp_ps *old_state = rs780_get_ps(rdev->pm.dpm.current_ps);
struct igp_power_info *pi = rs780_get_pi(rdev);
if ((new_state->sclk_high == old_state->sclk_high) &&
(new_state->sclk_low == old_state->sclk_low))
return;
if (pi->crtc_id == 0)
WREG32_P(CG_INTGFX_MISC, 0, ~FVTHROT_VBLANK_SEL);
else
WREG32_P(CG_INTGFX_MISC, FVTHROT_VBLANK_SEL, ~FVTHROT_VBLANK_SEL);
+}
+static void rs780_activate_engine_clk_scaling(struct radeon_device *rdev) +{
struct igp_ps *new_state = rs780_get_ps(rdev->pm.dpm.requested_ps);
struct igp_ps *old_state = rs780_get_ps(rdev->pm.dpm.current_ps);
if ((new_state->sclk_high == old_state->sclk_high) &&
(new_state->sclk_low == old_state->sclk_low))
return;
rs780_clk_scaling_enable(rdev, true);
+}
+static u32 rs780_get_voltage_for_vddc_level(struct radeon_device *rdev,
enum rs780_vddc_level vddc)
+{
struct igp_power_info *pi = rs780_get_pi(rdev);
if (vddc == RS780_VDDC_LEVEL_HIGH)
return pi->max_voltage;
else if (vddc == RS780_VDDC_LEVEL_LOW)
return pi->min_voltage;
else
return pi->max_voltage;
+}
+static void rs780_enable_voltage_scaling(struct radeon_device *rdev) +{
struct igp_ps *new_state = rs780_get_ps(rdev->pm.dpm.requested_ps);
struct igp_power_info *pi = rs780_get_pi(rdev);
enum rs780_vddc_level vddc_high, vddc_low;
udelay(100);
if ((new_state->max_voltage == RS780_VDDC_LEVEL_HIGH) &&
(new_state->min_voltage == RS780_VDDC_LEVEL_HIGH))
return;
vddc_high = rs780_get_voltage_for_vddc_level(rdev,
new_state->max_voltage);
vddc_low = rs780_get_voltage_for_vddc_level(rdev,
new_state->min_voltage);
WREG32_P(GFX_MACRO_BYPASS_CNTL, SPLL_BYPASS_CNTL, ~SPLL_BYPASS_CNTL);
udelay(1);
if (vddc_high > vddc_low) {
WREG32_P(FVTHROT_PWM_FEEDBACK_DIV_REG1,
RANGE_PWM_FEEDBACK_DIV_EN, ~RANGE_PWM_FEEDBACK_DIV_EN);
WREG32_P(FVTHROT_PWM_CTRL_REG0, 0, ~FORCE_STARTING_PWM_HIGHTIME);
} else if (vddc_high == vddc_low) {
if (pi->max_voltage != vddc_high) {
WREG32_P(FVTHROT_PWM_CTRL_REG0,
STARTING_PWM_HIGHTIME(vddc_high),
~STARTING_PWM_HIGHTIME_MASK);
WREG32_P(FVTHROT_PWM_CTRL_REG0,
FORCE_STARTING_PWM_HIGHTIME,
~FORCE_STARTING_PWM_HIGHTIME);
}
}
WREG32_P(GFX_MACRO_BYPASS_CNTL, 0, ~SPLL_BYPASS_CNTL);
+}
+int rs780_dpm_enable(struct radeon_device *rdev) +{
struct igp_power_info *pi = rs780_get_pi(rdev);
rs780_get_pm_mode_parameters(rdev);
rs780_disable_vbios_powersaving(rdev);
if (r600_dynamicpm_enabled(rdev))
return -EINVAL;
if (rs780_initialize_dpm_parameters(rdev))
return -EINVAL;
rs780_start_dpm(rdev);
rs780_preset_ranges_slow_clk_fbdiv_en(rdev);
rs780_preset_starting_fbdiv(rdev);
if (pi->voltage_control)
rs780_voltage_scaling_init(rdev);
rs780_clk_scaling_enable(rdev, true);
rs780_set_engine_clock_sc(rdev);
rs780_set_engine_clock_wfc(rdev);
rs780_program_at(rdev);
rs780_set_engine_clock_tdc(rdev);
rs780_set_engine_clock_ssc(rdev);
if (pi->gfx_clock_gating)
r600_gfx_clockgating_enable(rdev, true);
return 0;
+}
+void rs780_dpm_disable(struct radeon_device *rdev) +{
struct igp_power_info *pi = rs780_get_pi(rdev);
r600_dynamicpm_enable(rdev, false);
rs780_clk_scaling_enable(rdev, false);
rs780_voltage_scaling_enable(rdev, false);
if (pi->gfx_clock_gating)
r600_gfx_clockgating_enable(rdev, false);
+}
+int rs780_dpm_set_power_state(struct radeon_device *rdev) +{
struct igp_power_info *pi = rs780_get_pi(rdev);
rs780_get_pm_mode_parameters(rdev);
if (pi->voltage_control) {
rs780_force_voltage_to_high(rdev);
mdelay(5);
}
rs780_set_engine_clock_scaling(rdev);
rs780_set_engine_clock_spc(rdev);
rs780_activate_engine_clk_scaling(rdev);
if (pi->voltage_control)
rs780_enable_voltage_scaling(rdev);
return 0;
+}
+void rs780_dpm_setup_asic(struct radeon_device *rdev) +{
+}
+void rs780_dpm_display_configuration_changed(struct radeon_device *rdev) +{
rs780_get_pm_mode_parameters(rdev);
rs780_program_at(rdev);
+}
+union igp_info {
struct _ATOM_INTEGRATED_SYSTEM_INFO info;
struct _ATOM_INTEGRATED_SYSTEM_INFO_V2 info_2;
+};
+union power_info {
struct _ATOM_POWERPLAY_INFO info;
struct _ATOM_POWERPLAY_INFO_V2 info_2;
struct _ATOM_POWERPLAY_INFO_V3 info_3;
struct _ATOM_PPLIB_POWERPLAYTABLE pplib;
struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2;
struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3;
+};
+union pplib_clock_info {
struct _ATOM_PPLIB_R600_CLOCK_INFO r600;
struct _ATOM_PPLIB_RS780_CLOCK_INFO rs780;
struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO evergreen;
struct _ATOM_PPLIB_SUMO_CLOCK_INFO sumo;
+};
+union pplib_power_state {
struct _ATOM_PPLIB_STATE v1;
struct _ATOM_PPLIB_STATE_V2 v2;
+};
+static void rs780_parse_pplib_non_clock_info(struct radeon_device *rdev,
struct radeon_ps *rps,
struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info,
u8 table_rev)
+{
rps->caps = le32_to_cpu(non_clock_info->ulCapsAndSettings);
rps->class = le16_to_cpu(non_clock_info->usClassification);
rps->class2 = le16_to_cpu(non_clock_info->usClassification2);
if (ATOM_PPLIB_NONCLOCKINFO_VER1 < table_rev) {
rps->vclk = le32_to_cpu(non_clock_info->ulVCLK);
rps->dclk = le32_to_cpu(non_clock_info->ulDCLK);
} else if (r600_is_uvd_state(rps->class, rps->class2)) {
rps->vclk = RS780_DEFAULT_VCLK_FREQ;
rps->dclk = RS780_DEFAULT_DCLK_FREQ;
} else {
rps->vclk = 0;
rps->dclk = 0;
}
if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT)
rdev->pm.dpm.boot_ps = rps;
if (rps->class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE)
rdev->pm.dpm.uvd_ps = rps;
+}
+static void rs780_parse_pplib_clock_info(struct radeon_device *rdev,
struct radeon_ps *rps,
union pplib_clock_info *clock_info)
+{
struct igp_ps *ps = rs780_get_ps(rps);
u32 sclk;
sclk = le16_to_cpu(clock_info->rs780.usLowEngineClockLow);
sclk |= clock_info->rs780.ucLowEngineClockHigh << 16;
ps->sclk_low = sclk;
sclk = le16_to_cpu(clock_info->rs780.usHighEngineClockLow);
sclk |= clock_info->rs780.ucHighEngineClockHigh << 16;
ps->sclk_high = sclk;
switch (le16_to_cpu(clock_info->rs780.usVDDC)) {
case ATOM_PPLIB_RS780_VOLTAGE_NONE:
default:
ps->min_voltage = RS780_VDDC_LEVEL_UNKNOWN;
ps->max_voltage = RS780_VDDC_LEVEL_UNKNOWN;
break;
case ATOM_PPLIB_RS780_VOLTAGE_LOW:
ps->min_voltage = RS780_VDDC_LEVEL_LOW;
ps->max_voltage = RS780_VDDC_LEVEL_LOW;
break;
case ATOM_PPLIB_RS780_VOLTAGE_HIGH:
ps->min_voltage = RS780_VDDC_LEVEL_HIGH;
ps->max_voltage = RS780_VDDC_LEVEL_HIGH;
break;
case ATOM_PPLIB_RS780_VOLTAGE_VARIABLE:
ps->min_voltage = RS780_VDDC_LEVEL_LOW;
ps->max_voltage = RS780_VDDC_LEVEL_HIGH;
break;
}
ps->flags = le32_to_cpu(clock_info->rs780.ulFlags);
if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT) {
ps->sclk_low = rdev->clock.default_sclk;
ps->sclk_high = rdev->clock.default_sclk;
ps->min_voltage = RS780_VDDC_LEVEL_HIGH;
ps->max_voltage = RS780_VDDC_LEVEL_HIGH;
}
+}
+static int rs780_parse_power_table(struct radeon_device *rdev) +{
struct radeon_mode_info *mode_info = &rdev->mode_info;
struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info;
union pplib_power_state *power_state;
int i;
union pplib_clock_info *clock_info;
union power_info *power_info;
int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo);
u16 data_offset;
u8 frev, crev;
struct igp_ps *ps;
if (!atom_parse_data_header(mode_info->atom_context, index, NULL,
&frev, &crev, &data_offset))
return -EINVAL;
power_info = (union power_info *)(mode_info->atom_context->bios + data_offset);
rdev->pm.dpm.ps = kzalloc(sizeof(struct radeon_ps) *
power_info->pplib.ucNumStates, GFP_KERNEL);
if (!rdev->pm.dpm.ps)
return -ENOMEM;
rdev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps);
rdev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime);
rdev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime);
for (i = 0; i < power_info->pplib.ucNumStates; i++) {
power_state = (union pplib_power_state *)
(mode_info->atom_context->bios + data_offset +
le16_to_cpu(power_info->pplib.usStateArrayOffset) +
i * power_info->pplib.ucStateEntrySize);
non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *)
(mode_info->atom_context->bios + data_offset +
le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset) +
(power_state->v1.ucNonClockStateIndex *
power_info->pplib.ucNonClockSize));
if (power_info->pplib.ucStateEntrySize - 1) {
clock_info = (union pplib_clock_info *)
(mode_info->atom_context->bios + data_offset +
le16_to_cpu(power_info->pplib.usClockInfoArrayOffset) +
(power_state->v1.ucClockStateIndices[0] *
power_info->pplib.ucClockInfoSize));
ps = kzalloc(sizeof(struct igp_ps), GFP_KERNEL);
if (ps == NULL) {
kfree(rdev->pm.dpm.ps);
return -ENOMEM;
}
rdev->pm.dpm.ps[i].ps_priv = ps;
rs780_parse_pplib_non_clock_info(rdev, &rdev->pm.dpm.ps[i],
non_clock_info,
power_info->pplib.ucNonClockSize);
rs780_parse_pplib_clock_info(rdev,
&rdev->pm.dpm.ps[i],
clock_info);
}
}
rdev->pm.dpm.num_ps = power_info->pplib.ucNumStates;
return 0;
+}
+int rs780_dpm_init(struct radeon_device *rdev) +{
struct igp_power_info *pi;
int index = GetIndexIntoMasterTable(DATA, IntegratedSystemInfo);
union igp_info *info;
u16 data_offset;
u8 frev, crev;
int ret;
pi = kzalloc(sizeof(struct igp_power_info), GFP_KERNEL);
if (pi == NULL)
return -ENOMEM;
rdev->pm.dpm.priv = pi;
ret = rs780_parse_power_table(rdev);
if (ret)
return ret;
pi->voltage_control = false;
pi->gfx_clock_gating = true;
if (atom_parse_data_header(rdev->mode_info.atom_context, index, NULL,
&frev, &crev, &data_offset)) {
info = (union igp_info *)(rdev->mode_info.atom_context->bios + data_offset);
/* Get various system informations from bios */
switch (crev) {
case 1:
pi->num_of_cycles_in_period =
info->info.ucNumberOfCyclesInPeriod;
pi->num_of_cycles_in_period |=
info->info.ucNumberOfCyclesInPeriodHi << 8;
pi->invert_pwm_required =
(pi->num_of_cycles_in_period & 0x8000) ? true : false;
pi->boot_voltage = info->info.ucStartingPWM_HighTime;
pi->max_voltage = info->info.ucMaxNBVoltage;
pi->max_voltage |= info->info.ucMaxNBVoltageHigh << 8;
pi->min_voltage = info->info.ucMinNBVoltage;
pi->min_voltage |= info->info.ucMinNBVoltageHigh << 8;
pi->inter_voltage_low =
le16_to_cpu(info->info.usInterNBVoltageLow);
pi->inter_voltage_high =
le16_to_cpu(info->info.usInterNBVoltageHigh);
pi->voltage_control = true;
pi->bootup_uma_clk = info->info.usK8MemoryClock * 100;
break;
case 2:
pi->num_of_cycles_in_period =
le16_to_cpu(info->info_2.usNumberOfCyclesInPeriod);
pi->invert_pwm_required =
(pi->num_of_cycles_in_period & 0x8000) ? true : false;
pi->boot_voltage =
le16_to_cpu(info->info_2.usBootUpNBVoltage);
pi->max_voltage =
le16_to_cpu(info->info_2.usMaxNBVoltage);
pi->min_voltage =
le16_to_cpu(info->info_2.usMinNBVoltage);
pi->system_config =
le32_to_cpu(info->info_2.ulSystemConfig);
pi->pwm_voltage_control =
(pi->system_config & 0x4) ? true : false;
pi->voltage_control = true;
pi->bootup_uma_clk = le32_to_cpu(info->info_2.ulBootUpUMAClock);
break;
default:
DRM_ERROR("No integrated system info for your GPU\n");
return -EINVAL;
}
if (pi->min_voltage > pi->max_voltage)
pi->voltage_control = false;
if (pi->pwm_voltage_control) {
if ((pi->num_of_cycles_in_period == 0) ||
(pi->max_voltage == 0) ||
(pi->min_voltage == 0))
pi->voltage_control = false;
} else {
if ((pi->num_of_cycles_in_period == 0) ||
(pi->max_voltage == 0))
pi->voltage_control = false;
}
return 0;
}
radeon_dpm_fini(rdev);
return -EINVAL;
+}
+void rs780_dpm_print_power_state(struct radeon_device *rdev,
struct radeon_ps *rps)
+{
struct igp_ps *ps = rs780_get_ps(rps);
r600_dpm_print_class_info(rps->class, rps->class2);
r600_dpm_print_cap_info(rps->caps);
printk("\tuvd vclk: %d dclk: %d\n", rps->vclk, rps->dclk);
printk("\t\tpower level 0 sclk: %u vddc_index: %d\n",
ps->sclk_low, ps->min_voltage);
printk("\t\tpower level 1 sclk: %u vddc_index: %d\n",
ps->sclk_high, ps->max_voltage);
r600_dpm_print_ps_status(rdev, rps);
+}
+void rs780_dpm_fini(struct radeon_device *rdev) +{
int i;
for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
kfree(rdev->pm.dpm.ps[i].ps_priv);
}
kfree(rdev->pm.dpm.ps);
kfree(rdev->pm.dpm.priv);
+}
+u32 rs780_dpm_get_sclk(struct radeon_device *rdev, bool low) +{
struct igp_ps *requested_state = rs780_get_ps(rdev->pm.dpm.requested_ps);
if (low)
return requested_state->sclk_low;
else
return requested_state->sclk_high;
+}
+u32 rs780_dpm_get_mclk(struct radeon_device *rdev, bool low) +{
struct igp_power_info *pi = rs780_get_pi(rdev);
return pi->bootup_uma_clk;
+} diff --git a/drivers/gpu/drm/radeon/rs780_dpm.h b/drivers/gpu/drm/radeon/rs780_dpm.h new file mode 100644 index 0000000..47a40b1 --- /dev/null +++ b/drivers/gpu/drm/radeon/rs780_dpm.h @@ -0,0 +1,109 @@ +/*
- Copyright 2011 Advanced Micro Devices, Inc.
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- OTHER DEALINGS IN THE SOFTWARE.
- */
+#ifndef __RS780_DPM_H__ +#define __RS780_DPM_H__
+enum rs780_vddc_level {
RS780_VDDC_LEVEL_UNKNOWN = 0,
RS780_VDDC_LEVEL_LOW = 1,
RS780_VDDC_LEVEL_HIGH = 2,
+};
+struct igp_power_info {
/* flags */
bool invert_pwm_required;
bool pwm_voltage_control;
bool voltage_control;
bool gfx_clock_gating;
/* stored values */
u32 system_config;
u32 bootup_uma_clk;
u16 max_voltage;
u16 min_voltage;
u16 boot_voltage;
u16 inter_voltage_low;
u16 inter_voltage_high;
u16 num_of_cycles_in_period;
/* variable */
int crtc_id;
int refresh_rate;
+};
+struct igp_ps {
enum rs780_vddc_level min_voltage;
enum rs780_vddc_level max_voltage;
u32 sclk_low;
u32 sclk_high;
u32 flags;
+};
+#define RS780_CGFTV_DFLT 0x0303000f +#define RS780_FBDIVTIMERVAL_DFLT 0x2710
+#define RS780_FVTHROTUTC0_DFLT 0x04010040 +#define RS780_FVTHROTUTC1_DFLT 0x04010040 +#define RS780_FVTHROTUTC2_DFLT 0x04010040 +#define RS780_FVTHROTUTC3_DFLT 0x04010040 +#define RS780_FVTHROTUTC4_DFLT 0x04010040
+#define RS780_FVTHROTDTC0_DFLT 0x04010040 +#define RS780_FVTHROTDTC1_DFLT 0x04010040 +#define RS780_FVTHROTDTC2_DFLT 0x04010040 +#define RS780_FVTHROTDTC3_DFLT 0x04010040 +#define RS780_FVTHROTDTC4_DFLT 0x04010040
+#define RS780_FVTHROTFBUSREG0_DFLT 0x00001001 +#define RS780_FVTHROTFBUSREG1_DFLT 0x00002002 +#define RS780_FVTHROTFBDSREG0_DFLT 0x00004001 +#define RS780_FVTHROTFBDSREG1_DFLT 0x00020010
+#define RS780_FVTHROTPWMUSREG0_DFLT 0x00002001 +#define RS780_FVTHROTPWMUSREG1_DFLT 0x00004003 +#define RS780_FVTHROTPWMDSREG0_DFLT 0x00002001 +#define RS780_FVTHROTPWMDSREG1_DFLT 0x00004003
+#define RS780_FVTHROTPWMFBDIVRANGEREG0_DFLT 0x37 +#define RS780_FVTHROTPWMFBDIVRANGEREG1_DFLT 0x4b +#define RS780_FVTHROTPWMFBDIVRANGEREG2_DFLT 0x8b
+#define RS780D_FVTHROTPWMFBDIVRANGEREG0_DFLT 0x8b +#define RS780D_FVTHROTPWMFBDIVRANGEREG1_DFLT 0x8c +#define RS780D_FVTHROTPWMFBDIVRANGEREG2_DFLT 0xb5
+#define RS880D_FVTHROTPWMFBDIVRANGEREG0_DFLT 0x8d +#define RS880D_FVTHROTPWMFBDIVRANGEREG1_DFLT 0x8e +#define RS880D_FVTHROTPWMFBDIVRANGEREG2_DFLT 0xBa
+#define RS780_FVTHROTPWMRANGE0_GPIO_DFLT 0x1a +#define RS780_FVTHROTPWMRANGE1_GPIO_DFLT 0x1a +#define RS780_FVTHROTPWMRANGE2_GPIO_DFLT 0x0 +#define RS780_FVTHROTPWMRANGE3_GPIO_DFLT 0x0
+#define RS780_SLOWCLKFEEDBACKDIV_DFLT 110
+#define RS780_CGCLKGATING_DFLT 0x0000E204
+#define RS780_DEFAULT_VCLK_FREQ 53300 /* 10 khz */ +#define RS780_DEFAULT_DCLK_FREQ 40000 /* 10 khz */
+#endif diff --git a/drivers/gpu/drm/radeon/rs780d.h b/drivers/gpu/drm/radeon/rs780d.h new file mode 100644 index 0000000..b1142ed --- /dev/null +++ b/drivers/gpu/drm/radeon/rs780d.h @@ -0,0 +1,168 @@ +/*
- Copyright 2011 Advanced Micro Devices, Inc.
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- OTHER DEALINGS IN THE SOFTWARE.
- */
+#ifndef __RS780D_H__ +#define __RS780D_H__
+#define CG_SPLL_FUNC_CNTL 0x600 +# define SPLL_RESET (1 << 0) +# define SPLL_SLEEP (1 << 1) +# define SPLL_REF_DIV(x) ((x) << 2) +# define SPLL_REF_DIV_MASK (7 << 2) +# define SPLL_FB_DIV(x) ((x) << 5) +# define SPLL_FB_DIV_MASK (0xff << 2) +# define SPLL_FB_DIV_SHIFT 2 +# define SPLL_PULSEEN (1 << 13) +# define SPLL_PULSENUM(x) ((x) << 14) +# define SPLL_PULSENUM_MASK (3 << 14) +# define SPLL_SW_HILEN(x) ((x) << 16) +# define SPLL_SW_HILEN_MASK (0xf << 16) +# define SPLL_SW_LOLEN(x) ((x) << 20) +# define SPLL_SW_LOLEN_MASK (0xf << 20) +# define SPLL_DIVEN (1 << 24) +# define SPLL_BYPASS_EN (1 << 25) +# define SPLL_CHG_STATUS (1 << 29) +# define SPLL_CTLREQ (1 << 30) +# define SPLL_CTLACK (1 << 31)
+/* RS780/RS880 PM */ +#define FVTHROT_CNTRL_REG 0x3000 +#define DONT_WAIT_FOR_FBDIV_WRAP (1 << 0) +#define MINIMUM_CIP(x) ((x) << 1) +#define MINIMUM_CIP_SHIFT 1 +#define MINIMUM_CIP_MASK 0x1fffffe +#define REFRESH_RATE_DIVISOR(x) ((x) << 25) +#define REFRESH_RATE_DIVISOR_SHIFT 25 +#define REFRESH_RATE_DIVISOR_MASK (0x3 << 25) +#define ENABLE_FV_THROT (1 << 27) +#define ENABLE_FV_UPDATE (1 << 28) +#define TREND_SEL_MODE (1 << 29) +#define FORCE_TREND_SEL (1 << 30) +#define ENABLE_FV_THROT_IO (1 << 31) +#define FVTHROT_TARGET_REG 0x3004 +#define TARGET_IDLE_COUNT(x) ((x) << 0) +#define TARGET_IDLE_COUNT_MASK 0xffffff +#define TARGET_IDLE_COUNT_SHIFT 0 +#define FVTHROT_CB1 0x3008 +#define FVTHROT_CB2 0x300c +#define FVTHROT_CB3 0x3010 +#define FVTHROT_CB4 0x3014 +#define FVTHROT_UTC0 0x3018 +#define FVTHROT_UTC1 0x301c +#define FVTHROT_UTC2 0x3020 +#define FVTHROT_UTC3 0x3024 +#define FVTHROT_UTC4 0x3028 +#define FVTHROT_DTC0 0x302c +#define FVTHROT_DTC1 0x3030 +#define FVTHROT_DTC2 0x3034 +#define FVTHROT_DTC3 0x3038 +#define FVTHROT_DTC4 0x303c +#define FVTHROT_FBDIV_REG0 0x3040 +#define MIN_FEEDBACK_DIV(x) ((x) << 0) +#define MIN_FEEDBACK_DIV_MASK 0xfff +#define MIN_FEEDBACK_DIV_SHIFT 0 +#define MAX_FEEDBACK_DIV(x) ((x) << 12) +#define MAX_FEEDBACK_DIV_MASK (0xfff << 12) +#define MAX_FEEDBACK_DIV_SHIFT 12 +#define FVTHROT_FBDIV_REG1 0x3044 +#define MAX_FEEDBACK_STEP(x) ((x) << 0) +#define MAX_FEEDBACK_STEP_MASK 0xfff +#define MAX_FEEDBACK_STEP_SHIFT 0 +#define STARTING_FEEDBACK_DIV(x) ((x) << 12) +#define STARTING_FEEDBACK_DIV_MASK (0xfff << 12) +#define STARTING_FEEDBACK_DIV_SHIFT 12 +#define FORCE_FEEDBACK_DIV (1 << 24) +#define FVTHROT_FBDIV_REG2 0x3048 +#define FORCED_FEEDBACK_DIV(x) ((x) << 0) +#define FORCED_FEEDBACK_DIV_MASK 0xfff +#define FORCED_FEEDBACK_DIV_SHIFT 0 +#define FB_DIV_TIMER_VAL(x) ((x) << 12) +#define FB_DIV_TIMER_VAL_MASK (0xffff << 12) +#define FB_DIV_TIMER_VAL_SHIFT 12 +#define FVTHROT_FB_US_REG0 0x304c +#define FVTHROT_FB_US_REG1 0x3050 +#define FVTHROT_FB_DS_REG0 0x3054 +#define FVTHROT_FB_DS_REG1 0x3058 +#define FVTHROT_PWM_CTRL_REG0 0x305c +#define STARTING_PWM_HIGHTIME(x) ((x) << 0) +#define STARTING_PWM_HIGHTIME_MASK 0xfff +#define STARTING_PWM_HIGHTIME_SHIFT 0 +#define NUMBER_OF_CYCLES_IN_PERIOD(x) ((x) << 12) +#define NUMBER_OF_CYCLES_IN_PERIOD_MASK (0xfff << 12) +#define NUMBER_OF_CYCLES_IN_PERIOD_SHIFT 12 +#define FORCE_STARTING_PWM_HIGHTIME (1 << 24) +#define INVERT_PWM_WAVEFORM (1 << 25) +#define FVTHROT_PWM_CTRL_REG1 0x3060 +#define MIN_PWM_HIGHTIME(x) ((x) << 0) +#define MIN_PWM_HIGHTIME_MASK 0xfff +#define MIN_PWM_HIGHTIME_SHIFT 0 +#define MAX_PWM_HIGHTIME(x) ((x) << 12) +#define MAX_PWM_HIGHTIME_MASK (0xfff << 12) +#define MAX_PWM_HIGHTIME_SHIFT 12 +#define FVTHROT_PWM_US_REG0 0x3064 +#define FVTHROT_PWM_US_REG1 0x3068 +#define FVTHROT_PWM_DS_REG0 0x306c +#define FVTHROT_PWM_DS_REG1 0x3070 +#define FVTHROT_STATUS_REG0 0x3074 +#define CURRENT_FEEDBACK_DIV_MASK 0xfff +#define CURRENT_FEEDBACK_DIV_SHIFT 0 +#define FVTHROT_STATUS_REG1 0x3078 +#define FVTHROT_STATUS_REG2 0x307c +#define CG_INTGFX_MISC 0x3080 +#define FVTHROT_VBLANK_SEL (1 << 9) +#define FVTHROT_PWM_FEEDBACK_DIV_REG1 0x308c +#define RANGE0_PWM_FEEDBACK_DIV(x) ((x) << 0) +#define RANGE0_PWM_FEEDBACK_DIV_MASK 0xfff +#define RANGE0_PWM_FEEDBACK_DIV_SHIFT 0 +#define RANGE_PWM_FEEDBACK_DIV_EN (1 << 12) +#define FVTHROT_PWM_FEEDBACK_DIV_REG2 0x3090 +#define RANGE1_PWM_FEEDBACK_DIV(x) ((x) << 0) +#define RANGE1_PWM_FEEDBACK_DIV_MASK 0xfff +#define RANGE1_PWM_FEEDBACK_DIV_SHIFT 0 +#define RANGE2_PWM_FEEDBACK_DIV(x) ((x) << 12) +#define RANGE2_PWM_FEEDBACK_DIV_MASK (0xfff << 12) +#define RANGE2_PWM_FEEDBACK_DIV_SHIFT 12 +#define FVTHROT_PWM_FEEDBACK_DIV_REG3 0x3094 +#define RANGE0_PWM(x) ((x) << 0) +#define RANGE0_PWM_MASK 0xfff +#define RANGE0_PWM_SHIFT 0 +#define RANGE1_PWM(x) ((x) << 12) +#define RANGE1_PWM_MASK (0xfff << 12) +#define RANGE1_PWM_SHIFT 12 +#define FVTHROT_PWM_FEEDBACK_DIV_REG4 0x3098 +#define RANGE2_PWM(x) ((x) << 0) +#define RANGE2_PWM_MASK 0xfff +#define RANGE2_PWM_SHIFT 0 +#define RANGE3_PWM(x) ((x) << 12) +#define RANGE3_PWM_MASK (0xfff << 12) +#define RANGE3_PWM_SHIFT 12 +#define FVTHROT_SLOW_CLK_FEEDBACK_DIV_REG1 0x30ac +#define RANGE0_SLOW_CLK_FEEDBACK_DIV(x) ((x) << 0) +#define RANGE0_SLOW_CLK_FEEDBACK_DIV_MASK 0xfff +#define RANGE0_SLOW_CLK_FEEDBACK_DIV_SHIFT 0 +#define RANGE_SLOW_CLK_FEEDBACK_DIV_EN (1 << 12)
+#define GFX_MACRO_BYPASS_CNTL 0x30c0 +#define SPLL_BYPASS_CNTL (1 << 0) +#define UPLL_BYPASS_CNTL (1 << 1)
+#endif
1.7.7.5
dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
From: Alex Deucher alexander.deucher@amd.com
This adds dpm support for rv6xx asics. This includes: - clockgating - dynamic engine clock scaling - dynamic memory clock scaling - dynamic voltage scaling - dynamic pcie gen1/gen2 switching
Set radeon.dpm=1 to enable.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/Makefile | 2 +- drivers/gpu/drm/radeon/r600.c | 27 + drivers/gpu/drm/radeon/r600_dpm.c | 45 + drivers/gpu/drm/radeon/r600_dpm.h | 8 + drivers/gpu/drm/radeon/r600d.h | 13 + drivers/gpu/drm/radeon/radeon.h | 3 + drivers/gpu/drm/radeon/radeon_asic.c | 12 + drivers/gpu/drm/radeon/radeon_asic.h | 12 + drivers/gpu/drm/radeon/radeon_atombios.c | 4 +- drivers/gpu/drm/radeon/radeon_irq_kms.c | 2 + drivers/gpu/drm/radeon/radeon_mode.h | 2 + drivers/gpu/drm/radeon/radeon_pm.c | 9 + drivers/gpu/drm/radeon/rs780_dpm.c | 12 + drivers/gpu/drm/radeon/rv6xx_dpm.c | 1991 ++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/rv6xx_dpm.h | 95 ++ drivers/gpu/drm/radeon/rv6xxd.h | 246 ++++ 16 files changed, 2480 insertions(+), 3 deletions(-) create mode 100644 drivers/gpu/drm/radeon/rv6xx_dpm.c create mode 100644 drivers/gpu/drm/radeon/rv6xx_dpm.h create mode 100644 drivers/gpu/drm/radeon/rv6xxd.h
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index e44b046..3aa20dc 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile @@ -77,7 +77,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \ evergreen_hdmi.o radeon_trace_points.o ni.o cayman_blit_shaders.o \ atombios_encoders.o radeon_semaphore.o radeon_sa.o atombios_i2c.o si.o \ si_blit_shaders.o radeon_prime.o radeon_uvd.o cik.o cik_blit_shaders.o \ - r600_dpm.o rs780_dpm.o + r600_dpm.o rs780_dpm.o rv6xx_dpm.o
radeon-$(CONFIG_COMPAT) += radeon_ioc32.o radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index 4678ed1..7ea81c8 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -3998,6 +3998,7 @@ int r600_irq_set(struct radeon_device *rdev) u32 hdmi0, hdmi1; u32 d1grph = 0, d2grph = 0; u32 dma_cntl; + u32 thermal_int = 0;
if (!rdev->irq.installed) { WARN(1, "Can't enable IRQ/MSI because no handler is installed\n"); @@ -4032,8 +4033,18 @@ int r600_irq_set(struct radeon_device *rdev) hdmi0 = RREG32(HDMI0_AUDIO_PACKET_CONTROL) & ~HDMI0_AZ_FORMAT_WTRIG_MASK; hdmi1 = RREG32(HDMI1_AUDIO_PACKET_CONTROL) & ~HDMI0_AZ_FORMAT_WTRIG_MASK; } + dma_cntl = RREG32(DMA_CNTL) & ~TRAP_ENABLE;
+ if ((rdev->family > CHIP_R600) && (rdev->family < CHIP_RV770)) { + thermal_int = RREG32(CG_THERMAL_INT) & + ~(THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW); + if (rdev->irq.dpm_thermal) { + DRM_DEBUG("dpm thermal\n"); + thermal_int |= THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW; + } + } + if (atomic_read(&rdev->irq.ring_int[RADEON_RING_TYPE_GFX_INDEX])) { DRM_DEBUG("r600_irq_set: sw int\n"); cp_int_cntl |= RB_INT_ENABLE; @@ -4115,6 +4126,9 @@ int r600_irq_set(struct radeon_device *rdev) WREG32(HDMI0_AUDIO_PACKET_CONTROL, hdmi0); WREG32(HDMI1_AUDIO_PACKET_CONTROL, hdmi1); } + if ((rdev->family > CHIP_R600) && (rdev->family < CHIP_RV770)) { + WREG32(CG_THERMAL_INT, thermal_int); + }
return 0; } @@ -4306,6 +4320,7 @@ int r600_irq_process(struct radeon_device *rdev) u32 ring_index; bool queue_hotplug = false; bool queue_hdmi = false; + bool queue_thermal = false;
if (!rdev->ih.enabled || rdev->shutdown) return IRQ_NONE; @@ -4473,6 +4488,16 @@ restart_ih: DRM_DEBUG("IH: DMA trap\n"); radeon_fence_process(rdev, R600_RING_TYPE_DMA_INDEX); break; + case 230: /* thermal low to high */ + DRM_DEBUG("IH: thermal low to high\n"); + rdev->pm.dpm.thermal.high_to_low = false; + queue_thermal = true; + break; + case 231: /* thermal high to low */ + DRM_DEBUG("IH: thermal high to low\n"); + rdev->pm.dpm.thermal.high_to_low = true; + queue_thermal = true; + break; case 233: /* GUI IDLE */ DRM_DEBUG("IH: GUI idle\n"); break; @@ -4489,6 +4514,8 @@ restart_ih: schedule_work(&rdev->hotplug_work); if (queue_hdmi) schedule_work(&rdev->audio_work); + if (queue_thermal) + schedule_work(&rdev->pm.dpm.thermal.work); rdev->ih.rptr = rptr; WREG32(IH_RB_RPTR, rdev->ih.rptr); atomic_set(&rdev->ih.lock, 0); diff --git a/drivers/gpu/drm/radeon/r600_dpm.c b/drivers/gpu/drm/radeon/r600_dpm.c index 91bc5ab..bf396a0 100644 --- a/drivers/gpu/drm/radeon/r600_dpm.c +++ b/drivers/gpu/drm/radeon/r600_dpm.c @@ -676,3 +676,48 @@ bool r600_is_uvd_state(u32 class, u32 class2) return true; return false; } + +int r600_set_thermal_temperature_range(struct radeon_device *rdev, + int min_temp, int max_temp) +{ + int low_temp = 0 * 1000; + int high_temp = 255 * 1000; + + if (low_temp < min_temp) + low_temp = min_temp; + if (high_temp > max_temp) + high_temp = max_temp; + if (high_temp < low_temp) { + DRM_ERROR("invalid thermal range: %d - %d\n", low_temp, high_temp); + return -EINVAL; + } + + WREG32_P(CG_THERMAL_INT, DIG_THERM_INTH(high_temp / 1000), ~DIG_THERM_INTH_MASK); + WREG32_P(CG_THERMAL_INT, DIG_THERM_INTL(low_temp / 1000), ~DIG_THERM_INTL_MASK); + WREG32_P(CG_THERMAL_CTRL, DIG_THERM_DPM(high_temp / 1000), ~DIG_THERM_DPM_MASK); + + rdev->pm.dpm.thermal.min_temp = low_temp; + rdev->pm.dpm.thermal.max_temp = high_temp; + + return 0; +} + +bool r600_is_internal_thermal_sensor(enum radeon_int_thermal_type sensor) +{ + switch (sensor) { + case THERMAL_TYPE_RV6XX: + case THERMAL_TYPE_RV770: + case THERMAL_TYPE_EVERGREEN: + case THERMAL_TYPE_SUMO: + case THERMAL_TYPE_NI: + return true; + case THERMAL_TYPE_ADT7473_WITH_INTERNAL: + case THERMAL_TYPE_EMC2103_WITH_INTERNAL: + return false; /* need special handling */ + case THERMAL_TYPE_NONE: + case THERMAL_TYPE_EXTERNAL: + case THERMAL_TYPE_EXTERNAL_GPIO: + default: + return false; + } +} diff --git a/drivers/gpu/drm/radeon/r600_dpm.h b/drivers/gpu/drm/radeon/r600_dpm.h index 240a7ed..bd33aa1 100644 --- a/drivers/gpu/drm/radeon/r600_dpm.h +++ b/drivers/gpu/drm/radeon/r600_dpm.h @@ -92,6 +92,10 @@ #define R600_PM_NUMBER_OF_VOLTAGE_LEVELS 4 #define R600_PM_NUMBER_OF_ACTIVITY_LEVELS 3
+/* XXX are these ok? */ +#define R600_TEMP_RANGE_MIN (90 * 1000) +#define R600_TEMP_RANGE_MAX (120 * 1000) + enum r600_power_level { R600_POWER_LEVEL_LOW = 0, R600_POWER_LEVEL_MEDIUM = 1, @@ -207,4 +211,8 @@ void r600_wait_for_power_level(struct radeon_device *rdev, void r600_start_dpm(struct radeon_device *rdev); void r600_stop_dpm(struct radeon_device *rdev);
+int r600_set_thermal_temperature_range(struct radeon_device *rdev, + int min_temp, int max_temp); +bool r600_is_internal_thermal_sensor(enum radeon_int_thermal_type sensor); + #endif diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h index d6d385a..3bca4db 100644 --- a/drivers/gpu/drm/radeon/r600d.h +++ b/drivers/gpu/drm/radeon/r600d.h @@ -302,10 +302,23 @@ #define GRBM_SOFT_RESET 0x8020 #define SOFT_RESET_CP (1<<0)
+#define CG_THERMAL_CTRL 0x7F0 +#define DIG_THERM_DPM(x) ((x) << 12) +#define DIG_THERM_DPM_MASK 0x000FF000 +#define DIG_THERM_DPM_SHIFT 12 #define CG_THERMAL_STATUS 0x7F4 #define ASIC_T(x) ((x) << 0) #define ASIC_T_MASK 0x1FF #define ASIC_T_SHIFT 0 +#define CG_THERMAL_INT 0x7F8 +#define DIG_THERM_INTH(x) ((x) << 8) +#define DIG_THERM_INTH_MASK 0x0000FF00 +#define DIG_THERM_INTH_SHIFT 8 +#define DIG_THERM_INTL(x) ((x) << 16) +#define DIG_THERM_INTL_MASK 0x00FF0000 +#define DIG_THERM_INTL_SHIFT 16 +#define THERM_INT_MASK_HIGH (1 << 24) +#define THERM_INT_MASK_LOW (1 << 25)
#define HDP_HOST_PATH_CNTL 0x2C00 #define HDP_NONSURFACE_BASE 0x2C04 diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 7adc7a1..ce36130 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -227,6 +227,8 @@ void radeon_atom_set_engine_dram_timings(struct radeon_device *rdev, u32 eng_clock, u32 mem_clock); int radeon_atom_get_voltage_step(struct radeon_device *rdev, u8 voltage_type, u16 *voltage_step); +int radeon_atom_get_max_vddc(struct radeon_device *rdev, u8 voltage_type, + u16 voltage_id, u16 *voltage); int radeon_atom_round_to_true_voltage(struct radeon_device *rdev, u8 voltage_type, u16 nominal_voltage, @@ -681,6 +683,7 @@ struct radeon_irq { bool hpd[RADEON_MAX_HPD_PINS]; bool afmt[RADEON_MAX_AFMT_BLOCKS]; union radeon_irq_stat_regs stat_regs; + bool dpm_thermal; };
int radeon_irq_kms_init(struct radeon_device *rdev); diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index db3c930..4705b02 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1105,6 +1105,18 @@ static struct radeon_asic rv6xx_asic = { .set_clock_gating = NULL, .get_temperature = &rv6xx_get_temp, }, + .dpm = { + .init = &rv6xx_dpm_init, + .setup_asic = &rv6xx_setup_asic, + .enable = &rv6xx_dpm_enable, + .disable = &rv6xx_dpm_disable, + .set_power_state = &rv6xx_dpm_set_power_state, + .display_configuration_changed = &rv6xx_dpm_display_configuration_changed, + .fini = &rv6xx_dpm_fini, + .get_sclk = &rv6xx_dpm_get_sclk, + .get_mclk = &rv6xx_dpm_get_mclk, + .print_power_state = &rv6xx_dpm_print_power_state, + }, .pflip = { .pre_page_flip = &rs600_pre_page_flip, .page_flip = &rs600_page_flip, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 134bf57..878766624 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -396,6 +396,18 @@ int r600_mc_wait_for_idle(struct radeon_device *rdev); u32 r600_get_xclk(struct radeon_device *rdev); uint64_t r600_get_gpu_clock_counter(struct radeon_device *rdev); int rv6xx_get_temp(struct radeon_device *rdev); +/* rv6xx dpm */ +int rv6xx_dpm_init(struct radeon_device *rdev); +int rv6xx_dpm_enable(struct radeon_device *rdev); +void rv6xx_dpm_disable(struct radeon_device *rdev); +int rv6xx_dpm_set_power_state(struct radeon_device *rdev); +void rv6xx_setup_asic(struct radeon_device *rdev); +void rv6xx_dpm_display_configuration_changed(struct radeon_device *rdev); +void rv6xx_dpm_fini(struct radeon_device *rdev); +u32 rv6xx_dpm_get_sclk(struct radeon_device *rdev, bool low); +u32 rv6xx_dpm_get_mclk(struct radeon_device *rdev, bool low); +void rv6xx_dpm_print_power_state(struct radeon_device *rdev, + struct radeon_ps *ps); /* rs780 dpm */ int rs780_dpm_init(struct radeon_device *rdev); int rs780_dpm_enable(struct radeon_device *rdev); diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index 90401fd..612d9bc 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -2268,8 +2268,8 @@ static void radeon_atombios_add_pplib_thermal_controller(struct radeon_device *r } }
-static void radeon_atombios_get_default_voltages(struct radeon_device *rdev, - u16 *vddc, u16 *vddci) +void radeon_atombios_get_default_voltages(struct radeon_device *rdev, + u16 *vddc, u16 *vddci) { struct radeon_mode_info *mode_info = &rdev->mode_info; int index = GetIndexIntoMasterTable(DATA, FirmwareInfo); diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c index dbffeca..bcdefd1 100644 --- a/drivers/gpu/drm/radeon/radeon_irq_kms.c +++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c @@ -116,6 +116,7 @@ void radeon_driver_irq_preinstall_kms(struct drm_device *dev) /* Disable *all* interrupts */ for (i = 0; i < RADEON_NUM_RINGS; i++) atomic_set(&rdev->irq.ring_int[i], 0); + rdev->irq.dpm_thermal = false; for (i = 0; i < RADEON_MAX_HPD_PINS; i++) rdev->irq.hpd[i] = false; for (i = 0; i < RADEON_MAX_CRTCS; i++) { @@ -163,6 +164,7 @@ void radeon_driver_irq_uninstall_kms(struct drm_device *dev) /* Disable *all* interrupts */ for (i = 0; i < RADEON_NUM_RINGS; i++) atomic_set(&rdev->irq.ring_int[i], 0); + rdev->irq.dpm_thermal = false; for (i = 0; i < RADEON_MAX_HPD_PINS; i++) rdev->irq.hpd[i] = false; for (i = 0; i < RADEON_MAX_CRTCS; i++) { diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index 5a1c69e..02bf4a7 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -580,6 +580,8 @@ extern enum radeon_tv_std radeon_combios_get_tv_info(struct radeon_device *rdev); extern enum radeon_tv_std radeon_atombios_get_tv_info(struct radeon_device *rdev); +extern void radeon_atombios_get_default_voltages(struct radeon_device *rdev, + u16 *vddc, u16 *vddci);
extern struct drm_connector * radeon_get_connector_for_encoder(struct drm_encoder *encoder); diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 853a8a2..5cb01f2 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -565,6 +565,9 @@ static void radeon_dpm_thermal_work_handler(struct work_struct *work) if (!rdev->pm.dpm_enabled) return;
+ if (!rdev->pm.dpm_enabled) + return; + if (rdev->asic->pm.get_temperature) { int temp = radeon_get_temperature(rdev);
@@ -1030,6 +1033,11 @@ int radeon_pm_init(struct radeon_device *rdev) { /* enable dpm on rv6xx+ */ switch (rdev->family) { + case CHIP_RV610: + case CHIP_RV630: + case CHIP_RV620: + case CHIP_RV635: + case CHIP_RV670: case CHIP_RS780: case CHIP_RS880: if (radeon_dpm == 1) @@ -1114,6 +1122,7 @@ static void radeon_pm_compute_clocks_old(struct radeon_device *rdev) if (rdev->pm.num_power_states < 2) return;
+ INIT_WORK(&rdev->pm.dpm.thermal.work, radeon_dpm_thermal_work_handler); mutex_lock(&rdev->pm.mutex);
rdev->pm.active_crtcs = 0; diff --git a/drivers/gpu/drm/radeon/rs780_dpm.c b/drivers/gpu/drm/radeon/rs780_dpm.c index f594900..a1497a6 100644 --- a/drivers/gpu/drm/radeon/rs780_dpm.c +++ b/drivers/gpu/drm/radeon/rs780_dpm.c @@ -560,6 +560,12 @@ int rs780_dpm_enable(struct radeon_device *rdev) if (pi->gfx_clock_gating) r600_gfx_clockgating_enable(rdev, true);
+ if (rdev->irq.installed && (rdev->pm.int_thermal_type == THERMAL_TYPE_RV6XX)) { + r600_set_thermal_temperature_range(rdev, R600_TEMP_RANGE_MIN, R600_TEMP_RANGE_MAX); + rdev->irq.dpm_thermal = true; + radeon_irq_set(rdev); + } + return 0; }
@@ -574,6 +580,12 @@ void rs780_dpm_disable(struct radeon_device *rdev)
if (pi->gfx_clock_gating) r600_gfx_clockgating_enable(rdev, false); + + if (rdev->irq.installed && + (rdev->pm.int_thermal_type == THERMAL_TYPE_RV6XX)) { + rdev->irq.dpm_thermal = false; + radeon_irq_set(rdev); + } }
int rs780_dpm_set_power_state(struct radeon_device *rdev) diff --git a/drivers/gpu/drm/radeon/rv6xx_dpm.c b/drivers/gpu/drm/radeon/rv6xx_dpm.c new file mode 100644 index 0000000..fa4beb2 --- /dev/null +++ b/drivers/gpu/drm/radeon/rv6xx_dpm.c @@ -0,0 +1,1991 @@ +/* + * Copyright 2011 Advanced Micro Devices, 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: Alex Deucher + */ + +#include "drmP.h" +#include "radeon.h" +#include "rv6xxd.h" +#include "r600_dpm.h" +#include "rv6xx_dpm.h" +#include "atom.h" + +static u32 rv6xx_scale_count_given_unit(struct radeon_device *rdev, + u32 unscaled_count, u32 unit); + +static struct rv6xx_ps *rv6xx_get_ps(struct radeon_ps *rps) +{ + struct rv6xx_ps *ps = rps->ps_priv; + + return ps; +} + +static struct rv6xx_power_info *rv6xx_get_pi(struct radeon_device *rdev) +{ + struct rv6xx_power_info *pi = rdev->pm.dpm.priv; + + return pi; +} + +static void rv6xx_force_pcie_gen1(struct radeon_device *rdev) +{ + u32 tmp; + int i; + + tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL); + tmp &= LC_GEN2_EN; + WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp); + + tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL); + tmp |= LC_INITIATE_LINK_SPEED_CHANGE; + WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp); + + for (i = 0; i < rdev->usec_timeout; i++) { + if (!(RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL) & LC_CURRENT_DATA_RATE)) + break; + udelay(1); + } + + tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL); + tmp &= ~LC_INITIATE_LINK_SPEED_CHANGE; + WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp); +} + +static void rv6xx_enable_pcie_gen2_support(struct radeon_device *rdev) +{ + u32 tmp; + + tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL); + + if ((tmp & LC_OTHER_SIDE_EVER_SENT_GEN2) && + (tmp & LC_OTHER_SIDE_SUPPORTS_GEN2)) { + tmp |= LC_GEN2_EN; + WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp); + } +} + +static void rv6xx_enable_bif_dynamic_pcie_gen2(struct radeon_device *rdev, + bool enable) +{ + u32 tmp; + + tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL) & ~LC_HW_VOLTAGE_IF_CONTROL_MASK; + if (enable) + tmp |= LC_HW_VOLTAGE_IF_CONTROL(1); + else + tmp |= LC_HW_VOLTAGE_IF_CONTROL(0); + WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp); +} + +static void rv6xx_enable_l0s(struct radeon_device *rdev) +{ + u32 tmp; + + tmp = RREG32_PCIE_PORT(PCIE_LC_CNTL) & ~LC_L0S_INACTIVITY_MASK; + tmp |= LC_L0S_INACTIVITY(3); + WREG32_PCIE_PORT(PCIE_LC_CNTL, tmp); +} + +static void rv6xx_enable_l1(struct radeon_device *rdev) +{ + u32 tmp; + + tmp = RREG32_PCIE_PORT(PCIE_LC_CNTL); + tmp &= ~LC_L1_INACTIVITY_MASK; + tmp |= LC_L1_INACTIVITY(4); + tmp &= ~LC_PMI_TO_L1_DIS; + tmp &= ~LC_ASPM_TO_L1_DIS; + WREG32_PCIE_PORT(PCIE_LC_CNTL, tmp); +} + +static void rv6xx_enable_pll_sleep_in_l1(struct radeon_device *rdev) +{ + u32 tmp; + + tmp = RREG32_PCIE_PORT(PCIE_LC_CNTL) & ~LC_L1_INACTIVITY_MASK; + tmp |= LC_L1_INACTIVITY(8); + WREG32_PCIE_PORT(PCIE_LC_CNTL, tmp); + + /* NOTE, this is a PCIE indirect reg, not PCIE PORT */ + tmp = RREG32_PCIE(PCIE_P_CNTL); + tmp |= P_PLL_PWRDN_IN_L1L23; + tmp &= ~P_PLL_BUF_PDNB; + tmp &= ~P_PLL_PDNB; + tmp |= P_ALLOW_PRX_FRONTEND_SHUTOFF; + WREG32_PCIE(PCIE_P_CNTL, tmp); +} + +static int rv6xx_convert_clock_to_stepping(struct radeon_device *rdev, + u32 clock, struct rv6xx_sclk_stepping *step) +{ + int ret; + struct atom_clock_dividers dividers; + + ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM, + clock, false, ÷rs); + if (ret) + return ret; + + if (dividers.enable_post_div) + step->post_divider = 2 + (dividers.post_div & 0xF) + (dividers.post_div >> 4); + else + step->post_divider = 1; + + step->vco_frequency = clock * step->post_divider; + + return 0; +} + +static void rv6xx_output_stepping(struct radeon_device *rdev, + u32 step_index, struct rv6xx_sclk_stepping *step) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + u32 ref_clk = rdev->clock.spll.reference_freq; + u32 fb_divider; + u32 spll_step_count = rv6xx_scale_count_given_unit(rdev, + R600_SPLLSTEPTIME_DFLT * + pi->spll_ref_div, + R600_SPLLSTEPUNIT_DFLT); + + r600_engine_clock_entry_enable(rdev, step_index, true); + r600_engine_clock_entry_enable_pulse_skipping(rdev, step_index, false); + + if (step->post_divider == 1) + r600_engine_clock_entry_enable_post_divider(rdev, step_index, false); + else { + u32 lo_len = (step->post_divider - 2) / 2; + u32 hi_len = step->post_divider - 2 - lo_len; + + r600_engine_clock_entry_enable_post_divider(rdev, step_index, true); + r600_engine_clock_entry_set_post_divider(rdev, step_index, (hi_len << 4) | lo_len); + } + + fb_divider = ((step->vco_frequency * pi->spll_ref_div) / ref_clk) >> + pi->fb_div_scale; + + r600_engine_clock_entry_set_reference_divider(rdev, step_index, + pi->spll_ref_div - 1); + r600_engine_clock_entry_set_feedback_divider(rdev, step_index, fb_divider); + r600_engine_clock_entry_set_step_time(rdev, step_index, spll_step_count); + +} + +static struct rv6xx_sclk_stepping rv6xx_next_vco_step(struct radeon_device *rdev, + struct rv6xx_sclk_stepping *cur, + bool increasing_vco, u32 step_size) +{ + struct rv6xx_sclk_stepping next; + + next.post_divider = cur->post_divider; + + if (increasing_vco) + next.vco_frequency = (cur->vco_frequency * (100 + step_size)) / 100; + else + next.vco_frequency = (cur->vco_frequency * 100 + 99 + step_size) / (100 + step_size); + + return next; +} + +static bool rv6xx_can_step_post_div(struct radeon_device *rdev, + struct rv6xx_sclk_stepping *cur, + struct rv6xx_sclk_stepping *target) +{ + return (cur->post_divider > target->post_divider) && + ((cur->vco_frequency * target->post_divider) <= + (target->vco_frequency * (cur->post_divider - 1))); +} + +static struct rv6xx_sclk_stepping rv6xx_next_post_div_step(struct radeon_device *rdev, + struct rv6xx_sclk_stepping *cur, + struct rv6xx_sclk_stepping *target) +{ + struct rv6xx_sclk_stepping next = *cur; + + while (rv6xx_can_step_post_div(rdev, &next, target)) + next.post_divider--; + + return next; +} + +static bool rv6xx_reached_stepping_target(struct radeon_device *rdev, + struct rv6xx_sclk_stepping *cur, + struct rv6xx_sclk_stepping *target, + bool increasing_vco) +{ + return (increasing_vco && (cur->vco_frequency >= target->vco_frequency)) || + (!increasing_vco && (cur->vco_frequency <= target->vco_frequency)); +} + +static void rv6xx_generate_steps(struct radeon_device *rdev, + u32 low, u32 high, + u32 start_index, u8 *end_index) +{ + struct rv6xx_sclk_stepping cur; + struct rv6xx_sclk_stepping target; + bool increasing_vco; + u32 step_index = start_index; + + rv6xx_convert_clock_to_stepping(rdev, low, &cur); + rv6xx_convert_clock_to_stepping(rdev, high, &target); + + rv6xx_output_stepping(rdev, step_index++, &cur); + + increasing_vco = (target.vco_frequency >= cur.vco_frequency); + + if (target.post_divider > cur.post_divider) + cur.post_divider = target.post_divider; + + while (1) { + struct rv6xx_sclk_stepping next; + + if (rv6xx_can_step_post_div(rdev, &cur, &target)) + next = rv6xx_next_post_div_step(rdev, &cur, &target); + else + next = rv6xx_next_vco_step(rdev, &cur, increasing_vco, R600_VCOSTEPPCT_DFLT); + + if (rv6xx_reached_stepping_target(rdev, &next, &target, increasing_vco)) { + struct rv6xx_sclk_stepping tiny = + rv6xx_next_vco_step(rdev, &target, !increasing_vco, R600_ENDINGVCOSTEPPCT_DFLT); + tiny.post_divider = next.post_divider; + + if (!rv6xx_reached_stepping_target(rdev, &tiny, &cur, !increasing_vco)) + rv6xx_output_stepping(rdev, step_index++, &tiny); + + if ((next.post_divider != target.post_divider) && + (next.vco_frequency != target.vco_frequency)) { + struct rv6xx_sclk_stepping final_vco; + + final_vco.vco_frequency = target.vco_frequency; + final_vco.post_divider = next.post_divider; + + rv6xx_output_stepping(rdev, step_index++, &final_vco); + } + + rv6xx_output_stepping(rdev, step_index++, &target); + break; + } else + rv6xx_output_stepping(rdev, step_index++, &next); + + cur = next; + } + + *end_index = (u8)step_index - 1; + +} + +static void rv6xx_generate_single_step(struct radeon_device *rdev, + u32 clock, u32 index) +{ + struct rv6xx_sclk_stepping step; + + rv6xx_convert_clock_to_stepping(rdev, clock, &step); + rv6xx_output_stepping(rdev, index, &step); +} + +static void rv6xx_invalidate_intermediate_steps_range(struct radeon_device *rdev, + u32 start_index, u32 end_index) +{ + u32 step_index; + + for (step_index = start_index + 1; step_index < end_index; step_index++) + r600_engine_clock_entry_enable(rdev, step_index, false); +} + +static void rv6xx_set_engine_spread_spectrum_clk_s(struct radeon_device *rdev, + u32 index, u32 clk_s) +{ + WREG32_P(CG_SPLL_SPREAD_SPECTRUM_LOW + (index * 4), + CLKS(clk_s), ~CLKS_MASK); +} + +static void rv6xx_set_engine_spread_spectrum_clk_v(struct radeon_device *rdev, + u32 index, u32 clk_v) +{ + WREG32_P(CG_SPLL_SPREAD_SPECTRUM_LOW + (index * 4), + CLKV(clk_v), ~CLKV_MASK); +} + +static void rv6xx_enable_engine_spread_spectrum(struct radeon_device *rdev, + u32 index, bool enable) +{ + if (enable) + WREG32_P(CG_SPLL_SPREAD_SPECTRUM_LOW + (index * 4), + SSEN, ~SSEN); + else + WREG32_P(CG_SPLL_SPREAD_SPECTRUM_LOW + (index * 4), + 0, ~SSEN); +} + +static void rv6xx_set_memory_spread_spectrum_clk_s(struct radeon_device *rdev, + u32 clk_s) +{ + WREG32_P(CG_MPLL_SPREAD_SPECTRUM, CLKS(clk_s), ~CLKS_MASK); +} + +static void rv6xx_set_memory_spread_spectrum_clk_v(struct radeon_device *rdev, + u32 clk_v) +{ + WREG32_P(CG_MPLL_SPREAD_SPECTRUM, CLKV(clk_v), ~CLKV_MASK); +} + +static void rv6xx_enable_memory_spread_spectrum(struct radeon_device *rdev, + bool enable) +{ + if (enable) + WREG32_P(CG_MPLL_SPREAD_SPECTRUM, SSEN, ~SSEN); + else + WREG32_P(CG_MPLL_SPREAD_SPECTRUM, 0, ~SSEN); +} + +static void rv6xx_enable_dynamic_spread_spectrum(struct radeon_device *rdev, + bool enable) +{ + if (enable) + WREG32_P(GENERAL_PWRMGT, DYN_SPREAD_SPECTRUM_EN, ~DYN_SPREAD_SPECTRUM_EN); + else + WREG32_P(GENERAL_PWRMGT, 0, ~DYN_SPREAD_SPECTRUM_EN); +} + +static void rv6xx_memory_clock_entry_enable_post_divider(struct radeon_device *rdev, + u32 index, bool enable) +{ + if (enable) + WREG32_P(MPLL_FREQ_LEVEL_0 + (index * 4), + LEVEL0_MPLL_DIV_EN, ~LEVEL0_MPLL_DIV_EN); + else + WREG32_P(MPLL_FREQ_LEVEL_0 + (index * 4), 0, ~LEVEL0_MPLL_DIV_EN); +} + +static void rv6xx_memory_clock_entry_set_post_divider(struct radeon_device *rdev, + u32 index, u32 divider) +{ + WREG32_P(MPLL_FREQ_LEVEL_0 + (index * 4), + LEVEL0_MPLL_POST_DIV(divider), ~LEVEL0_MPLL_POST_DIV_MASK); +} + +static void rv6xx_memory_clock_entry_set_feedback_divider(struct radeon_device *rdev, + u32 index, u32 divider) +{ + WREG32_P(MPLL_FREQ_LEVEL_0 + (index * 4), LEVEL0_MPLL_FB_DIV(divider), + ~LEVEL0_MPLL_FB_DIV_MASK); +} + +static void rv6xx_memory_clock_entry_set_reference_divider(struct radeon_device *rdev, + u32 index, u32 divider) +{ + WREG32_P(MPLL_FREQ_LEVEL_0 + (index * 4), + LEVEL0_MPLL_REF_DIV(divider), ~LEVEL0_MPLL_REF_DIV_MASK); +} + +static void rv6xx_vid_response_set_brt(struct radeon_device *rdev, u32 rt) +{ + WREG32_P(VID_RT, BRT(rt), ~BRT_MASK); +} + +static void rv6xx_enable_engine_feedback_and_reference_sync(struct radeon_device *rdev) +{ + WREG32_P(SPLL_CNTL_MODE, SPLL_DIV_SYNC, ~SPLL_DIV_SYNC); +} + +static u64 rv6xx_clocks_per_unit(u32 unit) +{ + u64 tmp = 1 << (2 * unit); + + return tmp; +} + +static u32 rv6xx_scale_count_given_unit(struct radeon_device *rdev, + u32 unscaled_count, u32 unit) +{ + u32 count_per_unit = (u32)rv6xx_clocks_per_unit(unit); + + return (unscaled_count + count_per_unit - 1) / count_per_unit; +} + +static u32 rv6xx_compute_count_for_delay(struct radeon_device *rdev, + u32 delay_us, u32 unit) +{ + u32 ref_clk = rdev->clock.spll.reference_freq; + + return rv6xx_scale_count_given_unit(rdev, delay_us * (ref_clk / 100), unit); +} + +static void rv6xx_calculate_engine_speed_stepping_parameters(struct radeon_device *rdev, + struct rv6xx_ps *state) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + + pi->hw.sclks[R600_POWER_LEVEL_LOW] = + state->low.sclk; + pi->hw.sclks[R600_POWER_LEVEL_MEDIUM] = + state->medium.sclk; + pi->hw.sclks[R600_POWER_LEVEL_HIGH] = + state->high.sclk; + + pi->hw.low_sclk_index = R600_POWER_LEVEL_LOW; + pi->hw.medium_sclk_index = R600_POWER_LEVEL_MEDIUM; + pi->hw.high_sclk_index = R600_POWER_LEVEL_HIGH; +} + +static void rv6xx_calculate_memory_clock_stepping_parameters(struct radeon_device *rdev, + struct rv6xx_ps *state) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + + pi->hw.mclks[R600_POWER_LEVEL_CTXSW] = + state->high.mclk; + pi->hw.mclks[R600_POWER_LEVEL_HIGH] = + state->high.mclk; + pi->hw.mclks[R600_POWER_LEVEL_MEDIUM] = + state->medium.mclk; + pi->hw.mclks[R600_POWER_LEVEL_LOW] = + state->low.mclk; + + pi->hw.high_mclk_index = R600_POWER_LEVEL_HIGH; + + if (state->high.mclk == state->medium.mclk) + pi->hw.medium_mclk_index = + pi->hw.high_mclk_index; + else + pi->hw.medium_mclk_index = R600_POWER_LEVEL_MEDIUM; + + + if (state->medium.mclk == state->low.mclk) + pi->hw.low_mclk_index = + pi->hw.medium_mclk_index; + else + pi->hw.low_mclk_index = R600_POWER_LEVEL_LOW; +} + +static void rv6xx_calculate_voltage_stepping_parameters(struct radeon_device *rdev, + struct rv6xx_ps *state) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + + pi->hw.vddc[R600_POWER_LEVEL_CTXSW] = state->high.vddc; + pi->hw.vddc[R600_POWER_LEVEL_HIGH] = state->high.vddc; + pi->hw.vddc[R600_POWER_LEVEL_MEDIUM] = state->medium.vddc; + pi->hw.vddc[R600_POWER_LEVEL_LOW] = state->low.vddc; + + pi->hw.backbias[R600_POWER_LEVEL_CTXSW] = + (state->high.flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE) ? true : false; + pi->hw.backbias[R600_POWER_LEVEL_HIGH] = + (state->high.flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE) ? true : false; + pi->hw.backbias[R600_POWER_LEVEL_MEDIUM] = + (state->medium.flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE) ? true : false; + pi->hw.backbias[R600_POWER_LEVEL_LOW] = + (state->low.flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE) ? true : false; + + pi->hw.pcie_gen2[R600_POWER_LEVEL_HIGH] = + (state->high.flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) ? true : false; + pi->hw.pcie_gen2[R600_POWER_LEVEL_MEDIUM] = + (state->medium.flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) ? true : false; + pi->hw.pcie_gen2[R600_POWER_LEVEL_LOW] = + (state->low.flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) ? true : false; + + pi->hw.high_vddc_index = R600_POWER_LEVEL_HIGH; + + if ((state->high.vddc == state->medium.vddc) && + ((state->high.flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE) == + (state->medium.flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE))) + pi->hw.medium_vddc_index = + pi->hw.high_vddc_index; + else + pi->hw.medium_vddc_index = R600_POWER_LEVEL_MEDIUM; + + if ((state->medium.vddc == state->low.vddc) && + ((state->medium.flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE) == + (state->low.flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE))) + pi->hw.low_vddc_index = + pi->hw.medium_vddc_index; + else + pi->hw.medium_vddc_index = R600_POWER_LEVEL_LOW; +} + +static inline u32 rv6xx_calculate_vco_frequency(u32 ref_clock, + struct atom_clock_dividers *dividers, + u32 fb_divider_scale) +{ + return ref_clock * ((dividers->fb_div & ~1) << fb_divider_scale) / + (dividers->ref_div + 1); +} + +static inline u32 rv6xx_calculate_spread_spectrum_clk_v(u32 vco_freq, u32 ref_freq, + u32 ss_rate, u32 ss_percent, + u32 fb_divider_scale) +{ + u32 fb_divider = vco_freq / ref_freq; + + return (ss_percent * ss_rate * 4 * (fb_divider * fb_divider) / + (5375 * ((vco_freq * 10) / (4096 >> fb_divider_scale)))); +} + +static inline u32 rv6xx_calculate_spread_spectrum_clk_s(u32 ss_rate, u32 ref_freq) +{ + return (((ref_freq * 10) / (ss_rate * 2)) - 1) / 4; +} + +static void rv6xx_program_engine_spread_spectrum(struct radeon_device *rdev, + u32 clock, enum r600_power_level level) +{ + u32 ref_clk = rdev->clock.spll.reference_freq; + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + struct atom_clock_dividers dividers; + struct radeon_atom_ss ss; + u32 vco_freq, clk_v, clk_s; + + rv6xx_enable_engine_spread_spectrum(rdev, level, false); + + if (clock && pi->sclk_ss) { + if (radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM, clock, false, ÷rs) == 0) { + vco_freq = rv6xx_calculate_vco_frequency(ref_clk, ÷rs, + pi->fb_div_scale); + + if (radeon_atombios_get_asic_ss_info(rdev, &ss, + ASIC_INTERNAL_ENGINE_SS, vco_freq)) { + clk_v = rv6xx_calculate_spread_spectrum_clk_v(vco_freq, + (ref_clk / (dividers.ref_div + 1)), + ss.rate, + ss.percentage, + pi->fb_div_scale); + + clk_s = rv6xx_calculate_spread_spectrum_clk_s(ss.rate, + (ref_clk / (dividers.ref_div + 1))); + + rv6xx_set_engine_spread_spectrum_clk_v(rdev, level, clk_v); + rv6xx_set_engine_spread_spectrum_clk_s(rdev, level, clk_s); + rv6xx_enable_engine_spread_spectrum(rdev, level, true); + } + } + } +} + +static void rv6xx_program_sclk_spread_spectrum_parameters_except_lowest_entry(struct radeon_device *rdev) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + + rv6xx_program_engine_spread_spectrum(rdev, + pi->hw.sclks[R600_POWER_LEVEL_HIGH], + R600_POWER_LEVEL_HIGH); + + rv6xx_program_engine_spread_spectrum(rdev, + pi->hw.sclks[R600_POWER_LEVEL_MEDIUM], + R600_POWER_LEVEL_MEDIUM); + +} + +static int rv6xx_program_mclk_stepping_entry(struct radeon_device *rdev, + u32 entry, u32 clock) +{ + struct atom_clock_dividers dividers; + + if (radeon_atom_get_clock_dividers(rdev, COMPUTE_MEMORY_PLL_PARAM, clock, false, ÷rs)) + return -EINVAL; + + + rv6xx_memory_clock_entry_set_reference_divider(rdev, entry, dividers.ref_div); + rv6xx_memory_clock_entry_set_feedback_divider(rdev, entry, dividers.fb_div); + rv6xx_memory_clock_entry_set_post_divider(rdev, entry, dividers.post_div); + + if (dividers.enable_post_div) + rv6xx_memory_clock_entry_enable_post_divider(rdev, entry, true); + else + rv6xx_memory_clock_entry_enable_post_divider(rdev, entry, false); + + return 0; +} + +static void rv6xx_program_mclk_stepping_parameters_except_lowest_entry(struct radeon_device *rdev) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + int i; + + for (i = 1; i < R600_PM_NUMBER_OF_MCLKS; i++) { + if (pi->hw.mclks[i]) + rv6xx_program_mclk_stepping_entry(rdev, i, + pi->hw.mclks[i]); + } +} + +static void rv6xx_find_memory_clock_with_highest_vco(struct radeon_device *rdev, + u32 requested_memory_clock, + u32 ref_clk, + struct atom_clock_dividers *dividers, + u32 *vco_freq) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + struct atom_clock_dividers req_dividers; + u32 vco_freq_temp; + + if (radeon_atom_get_clock_dividers(rdev, COMPUTE_MEMORY_PLL_PARAM, + requested_memory_clock, false, &req_dividers) == 0) { + vco_freq_temp = rv6xx_calculate_vco_frequency(ref_clk, &req_dividers, + pi->fb_div_scale); + + if (vco_freq_temp > *vco_freq) { + *dividers = req_dividers; + *vco_freq = vco_freq_temp; + } + } +} + +static void rv6xx_program_mclk_spread_spectrum_parameters(struct radeon_device *rdev) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + u32 ref_clk = rdev->clock.mpll.reference_freq; + struct atom_clock_dividers dividers; + struct radeon_atom_ss ss; + u32 vco_freq = 0, clk_v, clk_s; + + rv6xx_enable_memory_spread_spectrum(rdev, false); + + if (pi->mclk_ss) { + rv6xx_find_memory_clock_with_highest_vco(rdev, + pi->hw.mclks[pi->hw.high_mclk_index], + ref_clk, + ÷rs, + &vco_freq); + + rv6xx_find_memory_clock_with_highest_vco(rdev, + pi->hw.mclks[pi->hw.medium_mclk_index], + ref_clk, + ÷rs, + &vco_freq); + + rv6xx_find_memory_clock_with_highest_vco(rdev, + pi->hw.mclks[pi->hw.low_mclk_index], + ref_clk, + ÷rs, + &vco_freq); + + if (vco_freq) { + if (radeon_atombios_get_asic_ss_info(rdev, &ss, + ASIC_INTERNAL_MEMORY_SS, vco_freq)) { + clk_v = rv6xx_calculate_spread_spectrum_clk_v(vco_freq, + (ref_clk / (dividers.ref_div + 1)), + ss.rate, + ss.percentage, + pi->fb_div_scale); + + clk_s = rv6xx_calculate_spread_spectrum_clk_s(ss.rate, + (ref_clk / (dividers.ref_div + 1))); + + rv6xx_set_memory_spread_spectrum_clk_v(rdev, clk_v); + rv6xx_set_memory_spread_spectrum_clk_s(rdev, clk_s); + rv6xx_enable_memory_spread_spectrum(rdev, true); + } + } + } +} + +static int rv6xx_program_voltage_stepping_entry(struct radeon_device *rdev, + u32 entry, u16 voltage) +{ + u32 mask, set_pins; + int ret; + + ret = radeon_atom_get_voltage_gpio_settings(rdev, voltage, + SET_VOLTAGE_TYPE_ASIC_VDDC, + &set_pins, &mask); + if (ret) + return ret; + + r600_voltage_control_program_voltages(rdev, entry, set_pins); + + return 0; +} + +static void rv6xx_program_voltage_stepping_parameters_except_lowest_entry(struct radeon_device *rdev) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + int i; + + for (i = 1; i < R600_PM_NUMBER_OF_VOLTAGE_LEVELS; i++) + rv6xx_program_voltage_stepping_entry(rdev, i, + pi->hw.vddc[i]); + +} + +static void rv6xx_program_backbias_stepping_parameters_except_lowest_entry(struct radeon_device *rdev) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + + if (pi->hw.backbias[1]) + WREG32_P(VID_UPPER_GPIO_CNTL, MEDIUM_BACKBIAS_VALUE, ~MEDIUM_BACKBIAS_VALUE); + else + WREG32_P(VID_UPPER_GPIO_CNTL, 0, ~MEDIUM_BACKBIAS_VALUE); + + if (pi->hw.backbias[2]) + WREG32_P(VID_UPPER_GPIO_CNTL, HIGH_BACKBIAS_VALUE, ~HIGH_BACKBIAS_VALUE); + else + WREG32_P(VID_UPPER_GPIO_CNTL, 0, ~HIGH_BACKBIAS_VALUE); +} + +static void rv6xx_program_sclk_spread_spectrum_parameters_lowest_entry(struct radeon_device *rdev) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + + rv6xx_program_engine_spread_spectrum(rdev, + pi->hw.sclks[R600_POWER_LEVEL_LOW], + R600_POWER_LEVEL_LOW); +} + +static void rv6xx_program_mclk_stepping_parameters_lowest_entry(struct radeon_device *rdev) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + + if (pi->hw.mclks[0]) + rv6xx_program_mclk_stepping_entry(rdev, 0, + pi->hw.mclks[0]); +} + +static void rv6xx_program_voltage_stepping_parameters_lowest_entry(struct radeon_device *rdev) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + + rv6xx_program_voltage_stepping_entry(rdev, 0, + pi->hw.vddc[0]); + +} + +static void rv6xx_program_backbias_stepping_parameters_lowest_entry(struct radeon_device *rdev) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + + if (pi->hw.backbias[0]) + WREG32_P(VID_UPPER_GPIO_CNTL, LOW_BACKBIAS_VALUE, ~LOW_BACKBIAS_VALUE); + else + WREG32_P(VID_UPPER_GPIO_CNTL, 0, ~LOW_BACKBIAS_VALUE); +} + +static u32 calculate_memory_refresh_rate(struct radeon_device *rdev, + u32 engine_clock) +{ + u32 dram_rows, dram_refresh_rate; + u32 tmp; + + tmp = (RREG32(RAMCFG) & NOOFROWS_MASK) >> NOOFROWS_SHIFT; + dram_rows = 1 << (tmp + 10); + dram_refresh_rate = 1 << ((RREG32(MC_SEQ_RESERVE_M) & 0x3) + 3); + + return ((engine_clock * 10) * dram_refresh_rate / dram_rows - 32) / 64; +} + +static void rv6xx_program_memory_timing_parameters(struct radeon_device *rdev) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + u32 sqm_ratio; + u32 arb_refresh_rate; + u32 high_clock; + + if (pi->hw.sclks[R600_POWER_LEVEL_HIGH] < + (pi->hw.sclks[R600_POWER_LEVEL_LOW] * 0xFF / 0x40)) + high_clock = pi->hw.sclks[R600_POWER_LEVEL_HIGH]; + else + high_clock = + pi->hw.sclks[R600_POWER_LEVEL_LOW] * 0xFF / 0x40; + + radeon_atom_set_engine_dram_timings(rdev, high_clock, 0); + + sqm_ratio = (STATE0(64 * high_clock / pi->hw.sclks[R600_POWER_LEVEL_LOW]) | + STATE1(64 * high_clock / pi->hw.sclks[R600_POWER_LEVEL_MEDIUM]) | + STATE2(64 * high_clock / pi->hw.sclks[R600_POWER_LEVEL_HIGH]) | + STATE3(64 * high_clock / pi->hw.sclks[R600_POWER_LEVEL_HIGH])); + WREG32(SQM_RATIO, sqm_ratio); + + arb_refresh_rate = + (POWERMODE0(calculate_memory_refresh_rate(rdev, + pi->hw.sclks[R600_POWER_LEVEL_LOW])) | + POWERMODE1(calculate_memory_refresh_rate(rdev, + pi->hw.sclks[R600_POWER_LEVEL_MEDIUM])) | + POWERMODE2(calculate_memory_refresh_rate(rdev, + pi->hw.sclks[R600_POWER_LEVEL_MEDIUM])) | + POWERMODE3(calculate_memory_refresh_rate(rdev, + pi->hw.sclks[R600_POWER_LEVEL_HIGH]))); + WREG32(ARB_RFSH_RATE, arb_refresh_rate); +} + +static void rv6xx_program_mpll_timing_parameters(struct radeon_device *rdev) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + + r600_set_mpll_lock_time(rdev, R600_MPLLLOCKTIME_DFLT * + pi->mpll_ref_div); + r600_set_mpll_reset_time(rdev, R600_MPLLRESETTIME_DFLT); +} + +static void rv6xx_program_bsp(struct radeon_device *rdev) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + u32 ref_clk = rdev->clock.spll.reference_freq; + + r600_calculate_u_and_p(R600_ASI_DFLT, + ref_clk, 16, + &pi->bsp, + &pi->bsu); + + r600_set_bsp(rdev, pi->bsu, pi->bsp); +} + +static void rv6xx_program_at(struct radeon_device *rdev) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + + r600_set_at(rdev, + (pi->hw.rp[0] * pi->bsp) / 200, + (pi->hw.rp[1] * pi->bsp) / 200, + (pi->hw.lp[2] * pi->bsp) / 200, + (pi->hw.lp[1] * pi->bsp) / 200); +} + +static void rv6xx_program_git(struct radeon_device *rdev) +{ + r600_set_git(rdev, R600_GICST_DFLT); +} + +static void rv6xx_program_tp(struct radeon_device *rdev) +{ + int i; + + for (i = 0; i < R600_PM_NUMBER_OF_TC; i++) + r600_set_tc(rdev, i, r600_utc[i], r600_dtc[i]); + + r600_select_td(rdev, R600_TD_DFLT); +} + +static void rv6xx_program_vc(struct radeon_device *rdev) +{ + r600_set_vrc(rdev, R600_VRC_DFLT); +} + +static void rv6xx_clear_vc(struct radeon_device *rdev) +{ + r600_set_vrc(rdev, 0); +} + +static void rv6xx_program_tpp(struct radeon_device *rdev) +{ + r600_set_tpu(rdev, R600_TPU_DFLT); + r600_set_tpc(rdev, R600_TPC_DFLT); +} + +static void rv6xx_program_sstp(struct radeon_device *rdev) +{ + r600_set_sstu(rdev, R600_SSTU_DFLT); + r600_set_sst(rdev, R600_SST_DFLT); +} + +static void rv6xx_program_fcp(struct radeon_device *rdev) +{ + r600_set_fctu(rdev, R600_FCTU_DFLT); + r600_set_fct(rdev, R600_FCT_DFLT); +} + +static void rv6xx_program_vddc3d_parameters(struct radeon_device *rdev) +{ + r600_set_vddc3d_oorsu(rdev, R600_VDDC3DOORSU_DFLT); + r600_set_vddc3d_oorphc(rdev, R600_VDDC3DOORPHC_DFLT); + r600_set_vddc3d_oorsdc(rdev, R600_VDDC3DOORSDC_DFLT); + r600_set_ctxcgtt3d_rphc(rdev, R600_CTXCGTT3DRPHC_DFLT); + r600_set_ctxcgtt3d_rsdc(rdev, R600_CTXCGTT3DRSDC_DFLT); +} + +static void rv6xx_program_voltage_timing_parameters(struct radeon_device *rdev) +{ + u32 rt; + + r600_vid_rt_set_vru(rdev, R600_VRU_DFLT); + + r600_vid_rt_set_vrt(rdev, + rv6xx_compute_count_for_delay(rdev, + rdev->pm.dpm.voltage_response_time, + R600_VRU_DFLT)); + + rt = rv6xx_compute_count_for_delay(rdev, + rdev->pm.dpm.backbias_response_time, + R600_VRU_DFLT); + + rv6xx_vid_response_set_brt(rdev, (rt + 0x1F) >> 5); +} + +static void rv6xx_program_engine_speed_parameters(struct radeon_device *rdev) +{ + r600_vid_rt_set_ssu(rdev, R600_SPLLSTEPUNIT_DFLT); + rv6xx_enable_engine_feedback_and_reference_sync(rdev); +} + +static u64 rv6xx_get_master_voltage_mask(struct radeon_device *rdev) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + u64 master_mask = 0; + int i; + + for (i = 0; i < R600_PM_NUMBER_OF_VOLTAGE_LEVELS; i++) { + u32 tmp_mask, tmp_set_pins; + int ret; + + ret = radeon_atom_get_voltage_gpio_settings(rdev, + pi->hw.vddc[i], + SET_VOLTAGE_TYPE_ASIC_VDDC, + &tmp_set_pins, &tmp_mask); + + if (ret == 0) + master_mask |= tmp_mask; + } + + return master_mask; +} + +static void rv6xx_program_voltage_gpio_pins(struct radeon_device *rdev) +{ + r600_voltage_control_enable_pins(rdev, + rv6xx_get_master_voltage_mask(rdev)); +} + +static void rv6xx_enable_static_voltage_control(struct radeon_device *rdev, bool enable) +{ + struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps); + + if (enable) + radeon_atom_set_voltage(rdev, + new_state->low.vddc, + SET_VOLTAGE_TYPE_ASIC_VDDC); + else + r600_voltage_control_deactivate_static_control(rdev, + rv6xx_get_master_voltage_mask(rdev)); +} + +static void rv6xx_enable_display_gap(struct radeon_device *rdev, bool enable) +{ + if (enable) { + u32 tmp = (DISP1_GAP(R600_PM_DISPLAY_GAP_VBLANK_OR_WM) | + DISP2_GAP(R600_PM_DISPLAY_GAP_VBLANK_OR_WM) | + DISP1_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE) | + DISP2_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE) | + VBI_TIMER_COUNT(0x3FFF) | + VBI_TIMER_UNIT(7)); + WREG32(CG_DISPLAY_GAP_CNTL, tmp); + + WREG32_P(MCLK_PWRMGT_CNTL, USE_DISPLAY_GAP, ~USE_DISPLAY_GAP); + } else + WREG32_P(MCLK_PWRMGT_CNTL, 0, ~USE_DISPLAY_GAP); +} + +static void rv6xx_program_power_level_enter_state(struct radeon_device *rdev) +{ + r600_power_level_set_enter_index(rdev, R600_POWER_LEVEL_MEDIUM); +} + +static void rv6xx_calculate_t(u32 l_f, u32 h_f, int h, + int d_l, int d_r, u8 *l, u8 *r) +{ + int a_n, a_d, h_r, l_r; + + h_r = d_l; + l_r = 100 - d_r; + + a_n = (int)h_f * d_l + (int)l_f * (h - d_r); + a_d = (int)l_f * l_r + (int)h_f * h_r; + + if (a_d != 0) { + *l = d_l - h_r * a_n / a_d; + *r = d_r + l_r * a_n / a_d; + } +} + +static void rv6xx_calculate_ap(struct radeon_device *rdev, + struct rv6xx_ps *state) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + + pi->hw.lp[0] = 0; + pi->hw.rp[R600_PM_NUMBER_OF_ACTIVITY_LEVELS - 1] + = 100; + + rv6xx_calculate_t(state->low.sclk, + state->medium.sclk, + R600_AH_DFLT, + R600_LMP_DFLT, + R600_RLP_DFLT, + &pi->hw.lp[1], + &pi->hw.rp[0]); + + rv6xx_calculate_t(state->medium.sclk, + state->high.sclk, + R600_AH_DFLT, + R600_LHP_DFLT, + R600_RMP_DFLT, + &pi->hw.lp[2], + &pi->hw.rp[1]); + +} + +static void rv6xx_calculate_stepping_parameters(struct radeon_device *rdev) +{ + struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps); + + rv6xx_calculate_engine_speed_stepping_parameters(rdev, new_state); + rv6xx_calculate_memory_clock_stepping_parameters(rdev, new_state); + rv6xx_calculate_voltage_stepping_parameters(rdev, new_state); + rv6xx_calculate_ap(rdev, new_state); +} + +static void rv6xx_program_stepping_parameters_except_lowest_entry(struct radeon_device *rdev) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + + rv6xx_program_mclk_stepping_parameters_except_lowest_entry(rdev); + if (pi->voltage_control) + rv6xx_program_voltage_stepping_parameters_except_lowest_entry(rdev); + rv6xx_program_backbias_stepping_parameters_except_lowest_entry(rdev); + rv6xx_program_sclk_spread_spectrum_parameters_except_lowest_entry(rdev); + rv6xx_program_mclk_spread_spectrum_parameters(rdev); + rv6xx_program_memory_timing_parameters(rdev); +} + +static void rv6xx_program_stepping_parameters_lowest_entry(struct radeon_device *rdev) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + + rv6xx_program_mclk_stepping_parameters_lowest_entry(rdev); + if (pi->voltage_control) + rv6xx_program_voltage_stepping_parameters_lowest_entry(rdev); + rv6xx_program_backbias_stepping_parameters_lowest_entry(rdev); + rv6xx_program_sclk_spread_spectrum_parameters_lowest_entry(rdev); +} + +static void rv6xx_program_power_level_low(struct radeon_device *rdev) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + + r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_LOW, + pi->hw.low_vddc_index); + r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_LOW, + pi->hw.low_mclk_index); + r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_LOW, + pi->hw.low_sclk_index); + r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_LOW, + R600_DISPLAY_WATERMARK_LOW); + r600_power_level_set_pcie_gen2(rdev, R600_POWER_LEVEL_LOW, + pi->hw.pcie_gen2[R600_POWER_LEVEL_LOW]); +} + +static void rv6xx_program_power_level_low_to_lowest_state(struct radeon_device *rdev) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + + r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_LOW, 0); + r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_LOW, 0); + r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_LOW, 0); + + r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_LOW, + R600_DISPLAY_WATERMARK_LOW); + + r600_power_level_set_pcie_gen2(rdev, R600_POWER_LEVEL_LOW, + pi->hw.pcie_gen2[R600_POWER_LEVEL_LOW]); + +} + +static void rv6xx_program_power_level_medium(struct radeon_device *rdev) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + + r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_MEDIUM, + pi->hw.medium_vddc_index); + r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_MEDIUM, + pi->hw.medium_mclk_index); + r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_MEDIUM, + pi->hw.medium_sclk_index); + r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_MEDIUM, + R600_DISPLAY_WATERMARK_LOW); + r600_power_level_set_pcie_gen2(rdev, R600_POWER_LEVEL_MEDIUM, + pi->hw.pcie_gen2[R600_POWER_LEVEL_MEDIUM]); +} + +static void rv6xx_program_power_level_medium_for_transition(struct radeon_device *rdev) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + + rv6xx_program_mclk_stepping_entry(rdev, + R600_POWER_LEVEL_CTXSW, + pi->hw.mclks[pi->hw.low_mclk_index]); + + r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_MEDIUM, 1); + + r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_MEDIUM, + R600_POWER_LEVEL_CTXSW); + r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_MEDIUM, + pi->hw.medium_sclk_index); + + r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_MEDIUM, + R600_DISPLAY_WATERMARK_LOW); + + rv6xx_enable_engine_spread_spectrum(rdev, R600_POWER_LEVEL_MEDIUM, false); + + r600_power_level_set_pcie_gen2(rdev, R600_POWER_LEVEL_MEDIUM, + pi->hw.pcie_gen2[R600_POWER_LEVEL_LOW]); +} + +static void rv6xx_program_power_level_high(struct radeon_device *rdev) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + + r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_HIGH, + pi->hw.high_vddc_index); + r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_HIGH, + pi->hw.high_mclk_index); + r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_HIGH, + pi->hw.high_sclk_index); + + r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_HIGH, + R600_DISPLAY_WATERMARK_HIGH); + + r600_power_level_set_pcie_gen2(rdev, R600_POWER_LEVEL_HIGH, + pi->hw.pcie_gen2[R600_POWER_LEVEL_HIGH]); +} + +static void rv6xx_enable_backbias(struct radeon_device *rdev, bool enable) +{ + if (enable) + WREG32_P(GENERAL_PWRMGT, BACKBIAS_PAD_EN | BACKBIAS_DPM_CNTL, + ~(BACKBIAS_PAD_EN | BACKBIAS_DPM_CNTL)); + else + WREG32_P(GENERAL_PWRMGT, 0, + ~(BACKBIAS_VALUE | BACKBIAS_PAD_EN | BACKBIAS_DPM_CNTL)); +} + +static void rv6xx_program_display_gap(struct radeon_device *rdev) +{ + u32 tmp = RREG32(CG_DISPLAY_GAP_CNTL); + + tmp &= ~(DISP1_GAP_MCHG_MASK | DISP2_GAP_MCHG_MASK); + if (RREG32(AVIVO_D1CRTC_CONTROL) & AVIVO_CRTC_EN) { + tmp |= DISP1_GAP_MCHG(R600_PM_DISPLAY_GAP_VBLANK); + tmp |= DISP2_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE); + } else if (RREG32(AVIVO_D2CRTC_CONTROL) & AVIVO_CRTC_EN) { + tmp |= DISP1_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE); + tmp |= DISP2_GAP_MCHG(R600_PM_DISPLAY_GAP_VBLANK); + } else { + tmp |= DISP1_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE); + tmp |= DISP2_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE); + } + WREG32(CG_DISPLAY_GAP_CNTL, tmp); +} + +static void rv6xx_set_sw_voltage_to_safe(struct radeon_device *rdev) +{ + struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps); + struct rv6xx_ps *old_state = rv6xx_get_ps(rdev->pm.dpm.current_ps); + u16 safe_voltage; + + safe_voltage = (new_state->low.vddc >= old_state->low.vddc) ? + new_state->low.vddc : old_state->low.vddc; + + rv6xx_program_voltage_stepping_entry(rdev, R600_POWER_LEVEL_CTXSW, + safe_voltage); + + WREG32_P(GENERAL_PWRMGT, SW_GPIO_INDEX(R600_POWER_LEVEL_CTXSW), + ~SW_GPIO_INDEX_MASK); +} + +static void rv6xx_set_sw_voltage_to_low(struct radeon_device *rdev) +{ + struct rv6xx_ps *old_state = rv6xx_get_ps(rdev->pm.dpm.current_ps); + + rv6xx_program_voltage_stepping_entry(rdev, R600_POWER_LEVEL_CTXSW, + old_state->low.vddc); + + WREG32_P(GENERAL_PWRMGT, SW_GPIO_INDEX(R600_POWER_LEVEL_CTXSW), + ~SW_GPIO_INDEX_MASK); +} + +static void rv6xx_set_safe_backbias(struct radeon_device *rdev) +{ + struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps); + struct rv6xx_ps *old_state = rv6xx_get_ps(rdev->pm.dpm.current_ps); + + if ((new_state->low.flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE) && + (old_state->low.flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE)) + WREG32_P(GENERAL_PWRMGT, BACKBIAS_VALUE, ~BACKBIAS_VALUE); + else + WREG32_P(GENERAL_PWRMGT, 0, ~BACKBIAS_VALUE); +} + +static void rv6xx_set_safe_pcie_gen2(struct radeon_device *rdev) +{ + struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps); + struct rv6xx_ps *old_state = rv6xx_get_ps(rdev->pm.dpm.current_ps); + + if ((new_state->low.flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) != + (old_state->low.flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2)) + rv6xx_force_pcie_gen1(rdev); +} + +static void rv6xx_enable_dynamic_voltage_control(struct radeon_device *rdev, + bool enable) +{ + if (enable) + WREG32_P(GENERAL_PWRMGT, VOLT_PWRMGT_EN, ~VOLT_PWRMGT_EN); + else + WREG32_P(GENERAL_PWRMGT, 0, ~VOLT_PWRMGT_EN); +} + +static void rv6xx_enable_dynamic_backbias_control(struct radeon_device *rdev, + bool enable) +{ + if (enable) + WREG32_P(GENERAL_PWRMGT, BACKBIAS_DPM_CNTL, ~BACKBIAS_DPM_CNTL); + else + WREG32_P(GENERAL_PWRMGT, 0, ~BACKBIAS_DPM_CNTL); +} + +static int rv6xx_step_sw_voltage(struct radeon_device *rdev, + u16 initial_voltage, + u16 target_voltage) +{ + u16 current_voltage; + u16 true_target_voltage; + u16 voltage_step; + int signed_voltage_step; + + if ((radeon_atom_get_voltage_step(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC, + &voltage_step)) || + (radeon_atom_round_to_true_voltage(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC, + initial_voltage, ¤t_voltage)) || + (radeon_atom_round_to_true_voltage(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC, + target_voltage, &true_target_voltage))) + return -EINVAL; + + if (true_target_voltage < current_voltage) + signed_voltage_step = -(int)voltage_step; + else + signed_voltage_step = voltage_step; + + while (current_voltage != true_target_voltage) { + current_voltage += signed_voltage_step; + rv6xx_program_voltage_stepping_entry(rdev, R600_POWER_LEVEL_CTXSW, + current_voltage); + msleep((rdev->pm.dpm.voltage_response_time + 999) / 1000); + } + + return 0; +} + +static int rv6xx_step_voltage_if_increasing(struct radeon_device *rdev) +{ + struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps); + struct rv6xx_ps *old_state = rv6xx_get_ps(rdev->pm.dpm.current_ps); + + if (new_state->low.vddc > old_state->low.vddc) + return rv6xx_step_sw_voltage(rdev, + old_state->low.vddc, + new_state->low.vddc); + + return 0; +} + +static int rv6xx_step_voltage_if_decreasing(struct radeon_device *rdev) +{ + struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps); + struct rv6xx_ps *old_state = rv6xx_get_ps(rdev->pm.dpm.current_ps); + + if (new_state->low.vddc < old_state->low.vddc) + return rv6xx_step_sw_voltage(rdev, + old_state->low.vddc, + new_state->low.vddc); + else + return 0; +} + +static void rv6xx_enable_high(struct radeon_device *rdev) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + + if ((pi->restricted_levels < 1) || + (pi->restricted_levels == 3)) + r600_power_level_enable(rdev, R600_POWER_LEVEL_HIGH, true); +} + +static void rv6xx_enable_medium(struct radeon_device *rdev) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + + if (pi->restricted_levels < 2) + r600_power_level_enable(rdev, R600_POWER_LEVEL_MEDIUM, true); +} + +static void rv6xx_set_dpm_event_sources(struct radeon_device *rdev, u32 sources) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + bool want_thermal_protection; + enum radeon_dpm_event_src dpm_event_src; + + switch (sources) { + case 0: + default: + want_thermal_protection = false; + break; + case (1 << RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL): + want_thermal_protection = true; + dpm_event_src = RADEON_DPM_EVENT_SRC_DIGITAL; + break; + + case (1 << RADEON_DPM_AUTO_THROTTLE_SRC_EXTERNAL): + want_thermal_protection = true; + dpm_event_src = RADEON_DPM_EVENT_SRC_EXTERNAL; + break; + + case ((1 << RADEON_DPM_AUTO_THROTTLE_SRC_EXTERNAL) | + (1 << RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL)): + want_thermal_protection = true; + dpm_event_src = RADEON_DPM_EVENT_SRC_DIGIAL_OR_EXTERNAL; + break; + } + + if (want_thermal_protection) { + WREG32_P(CG_THERMAL_CTRL, DPM_EVENT_SRC(dpm_event_src), ~DPM_EVENT_SRC_MASK); + if (pi->thermal_protection) + WREG32_P(GENERAL_PWRMGT, 0, ~THERMAL_PROTECTION_DIS); + } else { + WREG32_P(GENERAL_PWRMGT, THERMAL_PROTECTION_DIS, ~THERMAL_PROTECTION_DIS); + } +} + +static void rv6xx_enable_auto_throttle_source(struct radeon_device *rdev, + enum radeon_dpm_auto_throttle_src source, + bool enable) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + + if (enable) { + if (!(pi->active_auto_throttle_sources & (1 << source))) { + pi->active_auto_throttle_sources |= 1 << source; + rv6xx_set_dpm_event_sources(rdev, pi->active_auto_throttle_sources); + } + } else { + if (pi->active_auto_throttle_sources & (1 << source)) { + pi->active_auto_throttle_sources &= ~(1 << source); + rv6xx_set_dpm_event_sources(rdev, pi->active_auto_throttle_sources); + } + } +} + + +static void rv6xx_enable_thermal_protection(struct radeon_device *rdev, + bool enable) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + + if (pi->active_auto_throttle_sources) + r600_enable_thermal_protection(rdev, enable); +} + +static void rv6xx_generate_transition_stepping(struct radeon_device *rdev) +{ + struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps); + struct rv6xx_ps *old_state = rv6xx_get_ps(rdev->pm.dpm.current_ps); + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + + rv6xx_generate_steps(rdev, + old_state->low.sclk, + new_state->low.sclk, + 0, &pi->hw.medium_sclk_index); +} + +static void rv6xx_generate_low_step(struct radeon_device *rdev) +{ + struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps); + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + + pi->hw.low_sclk_index = 0; + rv6xx_generate_single_step(rdev, + new_state->low.sclk, + 0); +} + +static void rv6xx_invalidate_intermediate_steps(struct radeon_device *rdev) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + + rv6xx_invalidate_intermediate_steps_range(rdev, 0, + pi->hw.medium_sclk_index); +} + +static void rv6xx_generate_stepping_table(struct radeon_device *rdev) +{ + struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps); + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + + pi->hw.low_sclk_index = 0; + + rv6xx_generate_steps(rdev, + new_state->low.sclk, + new_state->medium.sclk, + 0, + &pi->hw.medium_sclk_index); + rv6xx_generate_steps(rdev, + new_state->medium.sclk, + new_state->high.sclk, + pi->hw.medium_sclk_index, + &pi->hw.high_sclk_index); +} + +static void rv6xx_enable_spread_spectrum(struct radeon_device *rdev, + bool enable) +{ + if (enable) + rv6xx_enable_dynamic_spread_spectrum(rdev, true); + else { + rv6xx_enable_engine_spread_spectrum(rdev, R600_POWER_LEVEL_LOW, false); + rv6xx_enable_engine_spread_spectrum(rdev, R600_POWER_LEVEL_MEDIUM, false); + rv6xx_enable_engine_spread_spectrum(rdev, R600_POWER_LEVEL_HIGH, false); + rv6xx_enable_dynamic_spread_spectrum(rdev, false); + rv6xx_enable_memory_spread_spectrum(rdev, false); + } +} + +static void rv6xx_reset_lvtm_data_sync(struct radeon_device *rdev) +{ + if (ASIC_IS_DCE3(rdev)) + WREG32_P(DCE3_LVTMA_DATA_SYNCHRONIZATION, LVTMA_PFREQCHG, ~LVTMA_PFREQCHG); + else + WREG32_P(LVTMA_DATA_SYNCHRONIZATION, LVTMA_PFREQCHG, ~LVTMA_PFREQCHG); +} + +static void rv6xx_enable_dynamic_pcie_gen2(struct radeon_device *rdev, + bool enable) +{ + struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps); + + if (enable) { + rv6xx_enable_bif_dynamic_pcie_gen2(rdev, true); + rv6xx_enable_pcie_gen2_support(rdev); + r600_enable_dynamic_pcie_gen2(rdev, true); + } else { + if (!(new_state->low.flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2)) + rv6xx_force_pcie_gen1(rdev); + rv6xx_enable_bif_dynamic_pcie_gen2(rdev, false); + r600_enable_dynamic_pcie_gen2(rdev, false); + } +} + +int rv6xx_dpm_enable(struct radeon_device *rdev) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + + if (r600_dynamicpm_enabled(rdev)) + return -EINVAL; + + if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_BACKBIAS) + rv6xx_enable_backbias(rdev, true); + + if (pi->dynamic_ss) + rv6xx_enable_spread_spectrum(rdev, true); + + rv6xx_program_mpll_timing_parameters(rdev); + rv6xx_program_bsp(rdev); + rv6xx_program_git(rdev); + rv6xx_program_tp(rdev); + rv6xx_program_tpp(rdev); + rv6xx_program_sstp(rdev); + rv6xx_program_fcp(rdev); + rv6xx_program_vddc3d_parameters(rdev); + rv6xx_program_voltage_timing_parameters(rdev); + rv6xx_program_engine_speed_parameters(rdev); + + rv6xx_enable_display_gap(rdev, true); + if (pi->display_gap == false) + rv6xx_enable_display_gap(rdev, false); + + rv6xx_program_power_level_enter_state(rdev); + + rv6xx_calculate_stepping_parameters(rdev); + + if (pi->voltage_control) + rv6xx_program_voltage_gpio_pins(rdev); + + rv6xx_generate_stepping_table(rdev); + + rv6xx_program_stepping_parameters_except_lowest_entry(rdev); + rv6xx_program_stepping_parameters_lowest_entry(rdev); + + rv6xx_program_power_level_low(rdev); + rv6xx_program_power_level_medium(rdev); + rv6xx_program_power_level_high(rdev); + rv6xx_program_vc(rdev); + rv6xx_program_at(rdev); + + r600_power_level_enable(rdev, R600_POWER_LEVEL_LOW, true); + r600_power_level_enable(rdev, R600_POWER_LEVEL_MEDIUM, true); + r600_power_level_enable(rdev, R600_POWER_LEVEL_HIGH, true); + + if (rdev->irq.installed && + r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) { + r600_set_thermal_temperature_range(rdev, R600_TEMP_RANGE_MIN, R600_TEMP_RANGE_MAX); + rdev->irq.dpm_thermal = true; + radeon_irq_set(rdev); + } + + rv6xx_enable_auto_throttle_source(rdev, RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL, true); + + r600_start_dpm(rdev); + + if (pi->voltage_control) + rv6xx_enable_static_voltage_control(rdev, false); + + if (pi->dynamic_pcie_gen2) + rv6xx_enable_dynamic_pcie_gen2(rdev, true); + + if (pi->gfx_clock_gating) + r600_gfx_clockgating_enable(rdev, true); + + return 0; +} + +void rv6xx_dpm_disable(struct radeon_device *rdev) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + + if (!r600_dynamicpm_enabled(rdev)) + return; + + r600_power_level_enable(rdev, R600_POWER_LEVEL_LOW, true); + r600_power_level_enable(rdev, R600_POWER_LEVEL_MEDIUM, true); + rv6xx_enable_display_gap(rdev, false); + rv6xx_clear_vc(rdev); + r600_set_at(rdev, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF); + + if (pi->thermal_protection) + r600_enable_thermal_protection(rdev, false); + + r600_wait_for_power_level(rdev, R600_POWER_LEVEL_LOW); + r600_power_level_enable(rdev, R600_POWER_LEVEL_HIGH, false); + r600_power_level_enable(rdev, R600_POWER_LEVEL_MEDIUM, false); + + if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_BACKBIAS) + rv6xx_enable_backbias(rdev, false); + + rv6xx_enable_spread_spectrum(rdev, false); + + if (pi->voltage_control) + rv6xx_enable_static_voltage_control(rdev, true); + + if (pi->dynamic_pcie_gen2) + rv6xx_enable_dynamic_pcie_gen2(rdev, false); + + if (rdev->irq.installed && + r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) { + rdev->irq.dpm_thermal = false; + radeon_irq_set(rdev); + } + + if (pi->gfx_clock_gating) + r600_gfx_clockgating_enable(rdev, false); + + r600_stop_dpm(rdev); +} + +int rv6xx_dpm_set_power_state(struct radeon_device *rdev) +{ + struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + + rv6xx_clear_vc(rdev); + r600_power_level_enable(rdev, R600_POWER_LEVEL_LOW, true); + r600_set_at(rdev, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF); + + if (pi->thermal_protection) + r600_enable_thermal_protection(rdev, false); + + r600_wait_for_power_level(rdev, R600_POWER_LEVEL_LOW); + r600_power_level_enable(rdev, R600_POWER_LEVEL_HIGH, false); + r600_power_level_enable(rdev, R600_POWER_LEVEL_MEDIUM, false); + + rv6xx_generate_transition_stepping(rdev); + rv6xx_program_power_level_medium_for_transition(rdev); + + if (pi->voltage_control) { + rv6xx_set_sw_voltage_to_safe(rdev); + if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_STEPVDDC) + rv6xx_set_sw_voltage_to_low(rdev); + } + + if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_BACKBIAS) + rv6xx_set_safe_backbias(rdev); + + if (pi->dynamic_pcie_gen2) + rv6xx_set_safe_pcie_gen2(rdev); + + if (pi->voltage_control) + rv6xx_enable_dynamic_voltage_control(rdev, false); + + if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_BACKBIAS) + rv6xx_enable_dynamic_backbias_control(rdev, false); + + if (pi->voltage_control) { + if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_STEPVDDC) + rv6xx_step_voltage_if_increasing(rdev); + msleep((rdev->pm.dpm.voltage_response_time + 999) / 1000); + } + + r600_power_level_enable(rdev, R600_POWER_LEVEL_MEDIUM, true); + r600_power_level_enable(rdev, R600_POWER_LEVEL_LOW, false); + r600_wait_for_power_level_unequal(rdev, R600_POWER_LEVEL_LOW); + + rv6xx_generate_low_step(rdev); + rv6xx_invalidate_intermediate_steps(rdev); + rv6xx_calculate_stepping_parameters(rdev); + rv6xx_program_stepping_parameters_lowest_entry(rdev); + rv6xx_program_power_level_low_to_lowest_state(rdev); + + r600_power_level_enable(rdev, R600_POWER_LEVEL_LOW, true); + r600_wait_for_power_level(rdev, R600_POWER_LEVEL_LOW); + r600_power_level_enable(rdev, R600_POWER_LEVEL_MEDIUM, false); + + if (pi->voltage_control) { + if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_STEPVDDC) + rv6xx_step_voltage_if_decreasing(rdev); + rv6xx_enable_dynamic_voltage_control(rdev, true); + } + + if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_BACKBIAS) + rv6xx_enable_dynamic_backbias_control(rdev, true); + + if (pi->dynamic_pcie_gen2) + rv6xx_enable_dynamic_pcie_gen2(rdev, true); + + rv6xx_reset_lvtm_data_sync(rdev); + + rv6xx_generate_stepping_table(rdev); + rv6xx_program_stepping_parameters_except_lowest_entry(rdev); + rv6xx_program_power_level_low(rdev); + rv6xx_program_power_level_medium(rdev); + rv6xx_program_power_level_high(rdev); + rv6xx_enable_medium(rdev); + rv6xx_enable_high(rdev); + + if (pi->thermal_protection) + rv6xx_enable_thermal_protection(rdev, true); + rv6xx_program_vc(rdev); + rv6xx_program_at(rdev); + + return 0; +} + +void rv6xx_setup_asic(struct radeon_device *rdev) +{ + r600_enable_acpi_pm(rdev); + + if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_ASPM_L0s) + rv6xx_enable_l0s(rdev); + if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_ASPM_L1) + rv6xx_enable_l1(rdev); + if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_TURNOFFPLL_ASPML1) + rv6xx_enable_pll_sleep_in_l1(rdev); +} + +void rv6xx_dpm_display_configuration_changed(struct radeon_device *rdev) +{ + rv6xx_program_display_gap(rdev); +} + +union power_info { + struct _ATOM_POWERPLAY_INFO info; + struct _ATOM_POWERPLAY_INFO_V2 info_2; + struct _ATOM_POWERPLAY_INFO_V3 info_3; + struct _ATOM_PPLIB_POWERPLAYTABLE pplib; + struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2; + struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3; +}; + +union pplib_clock_info { + struct _ATOM_PPLIB_R600_CLOCK_INFO r600; + struct _ATOM_PPLIB_RS780_CLOCK_INFO rs780; + struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO evergreen; + struct _ATOM_PPLIB_SUMO_CLOCK_INFO sumo; +}; + +union pplib_power_state { + struct _ATOM_PPLIB_STATE v1; + struct _ATOM_PPLIB_STATE_V2 v2; +}; + +static void rv6xx_parse_pplib_non_clock_info(struct radeon_device *rdev, + struct radeon_ps *rps, + struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info) +{ + rps->caps = le32_to_cpu(non_clock_info->ulCapsAndSettings); + rps->class = le16_to_cpu(non_clock_info->usClassification); + rps->class2 = le16_to_cpu(non_clock_info->usClassification2); + + if (r600_is_uvd_state(rps->class, rps->class2)) { + rps->vclk = RV6XX_DEFAULT_VCLK_FREQ; + rps->dclk = RV6XX_DEFAULT_DCLK_FREQ; + } else { + rps->vclk = 0; + rps->dclk = 0; + } + + if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT) + rdev->pm.dpm.boot_ps = rps; + if (rps->class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE) + rdev->pm.dpm.uvd_ps = rps; +} + +static void rv6xx_parse_pplib_clock_info(struct radeon_device *rdev, + struct radeon_ps *rps, int index, + union pplib_clock_info *clock_info) +{ + struct rv6xx_ps *ps = rv6xx_get_ps(rps); + u32 sclk, mclk; + u16 vddc; + struct rv6xx_pl *pl; + + switch (index) { + case 0: + pl = &ps->low; + break; + case 1: + pl = &ps->medium; + break; + case 2: + default: + pl = &ps->high; + break; + } + + sclk = le16_to_cpu(clock_info->r600.usEngineClockLow); + sclk |= clock_info->r600.ucEngineClockHigh << 16; + mclk = le16_to_cpu(clock_info->r600.usMemoryClockLow); + mclk |= clock_info->r600.ucMemoryClockHigh << 16; + + pl->mclk = mclk; + pl->sclk = sclk; + pl->vddc = le16_to_cpu(clock_info->r600.usVDDC); + pl->flags = le32_to_cpu(clock_info->r600.ulFlags); + + /* patch up vddc if necessary */ + if (pl->vddc == 0xff01) { + if (radeon_atom_get_max_vddc(rdev, 0, 0, &vddc) == 0) + pl->vddc = vddc; + } + + /* fix up pcie gen2 */ + if (pl->flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) { + if ((rdev->family == CHIP_RV610) || (rdev->family == CHIP_RV630)) { + if (pl->vddc < 1100) + pl->flags &= ~ATOM_PPLIB_R600_FLAGS_PCIEGEN2; + } + } + + /* patch up boot state */ + if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT) { + u16 vddc, vddci; + radeon_atombios_get_default_voltages(rdev, &vddc, &vddci); + pl->mclk = rdev->clock.default_mclk; + pl->sclk = rdev->clock.default_sclk; + pl->vddc = vddc; + } +} + +static int rv6xx_parse_power_table(struct radeon_device *rdev) +{ + struct radeon_mode_info *mode_info = &rdev->mode_info; + struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info; + union pplib_power_state *power_state; + int i, j; + union pplib_clock_info *clock_info; + union power_info *power_info; + int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo); + u16 data_offset; + u8 frev, crev; + struct rv6xx_ps *ps; + + if (!atom_parse_data_header(mode_info->atom_context, index, NULL, + &frev, &crev, &data_offset)) + return -EINVAL; + power_info = (union power_info *)(mode_info->atom_context->bios + data_offset); + + rdev->pm.dpm.ps = kzalloc(sizeof(struct radeon_ps) * + power_info->pplib.ucNumStates, GFP_KERNEL); + if (!rdev->pm.dpm.ps) + return -ENOMEM; + rdev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps); + rdev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime); + rdev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime); + + for (i = 0; i < power_info->pplib.ucNumStates; i++) { + power_state = (union pplib_power_state *) + (mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib.usStateArrayOffset) + + i * power_info->pplib.ucStateEntrySize); + non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *) + (mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset) + + (power_state->v1.ucNonClockStateIndex * + power_info->pplib.ucNonClockSize)); + if (power_info->pplib.ucStateEntrySize - 1) { + ps = kzalloc(sizeof(struct rv6xx_ps), GFP_KERNEL); + if (ps == NULL) { + kfree(rdev->pm.dpm.ps); + return -ENOMEM; + } + rdev->pm.dpm.ps[i].ps_priv = ps; + rv6xx_parse_pplib_non_clock_info(rdev, &rdev->pm.dpm.ps[i], + non_clock_info); + for (j = 0; j < (power_info->pplib.ucStateEntrySize - 1); j++) { + clock_info = (union pplib_clock_info *) + (mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib.usClockInfoArrayOffset) + + (power_state->v1.ucClockStateIndices[j] * + power_info->pplib.ucClockInfoSize)); + rv6xx_parse_pplib_clock_info(rdev, + &rdev->pm.dpm.ps[i], j, + clock_info); + } + } + } + rdev->pm.dpm.num_ps = power_info->pplib.ucNumStates; + return 0; +} + +int rv6xx_dpm_init(struct radeon_device *rdev) +{ + int index = GetIndexIntoMasterTable(DATA, ASIC_InternalSS_Info); + uint16_t data_offset, size; + uint8_t frev, crev; + struct atom_clock_dividers dividers; + struct rv6xx_power_info *pi; + int ret; + + pi = kzalloc(sizeof(struct rv6xx_power_info), GFP_KERNEL); + if (pi == NULL) + return -ENOMEM; + rdev->pm.dpm.priv = pi; + + ret = rv6xx_parse_power_table(rdev); + if (ret) + return ret; + + if (rdev->pm.dpm.voltage_response_time == 0) + rdev->pm.dpm.voltage_response_time = R600_VOLTAGERESPONSETIME_DFLT; + if (rdev->pm.dpm.backbias_response_time == 0) + rdev->pm.dpm.backbias_response_time = R600_BACKBIASRESPONSETIME_DFLT; + + ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM, + 0, false, ÷rs); + if (ret) + pi->spll_ref_div = dividers.ref_div + 1; + else + pi->spll_ref_div = R600_REFERENCEDIVIDER_DFLT; + + ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_MEMORY_PLL_PARAM, + 0, false, ÷rs); + if (ret) + pi->mpll_ref_div = dividers.ref_div + 1; + else + pi->mpll_ref_div = R600_REFERENCEDIVIDER_DFLT; + + if (rdev->family >= CHIP_RV670) + pi->fb_div_scale = 1; + else + pi->fb_div_scale = 0; + + pi->voltage_control = + radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC); + + pi->gfx_clock_gating = true; + + if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size, + &frev, &crev, &data_offset)) { + pi->sclk_ss = true; + pi->mclk_ss = true; + pi->dynamic_ss = true; + } else { + pi->sclk_ss = false; + pi->mclk_ss = false; + pi->dynamic_ss = false; + } + + pi->dynamic_pcie_gen2 = true; + + if (pi->gfx_clock_gating && + (rdev->pm.int_thermal_type != THERMAL_TYPE_NONE)) + pi->thermal_protection = true; + else + pi->thermal_protection = false; + + pi->display_gap = true; + + return 0; +} + +void rv6xx_dpm_print_power_state(struct radeon_device *rdev, + struct radeon_ps *rps) +{ + struct rv6xx_ps *ps = rv6xx_get_ps(rps); + struct rv6xx_pl *pl; + + r600_dpm_print_class_info(rps->class, rps->class2); + r600_dpm_print_cap_info(rps->caps); + printk("\tuvd vclk: %d dclk: %d\n", rps->vclk, rps->dclk); + pl = &ps->low; + printk("\t\tpower level 0 sclk: %u mclk: %u vddc: %u\n", + pl->sclk, pl->mclk, pl->vddc); + pl = &ps->medium; + printk("\t\tpower level 1 sclk: %u mclk: %u vddc: %u\n", + pl->sclk, pl->mclk, pl->vddc); + pl = &ps->high; + printk("\t\tpower level 2 sclk: %u mclk: %u vddc: %u\n", + pl->sclk, pl->mclk, pl->vddc); + r600_dpm_print_ps_status(rdev, rps); +} + +void rv6xx_dpm_fini(struct radeon_device *rdev) +{ + int i; + + for (i = 0; i < rdev->pm.dpm.num_ps; i++) { + kfree(rdev->pm.dpm.ps[i].ps_priv); + } + kfree(rdev->pm.dpm.ps); + kfree(rdev->pm.dpm.priv); +} + +u32 rv6xx_dpm_get_sclk(struct radeon_device *rdev, bool low) +{ + struct rv6xx_ps *requested_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps); + + if (low) + return requested_state->low.sclk; + else + return requested_state->high.sclk; +} + +u32 rv6xx_dpm_get_mclk(struct radeon_device *rdev, bool low) +{ + struct rv6xx_ps *requested_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps); + + if (low) + return requested_state->low.mclk; + else + return requested_state->high.mclk; +} diff --git a/drivers/gpu/drm/radeon/rv6xx_dpm.h b/drivers/gpu/drm/radeon/rv6xx_dpm.h new file mode 100644 index 0000000..8035d53 --- /dev/null +++ b/drivers/gpu/drm/radeon/rv6xx_dpm.h @@ -0,0 +1,95 @@ +/* + * Copyright 2011 Advanced Micro Devices, 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: Alex Deucher + */ + +#ifndef __RV6XX_DPM_H__ +#define __RV6XX_DPM_H__ + +#include "r600_dpm.h" + +/* Represents a single SCLK step. */ +struct rv6xx_sclk_stepping +{ + u32 vco_frequency; + u32 post_divider; +}; + +struct rv6xx_pm_hw_state { + u32 sclks[R600_PM_NUMBER_OF_ACTIVITY_LEVELS]; + u32 mclks[R600_PM_NUMBER_OF_MCLKS]; + u16 vddc[R600_PM_NUMBER_OF_VOLTAGE_LEVELS]; + bool backbias[R600_PM_NUMBER_OF_VOLTAGE_LEVELS]; + bool pcie_gen2[R600_PM_NUMBER_OF_ACTIVITY_LEVELS]; + u8 high_sclk_index; + u8 medium_sclk_index; + u8 low_sclk_index; + u8 high_mclk_index; + u8 medium_mclk_index; + u8 low_mclk_index; + u8 high_vddc_index; + u8 medium_vddc_index; + u8 low_vddc_index; + u8 rp[R600_PM_NUMBER_OF_ACTIVITY_LEVELS]; + u8 lp[R600_PM_NUMBER_OF_ACTIVITY_LEVELS]; +}; + +struct rv6xx_power_info { + /* flags */ + bool voltage_control; + bool sclk_ss; + bool mclk_ss; + bool dynamic_ss; + bool dynamic_pcie_gen2; + bool thermal_protection; + bool display_gap; + bool gfx_clock_gating; + /* clk values */ + u32 fb_div_scale; + u32 spll_ref_div; + u32 mpll_ref_div; + u32 bsu; + u32 bsp; + /* */ + u32 active_auto_throttle_sources; + /* current power state */ + u32 restricted_levels; + struct rv6xx_pm_hw_state hw; +}; + +struct rv6xx_pl { + u32 sclk; + u32 mclk; + u16 vddc; + u32 flags; +}; + +struct rv6xx_ps { + struct rv6xx_pl high; + struct rv6xx_pl medium; + struct rv6xx_pl low; +}; + +#define RV6XX_DEFAULT_VCLK_FREQ 40000 /* 10 khz */ +#define RV6XX_DEFAULT_DCLK_FREQ 30000 /* 10 khz */ + +#endif diff --git a/drivers/gpu/drm/radeon/rv6xxd.h b/drivers/gpu/drm/radeon/rv6xxd.h new file mode 100644 index 0000000..34e86f9 --- /dev/null +++ b/drivers/gpu/drm/radeon/rv6xxd.h @@ -0,0 +1,246 @@ +/* + * Copyright 2011 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ +#ifndef RV6XXD_H +#define RV6XXD_H + +/* RV6xx power management */ +#define SPLL_CNTL_MODE 0x60c +# define SPLL_DIV_SYNC (1 << 5) + +#define GENERAL_PWRMGT 0x618 +# define GLOBAL_PWRMGT_EN (1 << 0) +# define STATIC_PM_EN (1 << 1) +# define MOBILE_SU (1 << 2) +# define THERMAL_PROTECTION_DIS (1 << 3) +# define THERMAL_PROTECTION_TYPE (1 << 4) +# define ENABLE_GEN2PCIE (1 << 5) +# define SW_GPIO_INDEX(x) ((x) << 6) +# define SW_GPIO_INDEX_MASK (3 << 6) +# define LOW_VOLT_D2_ACPI (1 << 8) +# define LOW_VOLT_D3_ACPI (1 << 9) +# define VOLT_PWRMGT_EN (1 << 10) +# define BACKBIAS_PAD_EN (1 << 16) +# define BACKBIAS_VALUE (1 << 17) +# define BACKBIAS_DPM_CNTL (1 << 18) +# define DYN_SPREAD_SPECTRUM_EN (1 << 21) + +#define MCLK_PWRMGT_CNTL 0x624 +# define MPLL_PWRMGT_OFF (1 << 0) +# define YCLK_TURNOFF (1 << 1) +# define MPLL_TURNOFF (1 << 2) +# define SU_MCLK_USE_BCLK (1 << 3) +# define DLL_READY (1 << 4) +# define MC_BUSY (1 << 5) +# define MC_INT_CNTL (1 << 7) +# define MRDCKA_SLEEP (1 << 8) +# define MRDCKB_SLEEP (1 << 9) +# define MRDCKC_SLEEP (1 << 10) +# define MRDCKD_SLEEP (1 << 11) +# define MRDCKE_SLEEP (1 << 12) +# define MRDCKF_SLEEP (1 << 13) +# define MRDCKG_SLEEP (1 << 14) +# define MRDCKH_SLEEP (1 << 15) +# define MRDCKA_RESET (1 << 16) +# define MRDCKB_RESET (1 << 17) +# define MRDCKC_RESET (1 << 18) +# define MRDCKD_RESET (1 << 19) +# define MRDCKE_RESET (1 << 20) +# define MRDCKF_RESET (1 << 21) +# define MRDCKG_RESET (1 << 22) +# define MRDCKH_RESET (1 << 23) +# define DLL_READY_READ (1 << 24) +# define USE_DISPLAY_GAP (1 << 25) +# define USE_DISPLAY_URGENT_NORMAL (1 << 26) +# define USE_DISPLAY_GAP_CTXSW (1 << 27) +# define MPLL_TURNOFF_D2 (1 << 28) +# define USE_DISPLAY_URGENT_CTXSW (1 << 29) + +#define MPLL_FREQ_LEVEL_0 0x6e8 +# define LEVEL0_MPLL_POST_DIV(x) ((x) << 0) +# define LEVEL0_MPLL_POST_DIV_MASK (0xff << 0) +# define LEVEL0_MPLL_FB_DIV(x) ((x) << 8) +# define LEVEL0_MPLL_FB_DIV_MASK (0xfff << 8) +# define LEVEL0_MPLL_REF_DIV(x) ((x) << 20) +# define LEVEL0_MPLL_REF_DIV_MASK (0x3f << 20) +# define LEVEL0_MPLL_DIV_EN (1 << 28) +# define LEVEL0_DLL_BYPASS (1 << 29) +# define LEVEL0_DLL_RESET (1 << 30) + +#define VID_RT 0x6f8 +# define VID_CRT(x) ((x) << 0) +# define VID_CRT_MASK (0x1fff << 0) +# define VID_CRTU(x) ((x) << 13) +# define VID_CRTU_MASK (7 << 13) +# define SSTU(x) ((x) << 16) +# define SSTU_MASK (7 << 16) +# define VID_SWT(x) ((x) << 19) +# define VID_SWT_MASK (0x1f << 19) +# define BRT(x) ((x) << 24) +# define BRT_MASK (0xff << 24) + +#define TARGET_AND_CURRENT_PROFILE_INDEX 0x70c +# define TARGET_PROFILE_INDEX_MASK (3 << 0) +# define TARGET_PROFILE_INDEX_SHIFT 0 +# define CURRENT_PROFILE_INDEX_MASK (3 << 2) +# define CURRENT_PROFILE_INDEX_SHIFT 2 +# define DYN_PWR_ENTER_INDEX(x) ((x) << 4) +# define DYN_PWR_ENTER_INDEX_MASK (3 << 4) +# define DYN_PWR_ENTER_INDEX_SHIFT 4 +# define CURR_MCLK_INDEX_MASK (3 << 6) +# define CURR_MCLK_INDEX_SHIFT 6 +# define CURR_SCLK_INDEX_MASK (0x1f << 8) +# define CURR_SCLK_INDEX_SHIFT 8 +# define CURR_VID_INDEX_MASK (3 << 13) +# define CURR_VID_INDEX_SHIFT 13 + +#define VID_UPPER_GPIO_CNTL 0x740 +# define CTXSW_UPPER_GPIO_VALUES(x) ((x) << 0) +# define CTXSW_UPPER_GPIO_VALUES_MASK (7 << 0) +# define HIGH_UPPER_GPIO_VALUES(x) ((x) << 3) +# define HIGH_UPPER_GPIO_VALUES_MASK (7 << 3) +# define MEDIUM_UPPER_GPIO_VALUES(x) ((x) << 6) +# define MEDIUM_UPPER_GPIO_VALUES_MASK (7 << 6) +# define LOW_UPPER_GPIO_VALUES(x) ((x) << 9) +# define LOW_UPPER_GPIO_VALUES_MASK (7 << 9) +# define CTXSW_BACKBIAS_VALUE (1 << 12) +# define HIGH_BACKBIAS_VALUE (1 << 13) +# define MEDIUM_BACKBIAS_VALUE (1 << 14) +# define LOW_BACKBIAS_VALUE (1 << 15) + +#define CG_DISPLAY_GAP_CNTL 0x7dc +# define DISP1_GAP(x) ((x) << 0) +# define DISP1_GAP_MASK (3 << 0) +# define DISP2_GAP(x) ((x) << 2) +# define DISP2_GAP_MASK (3 << 2) +# define VBI_TIMER_COUNT(x) ((x) << 4) +# define VBI_TIMER_COUNT_MASK (0x3fff << 4) +# define VBI_TIMER_UNIT(x) ((x) << 20) +# define VBI_TIMER_UNIT_MASK (7 << 20) +# define DISP1_GAP_MCHG(x) ((x) << 24) +# define DISP1_GAP_MCHG_MASK (3 << 24) +# define DISP2_GAP_MCHG(x) ((x) << 26) +# define DISP2_GAP_MCHG_MASK (3 << 26) + +#define CG_THERMAL_CTRL 0x7f0 +# define DPM_EVENT_SRC(x) ((x) << 0) +# define DPM_EVENT_SRC_MASK (7 << 0) +# define THERM_INC_CLK (1 << 3) +# define TOFFSET(x) ((x) << 4) +# define TOFFSET_MASK (0xff << 4) +# define DIG_THERM_DPM(x) ((x) << 12) +# define DIG_THERM_DPM_MASK (0xff << 12) +# define CTF_SEL(x) ((x) << 20) +# define CTF_SEL_MASK (7 << 20) +# define CTF_PAD_POLARITY (1 << 23) +# define CTF_PAD_EN (1 << 24) + +#define CG_SPLL_SPREAD_SPECTRUM_LOW 0x820 +# define SSEN (1 << 0) +# define CLKS(x) ((x) << 3) +# define CLKS_MASK (0xff << 3) +# define CLKS_SHIFT 3 +# define CLKV(x) ((x) << 11) +# define CLKV_MASK (0x7ff << 11) +# define CLKV_SHIFT 11 +#define CG_MPLL_SPREAD_SPECTRUM 0x830 + +#define CITF_CNTL 0x200c +# define BLACKOUT_RD (1 << 0) +# define BLACKOUT_WR (1 << 1) + +#define RAMCFG 0x2408 +#define NOOFBANK_SHIFT 0 +#define NOOFBANK_MASK 0x00000001 +#define NOOFRANK_SHIFT 1 +#define NOOFRANK_MASK 0x00000002 +#define NOOFROWS_SHIFT 2 +#define NOOFROWS_MASK 0x0000001C +#define NOOFCOLS_SHIFT 5 +#define NOOFCOLS_MASK 0x00000060 +#define CHANSIZE_SHIFT 7 +#define CHANSIZE_MASK 0x00000080 +#define BURSTLENGTH_SHIFT 8 +#define BURSTLENGTH_MASK 0x00000100 +#define CHANSIZE_OVERRIDE (1 << 10) + +#define SQM_RATIO 0x2424 +# define STATE0(x) ((x) << 0) +# define STATE0_MASK (0xff << 0) +# define STATE1(x) ((x) << 8) +# define STATE1_MASK (0xff << 8) +# define STATE2(x) ((x) << 16) +# define STATE2_MASK (0xff << 16) +# define STATE3(x) ((x) << 24) +# define STATE3_MASK (0xff << 24) + +#define ARB_RFSH_CNTL 0x2460 +# define ENABLE (1 << 0) +#define ARB_RFSH_RATE 0x2464 +# define POWERMODE0(x) ((x) << 0) +# define POWERMODE0_MASK (0xff << 0) +# define POWERMODE1(x) ((x) << 8) +# define POWERMODE1_MASK (0xff << 8) +# define POWERMODE2(x) ((x) << 16) +# define POWERMODE2_MASK (0xff << 16) +# define POWERMODE3(x) ((x) << 24) +# define POWERMODE3_MASK (0xff << 24) + +#define MC_SEQ_DRAM 0x2608 +# define CKE_DYN (1 << 12) + +#define MC_SEQ_CMD 0x26c4 + +#define MC_SEQ_RESERVE_S 0x2890 +#define MC_SEQ_RESERVE_M 0x2894 + +#define LVTMA_DATA_SYNCHRONIZATION 0x7adc +# define LVTMA_PFREQCHG (1 << 8) +#define DCE3_LVTMA_DATA_SYNCHRONIZATION 0x7f98 + +/* PCIE indirect regs */ +#define PCIE_P_CNTL 0x40 +# define P_PLL_PWRDN_IN_L1L23 (1 << 3) +# define P_PLL_BUF_PDNB (1 << 4) +# define P_PLL_PDNB (1 << 9) +# define P_ALLOW_PRX_FRONTEND_SHUTOFF (1 << 12) +/* PCIE PORT indirect regs */ +#define PCIE_LC_CNTL 0xa0 +# define LC_L0S_INACTIVITY(x) ((x) << 8) +# define LC_L0S_INACTIVITY_MASK (0xf << 8) +# define LC_L0S_INACTIVITY_SHIFT 8 +# define LC_L1_INACTIVITY(x) ((x) << 12) +# define LC_L1_INACTIVITY_MASK (0xf << 12) +# define LC_L1_INACTIVITY_SHIFT 12 +# define LC_PMI_TO_L1_DIS (1 << 16) +# define LC_ASPM_TO_L1_DIS (1 << 24) +#define PCIE_LC_SPEED_CNTL 0xa4 +# define LC_GEN2_EN (1 << 0) +# define LC_INITIATE_LINK_SPEED_CHANGE (1 << 7) +# define LC_CURRENT_DATA_RATE (1 << 11) +# define LC_HW_VOLTAGE_IF_CONTROL(x) ((x) << 12) +# define LC_HW_VOLTAGE_IF_CONTROL_MASK (3 << 12) +# define LC_HW_VOLTAGE_IF_CONTROL_SHIFT 12 +# define LC_OTHER_SIDE_EVER_SENT_GEN2 (1 << 23) +# define LC_OTHER_SIDE_SUPPORTS_GEN2 (1 << 24) + +#endif
Am 26.06.2013 15:22, schrieb alexdeucher@gmail.com:
From: Alex Deucher alexander.deucher@amd.com
This adds dpm support for rv6xx asics. This includes:
- clockgating
- dynamic engine clock scaling
- dynamic memory clock scaling
- dynamic voltage scaling
- dynamic pcie gen1/gen2 switching
Set radeon.dpm=1 to enable.
Signed-off-by: Alex Deucher alexander.deucher@amd.com
drivers/gpu/drm/radeon/Makefile | 2 +- drivers/gpu/drm/radeon/r600.c | 27 + drivers/gpu/drm/radeon/r600_dpm.c | 45 + drivers/gpu/drm/radeon/r600_dpm.h | 8 + drivers/gpu/drm/radeon/r600d.h | 13 + drivers/gpu/drm/radeon/radeon.h | 3 + drivers/gpu/drm/radeon/radeon_asic.c | 12 + drivers/gpu/drm/radeon/radeon_asic.h | 12 + drivers/gpu/drm/radeon/radeon_atombios.c | 4 +- drivers/gpu/drm/radeon/radeon_irq_kms.c | 2 + drivers/gpu/drm/radeon/radeon_mode.h | 2 + drivers/gpu/drm/radeon/radeon_pm.c | 9 + drivers/gpu/drm/radeon/rs780_dpm.c | 12 + drivers/gpu/drm/radeon/rv6xx_dpm.c | 1991 ++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/rv6xx_dpm.h | 95 ++ drivers/gpu/drm/radeon/rv6xxd.h | 246 ++++ 16 files changed, 2480 insertions(+), 3 deletions(-) create mode 100644 drivers/gpu/drm/radeon/rv6xx_dpm.c create mode 100644 drivers/gpu/drm/radeon/rv6xx_dpm.h create mode 100644 drivers/gpu/drm/radeon/rv6xxd.h
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index e44b046..3aa20dc 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile @@ -77,7 +77,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \ evergreen_hdmi.o radeon_trace_points.o ni.o cayman_blit_shaders.o \ atombios_encoders.o radeon_semaphore.o radeon_sa.o atombios_i2c.o si.o \ si_blit_shaders.o radeon_prime.o radeon_uvd.o cik.o cik_blit_shaders.o \
- r600_dpm.o rs780_dpm.o
r600_dpm.o rs780_dpm.o rv6xx_dpm.o
radeon-$(CONFIG_COMPAT) += radeon_ioc32.o radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o
diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index 4678ed1..7ea81c8 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -3998,6 +3998,7 @@ int r600_irq_set(struct radeon_device *rdev) u32 hdmi0, hdmi1; u32 d1grph = 0, d2grph = 0; u32 dma_cntl;
u32 thermal_int = 0;
if (!rdev->irq.installed) { WARN(1, "Can't enable IRQ/MSI because no handler is installed\n");
@@ -4032,8 +4033,18 @@ int r600_irq_set(struct radeon_device *rdev) hdmi0 = RREG32(HDMI0_AUDIO_PACKET_CONTROL) & ~HDMI0_AZ_FORMAT_WTRIG_MASK; hdmi1 = RREG32(HDMI1_AUDIO_PACKET_CONTROL) & ~HDMI0_AZ_FORMAT_WTRIG_MASK; }
dma_cntl = RREG32(DMA_CNTL) & ~TRAP_ENABLE;
if ((rdev->family > CHIP_R600) && (rdev->family < CHIP_RV770)) {
thermal_int = RREG32(CG_THERMAL_INT) &
~(THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW);
if (rdev->irq.dpm_thermal) {
DRM_DEBUG("dpm thermal\n");
thermal_int |= THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW;
}
}
if (atomic_read(&rdev->irq.ring_int[RADEON_RING_TYPE_GFX_INDEX])) { DRM_DEBUG("r600_irq_set: sw int\n"); cp_int_cntl |= RB_INT_ENABLE;
@@ -4115,6 +4126,9 @@ int r600_irq_set(struct radeon_device *rdev) WREG32(HDMI0_AUDIO_PACKET_CONTROL, hdmi0); WREG32(HDMI1_AUDIO_PACKET_CONTROL, hdmi1); }
if ((rdev->family > CHIP_R600) && (rdev->family < CHIP_RV770)) {
WREG32(CG_THERMAL_INT, thermal_int);
}
return 0; }
@@ -4306,6 +4320,7 @@ int r600_irq_process(struct radeon_device *rdev) u32 ring_index; bool queue_hotplug = false; bool queue_hdmi = false;
bool queue_thermal = false;
if (!rdev->ih.enabled || rdev->shutdown) return IRQ_NONE;
@@ -4473,6 +4488,16 @@ restart_ih: DRM_DEBUG("IH: DMA trap\n"); radeon_fence_process(rdev, R600_RING_TYPE_DMA_INDEX); break;
case 230: /* thermal low to high */
DRM_DEBUG("IH: thermal low to high\n");
rdev->pm.dpm.thermal.high_to_low = false;
queue_thermal = true;
break;
case 231: /* thermal high to low */
DRM_DEBUG("IH: thermal high to low\n");
rdev->pm.dpm.thermal.high_to_low = true;
queue_thermal = true;
case 233: /* GUI IDLE */ DRM_DEBUG("IH: GUI idle\n"); break;break;
@@ -4489,6 +4514,8 @@ restart_ih: schedule_work(&rdev->hotplug_work); if (queue_hdmi) schedule_work(&rdev->audio_work);
- if (queue_thermal)
rdev->ih.rptr = rptr; WREG32(IH_RB_RPTR, rdev->ih.rptr); atomic_set(&rdev->ih.lock, 0);schedule_work(&rdev->pm.dpm.thermal.work);
diff --git a/drivers/gpu/drm/radeon/r600_dpm.c b/drivers/gpu/drm/radeon/r600_dpm.c index 91bc5ab..bf396a0 100644 --- a/drivers/gpu/drm/radeon/r600_dpm.c +++ b/drivers/gpu/drm/radeon/r600_dpm.c @@ -676,3 +676,48 @@ bool r600_is_uvd_state(u32 class, u32 class2) return true; return false; }
+int r600_set_thermal_temperature_range(struct radeon_device *rdev,
int min_temp, int max_temp)
+{
- int low_temp = 0 * 1000;
- int high_temp = 255 * 1000;
- if (low_temp < min_temp)
low_temp = min_temp;
- if (high_temp > max_temp)
high_temp = max_temp;
- if (high_temp < low_temp) {
DRM_ERROR("invalid thermal range: %d - %d\n", low_temp, high_temp);
return -EINVAL;
- }
- WREG32_P(CG_THERMAL_INT, DIG_THERM_INTH(high_temp / 1000), ~DIG_THERM_INTH_MASK);
- WREG32_P(CG_THERMAL_INT, DIG_THERM_INTL(low_temp / 1000), ~DIG_THERM_INTL_MASK);
- WREG32_P(CG_THERMAL_CTRL, DIG_THERM_DPM(high_temp / 1000), ~DIG_THERM_DPM_MASK);
- rdev->pm.dpm.thermal.min_temp = low_temp;
- rdev->pm.dpm.thermal.max_temp = high_temp;
- return 0;
+}
+bool r600_is_internal_thermal_sensor(enum radeon_int_thermal_type sensor) +{
- switch (sensor) {
- case THERMAL_TYPE_RV6XX:
- case THERMAL_TYPE_RV770:
- case THERMAL_TYPE_EVERGREEN:
- case THERMAL_TYPE_SUMO:
- case THERMAL_TYPE_NI:
return true;
- case THERMAL_TYPE_ADT7473_WITH_INTERNAL:
- case THERMAL_TYPE_EMC2103_WITH_INTERNAL:
return false; /* need special handling */
- case THERMAL_TYPE_NONE:
- case THERMAL_TYPE_EXTERNAL:
- case THERMAL_TYPE_EXTERNAL_GPIO:
- default:
return false;
- }
+} diff --git a/drivers/gpu/drm/radeon/r600_dpm.h b/drivers/gpu/drm/radeon/r600_dpm.h index 240a7ed..bd33aa1 100644 --- a/drivers/gpu/drm/radeon/r600_dpm.h +++ b/drivers/gpu/drm/radeon/r600_dpm.h @@ -92,6 +92,10 @@ #define R600_PM_NUMBER_OF_VOLTAGE_LEVELS 4 #define R600_PM_NUMBER_OF_ACTIVITY_LEVELS 3
+/* XXX are these ok? */ +#define R600_TEMP_RANGE_MIN (90 * 1000) +#define R600_TEMP_RANGE_MAX (120 * 1000)
- enum r600_power_level { R600_POWER_LEVEL_LOW = 0, R600_POWER_LEVEL_MEDIUM = 1,
@@ -207,4 +211,8 @@ void r600_wait_for_power_level(struct radeon_device *rdev, void r600_start_dpm(struct radeon_device *rdev); void r600_stop_dpm(struct radeon_device *rdev);
+int r600_set_thermal_temperature_range(struct radeon_device *rdev,
int min_temp, int max_temp);
+bool r600_is_internal_thermal_sensor(enum radeon_int_thermal_type sensor);
- #endif
diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h index d6d385a..3bca4db 100644 --- a/drivers/gpu/drm/radeon/r600d.h +++ b/drivers/gpu/drm/radeon/r600d.h @@ -302,10 +302,23 @@ #define GRBM_SOFT_RESET 0x8020 #define SOFT_RESET_CP (1<<0)
+#define CG_THERMAL_CTRL 0x7F0 +#define DIG_THERM_DPM(x) ((x) << 12) +#define DIG_THERM_DPM_MASK 0x000FF000 +#define DIG_THERM_DPM_SHIFT 12 #define CG_THERMAL_STATUS 0x7F4 #define ASIC_T(x) ((x) << 0) #define ASIC_T_MASK 0x1FF #define ASIC_T_SHIFT 0 +#define CG_THERMAL_INT 0x7F8 +#define DIG_THERM_INTH(x) ((x) << 8) +#define DIG_THERM_INTH_MASK 0x0000FF00 +#define DIG_THERM_INTH_SHIFT 8 +#define DIG_THERM_INTL(x) ((x) << 16) +#define DIG_THERM_INTL_MASK 0x00FF0000 +#define DIG_THERM_INTL_SHIFT 16 +#define THERM_INT_MASK_HIGH (1 << 24) +#define THERM_INT_MASK_LOW (1 << 25)
#define HDP_HOST_PATH_CNTL 0x2C00 #define HDP_NONSURFACE_BASE 0x2C04 diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 7adc7a1..ce36130 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -227,6 +227,8 @@ void radeon_atom_set_engine_dram_timings(struct radeon_device *rdev, u32 eng_clock, u32 mem_clock); int radeon_atom_get_voltage_step(struct radeon_device *rdev, u8 voltage_type, u16 *voltage_step); +int radeon_atom_get_max_vddc(struct radeon_device *rdev, u8 voltage_type,
int radeon_atom_round_to_true_voltage(struct radeon_device *rdev, u8 voltage_type, u16 nominal_voltage,u16 voltage_id, u16 *voltage);
@@ -681,6 +683,7 @@ struct radeon_irq { bool hpd[RADEON_MAX_HPD_PINS]; bool afmt[RADEON_MAX_AFMT_BLOCKS]; union radeon_irq_stat_regs stat_regs;
bool dpm_thermal; };
int radeon_irq_kms_init(struct radeon_device *rdev);
diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index db3c930..4705b02 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1105,6 +1105,18 @@ static struct radeon_asic rv6xx_asic = { .set_clock_gating = NULL, .get_temperature = &rv6xx_get_temp, },
- .dpm = {
.init = &rv6xx_dpm_init,
.setup_asic = &rv6xx_setup_asic,
.enable = &rv6xx_dpm_enable,
.disable = &rv6xx_dpm_disable,
.set_power_state = &rv6xx_dpm_set_power_state,
.display_configuration_changed = &rv6xx_dpm_display_configuration_changed,
.fini = &rv6xx_dpm_fini,
.get_sclk = &rv6xx_dpm_get_sclk,
.get_mclk = &rv6xx_dpm_get_mclk,
.print_power_state = &rv6xx_dpm_print_power_state,
- }, .pflip = { .pre_page_flip = &rs600_pre_page_flip, .page_flip = &rs600_page_flip,
diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 134bf57..878766624 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -396,6 +396,18 @@ int r600_mc_wait_for_idle(struct radeon_device *rdev); u32 r600_get_xclk(struct radeon_device *rdev); uint64_t r600_get_gpu_clock_counter(struct radeon_device *rdev); int rv6xx_get_temp(struct radeon_device *rdev); +/* rv6xx dpm */ +int rv6xx_dpm_init(struct radeon_device *rdev); +int rv6xx_dpm_enable(struct radeon_device *rdev); +void rv6xx_dpm_disable(struct radeon_device *rdev); +int rv6xx_dpm_set_power_state(struct radeon_device *rdev); +void rv6xx_setup_asic(struct radeon_device *rdev); +void rv6xx_dpm_display_configuration_changed(struct radeon_device *rdev); +void rv6xx_dpm_fini(struct radeon_device *rdev); +u32 rv6xx_dpm_get_sclk(struct radeon_device *rdev, bool low); +u32 rv6xx_dpm_get_mclk(struct radeon_device *rdev, bool low); +void rv6xx_dpm_print_power_state(struct radeon_device *rdev,
/* rs780 dpm */ int rs780_dpm_init(struct radeon_device *rdev); int rs780_dpm_enable(struct radeon_device *rdev);struct radeon_ps *ps);
diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index 90401fd..612d9bc 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -2268,8 +2268,8 @@ static void radeon_atombios_add_pplib_thermal_controller(struct radeon_device *r } }
-static void radeon_atombios_get_default_voltages(struct radeon_device *rdev,
u16 *vddc, u16 *vddci)
+void radeon_atombios_get_default_voltages(struct radeon_device *rdev,
{ struct radeon_mode_info *mode_info = &rdev->mode_info; int index = GetIndexIntoMasterTable(DATA, FirmwareInfo);u16 *vddc, u16 *vddci)
diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c index dbffeca..bcdefd1 100644 --- a/drivers/gpu/drm/radeon/radeon_irq_kms.c +++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c @@ -116,6 +116,7 @@ void radeon_driver_irq_preinstall_kms(struct drm_device *dev) /* Disable *all* interrupts */ for (i = 0; i < RADEON_NUM_RINGS; i++) atomic_set(&rdev->irq.ring_int[i], 0);
- rdev->irq.dpm_thermal = false; for (i = 0; i < RADEON_MAX_HPD_PINS; i++) rdev->irq.hpd[i] = false; for (i = 0; i < RADEON_MAX_CRTCS; i++) {
@@ -163,6 +164,7 @@ void radeon_driver_irq_uninstall_kms(struct drm_device *dev) /* Disable *all* interrupts */ for (i = 0; i < RADEON_NUM_RINGS; i++) atomic_set(&rdev->irq.ring_int[i], 0);
- rdev->irq.dpm_thermal = false; for (i = 0; i < RADEON_MAX_HPD_PINS; i++) rdev->irq.hpd[i] = false; for (i = 0; i < RADEON_MAX_CRTCS; i++) {
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index 5a1c69e..02bf4a7 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -580,6 +580,8 @@ extern enum radeon_tv_std radeon_combios_get_tv_info(struct radeon_device *rdev); extern enum radeon_tv_std radeon_atombios_get_tv_info(struct radeon_device *rdev); +extern void radeon_atombios_get_default_voltages(struct radeon_device *rdev,
u16 *vddc, u16 *vddci);
extern struct drm_connector * radeon_get_connector_for_encoder(struct drm_encoder *encoder);
diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 853a8a2..5cb01f2 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -565,6 +565,9 @@ static void radeon_dpm_thermal_work_handler(struct work_struct *work) if (!rdev->pm.dpm_enabled) return;
- if (!rdev->pm.dpm_enabled)
return;
Looks like the check is already present here. With that fixed the patch is:
Reviewed-by: Christian König christian.koenig@amd.com
if (rdev->asic->pm.get_temperature) { int temp = radeon_get_temperature(rdev);
@@ -1030,6 +1033,11 @@ int radeon_pm_init(struct radeon_device *rdev) { /* enable dpm on rv6xx+ */ switch (rdev->family) {
- case CHIP_RV610:
- case CHIP_RV630:
- case CHIP_RV620:
- case CHIP_RV635:
- case CHIP_RV670: case CHIP_RS780: case CHIP_RS880: if (radeon_dpm == 1)
@@ -1114,6 +1122,7 @@ static void radeon_pm_compute_clocks_old(struct radeon_device *rdev) if (rdev->pm.num_power_states < 2) return;
INIT_WORK(&rdev->pm.dpm.thermal.work, radeon_dpm_thermal_work_handler); mutex_lock(&rdev->pm.mutex);
rdev->pm.active_crtcs = 0;
diff --git a/drivers/gpu/drm/radeon/rs780_dpm.c b/drivers/gpu/drm/radeon/rs780_dpm.c index f594900..a1497a6 100644 --- a/drivers/gpu/drm/radeon/rs780_dpm.c +++ b/drivers/gpu/drm/radeon/rs780_dpm.c @@ -560,6 +560,12 @@ int rs780_dpm_enable(struct radeon_device *rdev) if (pi->gfx_clock_gating) r600_gfx_clockgating_enable(rdev, true);
- if (rdev->irq.installed && (rdev->pm.int_thermal_type == THERMAL_TYPE_RV6XX)) {
r600_set_thermal_temperature_range(rdev, R600_TEMP_RANGE_MIN, R600_TEMP_RANGE_MAX);
rdev->irq.dpm_thermal = true;
radeon_irq_set(rdev);
- }
- return 0; }
@@ -574,6 +580,12 @@ void rs780_dpm_disable(struct radeon_device *rdev)
if (pi->gfx_clock_gating) r600_gfx_clockgating_enable(rdev, false);
if (rdev->irq.installed &&
(rdev->pm.int_thermal_type == THERMAL_TYPE_RV6XX)) {
rdev->irq.dpm_thermal = false;
radeon_irq_set(rdev);
} }
int rs780_dpm_set_power_state(struct radeon_device *rdev)
diff --git a/drivers/gpu/drm/radeon/rv6xx_dpm.c b/drivers/gpu/drm/radeon/rv6xx_dpm.c new file mode 100644 index 0000000..fa4beb2 --- /dev/null +++ b/drivers/gpu/drm/radeon/rv6xx_dpm.c @@ -0,0 +1,1991 @@ +/*
- Copyright 2011 Advanced Micro Devices, 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: Alex Deucher
- */
+#include "drmP.h" +#include "radeon.h" +#include "rv6xxd.h" +#include "r600_dpm.h" +#include "rv6xx_dpm.h" +#include "atom.h"
+static u32 rv6xx_scale_count_given_unit(struct radeon_device *rdev,
u32 unscaled_count, u32 unit);
+static struct rv6xx_ps *rv6xx_get_ps(struct radeon_ps *rps) +{
- struct rv6xx_ps *ps = rps->ps_priv;
- return ps;
+}
+static struct rv6xx_power_info *rv6xx_get_pi(struct radeon_device *rdev) +{
- struct rv6xx_power_info *pi = rdev->pm.dpm.priv;
- return pi;
+}
+static void rv6xx_force_pcie_gen1(struct radeon_device *rdev) +{
- u32 tmp;
- int i;
- tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL);
- tmp &= LC_GEN2_EN;
- WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp);
- tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL);
- tmp |= LC_INITIATE_LINK_SPEED_CHANGE;
- WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp);
- for (i = 0; i < rdev->usec_timeout; i++) {
if (!(RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL) & LC_CURRENT_DATA_RATE))
break;
udelay(1);
- }
- tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL);
- tmp &= ~LC_INITIATE_LINK_SPEED_CHANGE;
- WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp);
+}
+static void rv6xx_enable_pcie_gen2_support(struct radeon_device *rdev) +{
- u32 tmp;
- tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL);
- if ((tmp & LC_OTHER_SIDE_EVER_SENT_GEN2) &&
(tmp & LC_OTHER_SIDE_SUPPORTS_GEN2)) {
tmp |= LC_GEN2_EN;
WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp);
- }
+}
+static void rv6xx_enable_bif_dynamic_pcie_gen2(struct radeon_device *rdev,
bool enable)
+{
- u32 tmp;
- tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL) & ~LC_HW_VOLTAGE_IF_CONTROL_MASK;
- if (enable)
tmp |= LC_HW_VOLTAGE_IF_CONTROL(1);
- else
tmp |= LC_HW_VOLTAGE_IF_CONTROL(0);
- WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp);
+}
+static void rv6xx_enable_l0s(struct radeon_device *rdev) +{
- u32 tmp;
- tmp = RREG32_PCIE_PORT(PCIE_LC_CNTL) & ~LC_L0S_INACTIVITY_MASK;
- tmp |= LC_L0S_INACTIVITY(3);
- WREG32_PCIE_PORT(PCIE_LC_CNTL, tmp);
+}
+static void rv6xx_enable_l1(struct radeon_device *rdev) +{
- u32 tmp;
- tmp = RREG32_PCIE_PORT(PCIE_LC_CNTL);
- tmp &= ~LC_L1_INACTIVITY_MASK;
- tmp |= LC_L1_INACTIVITY(4);
- tmp &= ~LC_PMI_TO_L1_DIS;
- tmp &= ~LC_ASPM_TO_L1_DIS;
- WREG32_PCIE_PORT(PCIE_LC_CNTL, tmp);
+}
+static void rv6xx_enable_pll_sleep_in_l1(struct radeon_device *rdev) +{
- u32 tmp;
- tmp = RREG32_PCIE_PORT(PCIE_LC_CNTL) & ~LC_L1_INACTIVITY_MASK;
- tmp |= LC_L1_INACTIVITY(8);
- WREG32_PCIE_PORT(PCIE_LC_CNTL, tmp);
- /* NOTE, this is a PCIE indirect reg, not PCIE PORT */
- tmp = RREG32_PCIE(PCIE_P_CNTL);
- tmp |= P_PLL_PWRDN_IN_L1L23;
- tmp &= ~P_PLL_BUF_PDNB;
- tmp &= ~P_PLL_PDNB;
- tmp |= P_ALLOW_PRX_FRONTEND_SHUTOFF;
- WREG32_PCIE(PCIE_P_CNTL, tmp);
+}
+static int rv6xx_convert_clock_to_stepping(struct radeon_device *rdev,
u32 clock, struct rv6xx_sclk_stepping *step)
+{
- int ret;
- struct atom_clock_dividers dividers;
- ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
clock, false, ÷rs);
- if (ret)
return ret;
- if (dividers.enable_post_div)
step->post_divider = 2 + (dividers.post_div & 0xF) + (dividers.post_div >> 4);
- else
step->post_divider = 1;
- step->vco_frequency = clock * step->post_divider;
- return 0;
+}
+static void rv6xx_output_stepping(struct radeon_device *rdev,
u32 step_index, struct rv6xx_sclk_stepping *step)
+{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- u32 ref_clk = rdev->clock.spll.reference_freq;
- u32 fb_divider;
- u32 spll_step_count = rv6xx_scale_count_given_unit(rdev,
R600_SPLLSTEPTIME_DFLT *
pi->spll_ref_div,
R600_SPLLSTEPUNIT_DFLT);
- r600_engine_clock_entry_enable(rdev, step_index, true);
- r600_engine_clock_entry_enable_pulse_skipping(rdev, step_index, false);
- if (step->post_divider == 1)
r600_engine_clock_entry_enable_post_divider(rdev, step_index, false);
- else {
u32 lo_len = (step->post_divider - 2) / 2;
u32 hi_len = step->post_divider - 2 - lo_len;
r600_engine_clock_entry_enable_post_divider(rdev, step_index, true);
r600_engine_clock_entry_set_post_divider(rdev, step_index, (hi_len << 4) | lo_len);
- }
- fb_divider = ((step->vco_frequency * pi->spll_ref_div) / ref_clk) >>
pi->fb_div_scale;
- r600_engine_clock_entry_set_reference_divider(rdev, step_index,
pi->spll_ref_div - 1);
- r600_engine_clock_entry_set_feedback_divider(rdev, step_index, fb_divider);
- r600_engine_clock_entry_set_step_time(rdev, step_index, spll_step_count);
+}
+static struct rv6xx_sclk_stepping rv6xx_next_vco_step(struct radeon_device *rdev,
struct rv6xx_sclk_stepping *cur,
bool increasing_vco, u32 step_size)
+{
- struct rv6xx_sclk_stepping next;
- next.post_divider = cur->post_divider;
- if (increasing_vco)
next.vco_frequency = (cur->vco_frequency * (100 + step_size)) / 100;
- else
next.vco_frequency = (cur->vco_frequency * 100 + 99 + step_size) / (100 + step_size);
- return next;
+}
+static bool rv6xx_can_step_post_div(struct radeon_device *rdev,
struct rv6xx_sclk_stepping *cur,
struct rv6xx_sclk_stepping *target)
+{
- return (cur->post_divider > target->post_divider) &&
((cur->vco_frequency * target->post_divider) <=
(target->vco_frequency * (cur->post_divider - 1)));
+}
+static struct rv6xx_sclk_stepping rv6xx_next_post_div_step(struct radeon_device *rdev,
struct rv6xx_sclk_stepping *cur,
struct rv6xx_sclk_stepping *target)
+{
- struct rv6xx_sclk_stepping next = *cur;
- while (rv6xx_can_step_post_div(rdev, &next, target))
next.post_divider--;
- return next;
+}
+static bool rv6xx_reached_stepping_target(struct radeon_device *rdev,
struct rv6xx_sclk_stepping *cur,
struct rv6xx_sclk_stepping *target,
bool increasing_vco)
+{
- return (increasing_vco && (cur->vco_frequency >= target->vco_frequency)) ||
(!increasing_vco && (cur->vco_frequency <= target->vco_frequency));
+}
+static void rv6xx_generate_steps(struct radeon_device *rdev,
u32 low, u32 high,
u32 start_index, u8 *end_index)
+{
- struct rv6xx_sclk_stepping cur;
- struct rv6xx_sclk_stepping target;
- bool increasing_vco;
- u32 step_index = start_index;
- rv6xx_convert_clock_to_stepping(rdev, low, &cur);
- rv6xx_convert_clock_to_stepping(rdev, high, &target);
- rv6xx_output_stepping(rdev, step_index++, &cur);
- increasing_vco = (target.vco_frequency >= cur.vco_frequency);
- if (target.post_divider > cur.post_divider)
cur.post_divider = target.post_divider;
- while (1) {
struct rv6xx_sclk_stepping next;
if (rv6xx_can_step_post_div(rdev, &cur, &target))
next = rv6xx_next_post_div_step(rdev, &cur, &target);
else
next = rv6xx_next_vco_step(rdev, &cur, increasing_vco, R600_VCOSTEPPCT_DFLT);
if (rv6xx_reached_stepping_target(rdev, &next, &target, increasing_vco)) {
struct rv6xx_sclk_stepping tiny =
rv6xx_next_vco_step(rdev, &target, !increasing_vco, R600_ENDINGVCOSTEPPCT_DFLT);
tiny.post_divider = next.post_divider;
if (!rv6xx_reached_stepping_target(rdev, &tiny, &cur, !increasing_vco))
rv6xx_output_stepping(rdev, step_index++, &tiny);
if ((next.post_divider != target.post_divider) &&
(next.vco_frequency != target.vco_frequency)) {
struct rv6xx_sclk_stepping final_vco;
final_vco.vco_frequency = target.vco_frequency;
final_vco.post_divider = next.post_divider;
rv6xx_output_stepping(rdev, step_index++, &final_vco);
}
rv6xx_output_stepping(rdev, step_index++, &target);
break;
} else
rv6xx_output_stepping(rdev, step_index++, &next);
cur = next;
- }
- *end_index = (u8)step_index - 1;
+}
+static void rv6xx_generate_single_step(struct radeon_device *rdev,
u32 clock, u32 index)
+{
- struct rv6xx_sclk_stepping step;
- rv6xx_convert_clock_to_stepping(rdev, clock, &step);
- rv6xx_output_stepping(rdev, index, &step);
+}
+static void rv6xx_invalidate_intermediate_steps_range(struct radeon_device *rdev,
u32 start_index, u32 end_index)
+{
- u32 step_index;
- for (step_index = start_index + 1; step_index < end_index; step_index++)
r600_engine_clock_entry_enable(rdev, step_index, false);
+}
+static void rv6xx_set_engine_spread_spectrum_clk_s(struct radeon_device *rdev,
u32 index, u32 clk_s)
+{
- WREG32_P(CG_SPLL_SPREAD_SPECTRUM_LOW + (index * 4),
CLKS(clk_s), ~CLKS_MASK);
+}
+static void rv6xx_set_engine_spread_spectrum_clk_v(struct radeon_device *rdev,
u32 index, u32 clk_v)
+{
- WREG32_P(CG_SPLL_SPREAD_SPECTRUM_LOW + (index * 4),
CLKV(clk_v), ~CLKV_MASK);
+}
+static void rv6xx_enable_engine_spread_spectrum(struct radeon_device *rdev,
u32 index, bool enable)
+{
- if (enable)
WREG32_P(CG_SPLL_SPREAD_SPECTRUM_LOW + (index * 4),
SSEN, ~SSEN);
- else
WREG32_P(CG_SPLL_SPREAD_SPECTRUM_LOW + (index * 4),
0, ~SSEN);
+}
+static void rv6xx_set_memory_spread_spectrum_clk_s(struct radeon_device *rdev,
u32 clk_s)
+{
- WREG32_P(CG_MPLL_SPREAD_SPECTRUM, CLKS(clk_s), ~CLKS_MASK);
+}
+static void rv6xx_set_memory_spread_spectrum_clk_v(struct radeon_device *rdev,
u32 clk_v)
+{
- WREG32_P(CG_MPLL_SPREAD_SPECTRUM, CLKV(clk_v), ~CLKV_MASK);
+}
+static void rv6xx_enable_memory_spread_spectrum(struct radeon_device *rdev,
bool enable)
+{
- if (enable)
WREG32_P(CG_MPLL_SPREAD_SPECTRUM, SSEN, ~SSEN);
- else
WREG32_P(CG_MPLL_SPREAD_SPECTRUM, 0, ~SSEN);
+}
+static void rv6xx_enable_dynamic_spread_spectrum(struct radeon_device *rdev,
bool enable)
+{
- if (enable)
WREG32_P(GENERAL_PWRMGT, DYN_SPREAD_SPECTRUM_EN, ~DYN_SPREAD_SPECTRUM_EN);
- else
WREG32_P(GENERAL_PWRMGT, 0, ~DYN_SPREAD_SPECTRUM_EN);
+}
+static void rv6xx_memory_clock_entry_enable_post_divider(struct radeon_device *rdev,
u32 index, bool enable)
+{
- if (enable)
WREG32_P(MPLL_FREQ_LEVEL_0 + (index * 4),
LEVEL0_MPLL_DIV_EN, ~LEVEL0_MPLL_DIV_EN);
- else
WREG32_P(MPLL_FREQ_LEVEL_0 + (index * 4), 0, ~LEVEL0_MPLL_DIV_EN);
+}
+static void rv6xx_memory_clock_entry_set_post_divider(struct radeon_device *rdev,
u32 index, u32 divider)
+{
- WREG32_P(MPLL_FREQ_LEVEL_0 + (index * 4),
LEVEL0_MPLL_POST_DIV(divider), ~LEVEL0_MPLL_POST_DIV_MASK);
+}
+static void rv6xx_memory_clock_entry_set_feedback_divider(struct radeon_device *rdev,
u32 index, u32 divider)
+{
- WREG32_P(MPLL_FREQ_LEVEL_0 + (index * 4), LEVEL0_MPLL_FB_DIV(divider),
~LEVEL0_MPLL_FB_DIV_MASK);
+}
+static void rv6xx_memory_clock_entry_set_reference_divider(struct radeon_device *rdev,
u32 index, u32 divider)
+{
- WREG32_P(MPLL_FREQ_LEVEL_0 + (index * 4),
LEVEL0_MPLL_REF_DIV(divider), ~LEVEL0_MPLL_REF_DIV_MASK);
+}
+static void rv6xx_vid_response_set_brt(struct radeon_device *rdev, u32 rt) +{
- WREG32_P(VID_RT, BRT(rt), ~BRT_MASK);
+}
+static void rv6xx_enable_engine_feedback_and_reference_sync(struct radeon_device *rdev) +{
- WREG32_P(SPLL_CNTL_MODE, SPLL_DIV_SYNC, ~SPLL_DIV_SYNC);
+}
+static u64 rv6xx_clocks_per_unit(u32 unit) +{
- u64 tmp = 1 << (2 * unit);
- return tmp;
+}
+static u32 rv6xx_scale_count_given_unit(struct radeon_device *rdev,
u32 unscaled_count, u32 unit)
+{
- u32 count_per_unit = (u32)rv6xx_clocks_per_unit(unit);
- return (unscaled_count + count_per_unit - 1) / count_per_unit;
+}
+static u32 rv6xx_compute_count_for_delay(struct radeon_device *rdev,
u32 delay_us, u32 unit)
+{
- u32 ref_clk = rdev->clock.spll.reference_freq;
- return rv6xx_scale_count_given_unit(rdev, delay_us * (ref_clk / 100), unit);
+}
+static void rv6xx_calculate_engine_speed_stepping_parameters(struct radeon_device *rdev,
struct rv6xx_ps *state)
+{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- pi->hw.sclks[R600_POWER_LEVEL_LOW] =
state->low.sclk;
- pi->hw.sclks[R600_POWER_LEVEL_MEDIUM] =
state->medium.sclk;
- pi->hw.sclks[R600_POWER_LEVEL_HIGH] =
state->high.sclk;
- pi->hw.low_sclk_index = R600_POWER_LEVEL_LOW;
- pi->hw.medium_sclk_index = R600_POWER_LEVEL_MEDIUM;
- pi->hw.high_sclk_index = R600_POWER_LEVEL_HIGH;
+}
+static void rv6xx_calculate_memory_clock_stepping_parameters(struct radeon_device *rdev,
struct rv6xx_ps *state)
+{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- pi->hw.mclks[R600_POWER_LEVEL_CTXSW] =
state->high.mclk;
- pi->hw.mclks[R600_POWER_LEVEL_HIGH] =
state->high.mclk;
- pi->hw.mclks[R600_POWER_LEVEL_MEDIUM] =
state->medium.mclk;
- pi->hw.mclks[R600_POWER_LEVEL_LOW] =
state->low.mclk;
- pi->hw.high_mclk_index = R600_POWER_LEVEL_HIGH;
- if (state->high.mclk == state->medium.mclk)
pi->hw.medium_mclk_index =
pi->hw.high_mclk_index;
- else
pi->hw.medium_mclk_index = R600_POWER_LEVEL_MEDIUM;
- if (state->medium.mclk == state->low.mclk)
pi->hw.low_mclk_index =
pi->hw.medium_mclk_index;
- else
pi->hw.low_mclk_index = R600_POWER_LEVEL_LOW;
+}
+static void rv6xx_calculate_voltage_stepping_parameters(struct radeon_device *rdev,
struct rv6xx_ps *state)
+{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- pi->hw.vddc[R600_POWER_LEVEL_CTXSW] = state->high.vddc;
- pi->hw.vddc[R600_POWER_LEVEL_HIGH] = state->high.vddc;
- pi->hw.vddc[R600_POWER_LEVEL_MEDIUM] = state->medium.vddc;
- pi->hw.vddc[R600_POWER_LEVEL_LOW] = state->low.vddc;
- pi->hw.backbias[R600_POWER_LEVEL_CTXSW] =
(state->high.flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE) ? true : false;
- pi->hw.backbias[R600_POWER_LEVEL_HIGH] =
(state->high.flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE) ? true : false;
- pi->hw.backbias[R600_POWER_LEVEL_MEDIUM] =
(state->medium.flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE) ? true : false;
- pi->hw.backbias[R600_POWER_LEVEL_LOW] =
(state->low.flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE) ? true : false;
- pi->hw.pcie_gen2[R600_POWER_LEVEL_HIGH] =
(state->high.flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) ? true : false;
- pi->hw.pcie_gen2[R600_POWER_LEVEL_MEDIUM] =
(state->medium.flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) ? true : false;
- pi->hw.pcie_gen2[R600_POWER_LEVEL_LOW] =
(state->low.flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) ? true : false;
- pi->hw.high_vddc_index = R600_POWER_LEVEL_HIGH;
- if ((state->high.vddc == state->medium.vddc) &&
((state->high.flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE) ==
(state->medium.flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE)))
pi->hw.medium_vddc_index =
pi->hw.high_vddc_index;
- else
pi->hw.medium_vddc_index = R600_POWER_LEVEL_MEDIUM;
- if ((state->medium.vddc == state->low.vddc) &&
((state->medium.flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE) ==
(state->low.flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE)))
pi->hw.low_vddc_index =
pi->hw.medium_vddc_index;
- else
pi->hw.medium_vddc_index = R600_POWER_LEVEL_LOW;
+}
+static inline u32 rv6xx_calculate_vco_frequency(u32 ref_clock,
struct atom_clock_dividers *dividers,
u32 fb_divider_scale)
+{
- return ref_clock * ((dividers->fb_div & ~1) << fb_divider_scale) /
(dividers->ref_div + 1);
+}
+static inline u32 rv6xx_calculate_spread_spectrum_clk_v(u32 vco_freq, u32 ref_freq,
u32 ss_rate, u32 ss_percent,
u32 fb_divider_scale)
+{
- u32 fb_divider = vco_freq / ref_freq;
- return (ss_percent * ss_rate * 4 * (fb_divider * fb_divider) /
(5375 * ((vco_freq * 10) / (4096 >> fb_divider_scale))));
+}
+static inline u32 rv6xx_calculate_spread_spectrum_clk_s(u32 ss_rate, u32 ref_freq) +{
- return (((ref_freq * 10) / (ss_rate * 2)) - 1) / 4;
+}
+static void rv6xx_program_engine_spread_spectrum(struct radeon_device *rdev,
u32 clock, enum r600_power_level level)
+{
- u32 ref_clk = rdev->clock.spll.reference_freq;
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- struct atom_clock_dividers dividers;
- struct radeon_atom_ss ss;
- u32 vco_freq, clk_v, clk_s;
- rv6xx_enable_engine_spread_spectrum(rdev, level, false);
- if (clock && pi->sclk_ss) {
if (radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM, clock, false, ÷rs) == 0) {
vco_freq = rv6xx_calculate_vco_frequency(ref_clk, ÷rs,
pi->fb_div_scale);
if (radeon_atombios_get_asic_ss_info(rdev, &ss,
ASIC_INTERNAL_ENGINE_SS, vco_freq)) {
clk_v = rv6xx_calculate_spread_spectrum_clk_v(vco_freq,
(ref_clk / (dividers.ref_div + 1)),
ss.rate,
ss.percentage,
pi->fb_div_scale);
clk_s = rv6xx_calculate_spread_spectrum_clk_s(ss.rate,
(ref_clk / (dividers.ref_div + 1)));
rv6xx_set_engine_spread_spectrum_clk_v(rdev, level, clk_v);
rv6xx_set_engine_spread_spectrum_clk_s(rdev, level, clk_s);
rv6xx_enable_engine_spread_spectrum(rdev, level, true);
}
}
- }
+}
+static void rv6xx_program_sclk_spread_spectrum_parameters_except_lowest_entry(struct radeon_device *rdev) +{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- rv6xx_program_engine_spread_spectrum(rdev,
pi->hw.sclks[R600_POWER_LEVEL_HIGH],
R600_POWER_LEVEL_HIGH);
- rv6xx_program_engine_spread_spectrum(rdev,
pi->hw.sclks[R600_POWER_LEVEL_MEDIUM],
R600_POWER_LEVEL_MEDIUM);
+}
+static int rv6xx_program_mclk_stepping_entry(struct radeon_device *rdev,
u32 entry, u32 clock)
+{
- struct atom_clock_dividers dividers;
- if (radeon_atom_get_clock_dividers(rdev, COMPUTE_MEMORY_PLL_PARAM, clock, false, ÷rs))
return -EINVAL;
- rv6xx_memory_clock_entry_set_reference_divider(rdev, entry, dividers.ref_div);
- rv6xx_memory_clock_entry_set_feedback_divider(rdev, entry, dividers.fb_div);
- rv6xx_memory_clock_entry_set_post_divider(rdev, entry, dividers.post_div);
- if (dividers.enable_post_div)
rv6xx_memory_clock_entry_enable_post_divider(rdev, entry, true);
- else
rv6xx_memory_clock_entry_enable_post_divider(rdev, entry, false);
- return 0;
+}
+static void rv6xx_program_mclk_stepping_parameters_except_lowest_entry(struct radeon_device *rdev) +{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- int i;
- for (i = 1; i < R600_PM_NUMBER_OF_MCLKS; i++) {
if (pi->hw.mclks[i])
rv6xx_program_mclk_stepping_entry(rdev, i,
pi->hw.mclks[i]);
- }
+}
+static void rv6xx_find_memory_clock_with_highest_vco(struct radeon_device *rdev,
u32 requested_memory_clock,
u32 ref_clk,
struct atom_clock_dividers *dividers,
u32 *vco_freq)
+{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- struct atom_clock_dividers req_dividers;
- u32 vco_freq_temp;
- if (radeon_atom_get_clock_dividers(rdev, COMPUTE_MEMORY_PLL_PARAM,
requested_memory_clock, false, &req_dividers) == 0) {
vco_freq_temp = rv6xx_calculate_vco_frequency(ref_clk, &req_dividers,
pi->fb_div_scale);
if (vco_freq_temp > *vco_freq) {
*dividers = req_dividers;
*vco_freq = vco_freq_temp;
}
- }
+}
+static void rv6xx_program_mclk_spread_spectrum_parameters(struct radeon_device *rdev) +{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- u32 ref_clk = rdev->clock.mpll.reference_freq;
- struct atom_clock_dividers dividers;
- struct radeon_atom_ss ss;
- u32 vco_freq = 0, clk_v, clk_s;
- rv6xx_enable_memory_spread_spectrum(rdev, false);
- if (pi->mclk_ss) {
rv6xx_find_memory_clock_with_highest_vco(rdev,
pi->hw.mclks[pi->hw.high_mclk_index],
ref_clk,
÷rs,
&vco_freq);
rv6xx_find_memory_clock_with_highest_vco(rdev,
pi->hw.mclks[pi->hw.medium_mclk_index],
ref_clk,
÷rs,
&vco_freq);
rv6xx_find_memory_clock_with_highest_vco(rdev,
pi->hw.mclks[pi->hw.low_mclk_index],
ref_clk,
÷rs,
&vco_freq);
if (vco_freq) {
if (radeon_atombios_get_asic_ss_info(rdev, &ss,
ASIC_INTERNAL_MEMORY_SS, vco_freq)) {
clk_v = rv6xx_calculate_spread_spectrum_clk_v(vco_freq,
(ref_clk / (dividers.ref_div + 1)),
ss.rate,
ss.percentage,
pi->fb_div_scale);
clk_s = rv6xx_calculate_spread_spectrum_clk_s(ss.rate,
(ref_clk / (dividers.ref_div + 1)));
rv6xx_set_memory_spread_spectrum_clk_v(rdev, clk_v);
rv6xx_set_memory_spread_spectrum_clk_s(rdev, clk_s);
rv6xx_enable_memory_spread_spectrum(rdev, true);
}
}
- }
+}
+static int rv6xx_program_voltage_stepping_entry(struct radeon_device *rdev,
u32 entry, u16 voltage)
+{
- u32 mask, set_pins;
- int ret;
- ret = radeon_atom_get_voltage_gpio_settings(rdev, voltage,
SET_VOLTAGE_TYPE_ASIC_VDDC,
&set_pins, &mask);
- if (ret)
return ret;
- r600_voltage_control_program_voltages(rdev, entry, set_pins);
- return 0;
+}
+static void rv6xx_program_voltage_stepping_parameters_except_lowest_entry(struct radeon_device *rdev) +{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- int i;
- for (i = 1; i < R600_PM_NUMBER_OF_VOLTAGE_LEVELS; i++)
rv6xx_program_voltage_stepping_entry(rdev, i,
pi->hw.vddc[i]);
+}
+static void rv6xx_program_backbias_stepping_parameters_except_lowest_entry(struct radeon_device *rdev) +{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- if (pi->hw.backbias[1])
WREG32_P(VID_UPPER_GPIO_CNTL, MEDIUM_BACKBIAS_VALUE, ~MEDIUM_BACKBIAS_VALUE);
- else
WREG32_P(VID_UPPER_GPIO_CNTL, 0, ~MEDIUM_BACKBIAS_VALUE);
- if (pi->hw.backbias[2])
WREG32_P(VID_UPPER_GPIO_CNTL, HIGH_BACKBIAS_VALUE, ~HIGH_BACKBIAS_VALUE);
- else
WREG32_P(VID_UPPER_GPIO_CNTL, 0, ~HIGH_BACKBIAS_VALUE);
+}
+static void rv6xx_program_sclk_spread_spectrum_parameters_lowest_entry(struct radeon_device *rdev) +{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- rv6xx_program_engine_spread_spectrum(rdev,
pi->hw.sclks[R600_POWER_LEVEL_LOW],
R600_POWER_LEVEL_LOW);
+}
+static void rv6xx_program_mclk_stepping_parameters_lowest_entry(struct radeon_device *rdev) +{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- if (pi->hw.mclks[0])
rv6xx_program_mclk_stepping_entry(rdev, 0,
pi->hw.mclks[0]);
+}
+static void rv6xx_program_voltage_stepping_parameters_lowest_entry(struct radeon_device *rdev) +{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- rv6xx_program_voltage_stepping_entry(rdev, 0,
pi->hw.vddc[0]);
+}
+static void rv6xx_program_backbias_stepping_parameters_lowest_entry(struct radeon_device *rdev) +{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- if (pi->hw.backbias[0])
WREG32_P(VID_UPPER_GPIO_CNTL, LOW_BACKBIAS_VALUE, ~LOW_BACKBIAS_VALUE);
- else
WREG32_P(VID_UPPER_GPIO_CNTL, 0, ~LOW_BACKBIAS_VALUE);
+}
+static u32 calculate_memory_refresh_rate(struct radeon_device *rdev,
u32 engine_clock)
+{
- u32 dram_rows, dram_refresh_rate;
- u32 tmp;
- tmp = (RREG32(RAMCFG) & NOOFROWS_MASK) >> NOOFROWS_SHIFT;
- dram_rows = 1 << (tmp + 10);
- dram_refresh_rate = 1 << ((RREG32(MC_SEQ_RESERVE_M) & 0x3) + 3);
- return ((engine_clock * 10) * dram_refresh_rate / dram_rows - 32) / 64;
+}
+static void rv6xx_program_memory_timing_parameters(struct radeon_device *rdev) +{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- u32 sqm_ratio;
- u32 arb_refresh_rate;
- u32 high_clock;
- if (pi->hw.sclks[R600_POWER_LEVEL_HIGH] <
(pi->hw.sclks[R600_POWER_LEVEL_LOW] * 0xFF / 0x40))
high_clock = pi->hw.sclks[R600_POWER_LEVEL_HIGH];
- else
high_clock =
pi->hw.sclks[R600_POWER_LEVEL_LOW] * 0xFF / 0x40;
- radeon_atom_set_engine_dram_timings(rdev, high_clock, 0);
- sqm_ratio = (STATE0(64 * high_clock / pi->hw.sclks[R600_POWER_LEVEL_LOW]) |
STATE1(64 * high_clock / pi->hw.sclks[R600_POWER_LEVEL_MEDIUM]) |
STATE2(64 * high_clock / pi->hw.sclks[R600_POWER_LEVEL_HIGH]) |
STATE3(64 * high_clock / pi->hw.sclks[R600_POWER_LEVEL_HIGH]));
- WREG32(SQM_RATIO, sqm_ratio);
- arb_refresh_rate =
(POWERMODE0(calculate_memory_refresh_rate(rdev,
pi->hw.sclks[R600_POWER_LEVEL_LOW])) |
POWERMODE1(calculate_memory_refresh_rate(rdev,
pi->hw.sclks[R600_POWER_LEVEL_MEDIUM])) |
POWERMODE2(calculate_memory_refresh_rate(rdev,
pi->hw.sclks[R600_POWER_LEVEL_MEDIUM])) |
POWERMODE3(calculate_memory_refresh_rate(rdev,
pi->hw.sclks[R600_POWER_LEVEL_HIGH])));
- WREG32(ARB_RFSH_RATE, arb_refresh_rate);
+}
+static void rv6xx_program_mpll_timing_parameters(struct radeon_device *rdev) +{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- r600_set_mpll_lock_time(rdev, R600_MPLLLOCKTIME_DFLT *
pi->mpll_ref_div);
- r600_set_mpll_reset_time(rdev, R600_MPLLRESETTIME_DFLT);
+}
+static void rv6xx_program_bsp(struct radeon_device *rdev) +{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- u32 ref_clk = rdev->clock.spll.reference_freq;
- r600_calculate_u_and_p(R600_ASI_DFLT,
ref_clk, 16,
&pi->bsp,
&pi->bsu);
- r600_set_bsp(rdev, pi->bsu, pi->bsp);
+}
+static void rv6xx_program_at(struct radeon_device *rdev) +{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- r600_set_at(rdev,
(pi->hw.rp[0] * pi->bsp) / 200,
(pi->hw.rp[1] * pi->bsp) / 200,
(pi->hw.lp[2] * pi->bsp) / 200,
(pi->hw.lp[1] * pi->bsp) / 200);
+}
+static void rv6xx_program_git(struct radeon_device *rdev) +{
- r600_set_git(rdev, R600_GICST_DFLT);
+}
+static void rv6xx_program_tp(struct radeon_device *rdev) +{
- int i;
- for (i = 0; i < R600_PM_NUMBER_OF_TC; i++)
r600_set_tc(rdev, i, r600_utc[i], r600_dtc[i]);
- r600_select_td(rdev, R600_TD_DFLT);
+}
+static void rv6xx_program_vc(struct radeon_device *rdev) +{
- r600_set_vrc(rdev, R600_VRC_DFLT);
+}
+static void rv6xx_clear_vc(struct radeon_device *rdev) +{
- r600_set_vrc(rdev, 0);
+}
+static void rv6xx_program_tpp(struct radeon_device *rdev) +{
- r600_set_tpu(rdev, R600_TPU_DFLT);
- r600_set_tpc(rdev, R600_TPC_DFLT);
+}
+static void rv6xx_program_sstp(struct radeon_device *rdev) +{
- r600_set_sstu(rdev, R600_SSTU_DFLT);
- r600_set_sst(rdev, R600_SST_DFLT);
+}
+static void rv6xx_program_fcp(struct radeon_device *rdev) +{
- r600_set_fctu(rdev, R600_FCTU_DFLT);
- r600_set_fct(rdev, R600_FCT_DFLT);
+}
+static void rv6xx_program_vddc3d_parameters(struct radeon_device *rdev) +{
- r600_set_vddc3d_oorsu(rdev, R600_VDDC3DOORSU_DFLT);
- r600_set_vddc3d_oorphc(rdev, R600_VDDC3DOORPHC_DFLT);
- r600_set_vddc3d_oorsdc(rdev, R600_VDDC3DOORSDC_DFLT);
- r600_set_ctxcgtt3d_rphc(rdev, R600_CTXCGTT3DRPHC_DFLT);
- r600_set_ctxcgtt3d_rsdc(rdev, R600_CTXCGTT3DRSDC_DFLT);
+}
+static void rv6xx_program_voltage_timing_parameters(struct radeon_device *rdev) +{
- u32 rt;
- r600_vid_rt_set_vru(rdev, R600_VRU_DFLT);
- r600_vid_rt_set_vrt(rdev,
rv6xx_compute_count_for_delay(rdev,
rdev->pm.dpm.voltage_response_time,
R600_VRU_DFLT));
- rt = rv6xx_compute_count_for_delay(rdev,
rdev->pm.dpm.backbias_response_time,
R600_VRU_DFLT);
- rv6xx_vid_response_set_brt(rdev, (rt + 0x1F) >> 5);
+}
+static void rv6xx_program_engine_speed_parameters(struct radeon_device *rdev) +{
- r600_vid_rt_set_ssu(rdev, R600_SPLLSTEPUNIT_DFLT);
- rv6xx_enable_engine_feedback_and_reference_sync(rdev);
+}
+static u64 rv6xx_get_master_voltage_mask(struct radeon_device *rdev) +{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- u64 master_mask = 0;
- int i;
- for (i = 0; i < R600_PM_NUMBER_OF_VOLTAGE_LEVELS; i++) {
u32 tmp_mask, tmp_set_pins;
int ret;
ret = radeon_atom_get_voltage_gpio_settings(rdev,
pi->hw.vddc[i],
SET_VOLTAGE_TYPE_ASIC_VDDC,
&tmp_set_pins, &tmp_mask);
if (ret == 0)
master_mask |= tmp_mask;
- }
- return master_mask;
+}
+static void rv6xx_program_voltage_gpio_pins(struct radeon_device *rdev) +{
- r600_voltage_control_enable_pins(rdev,
rv6xx_get_master_voltage_mask(rdev));
+}
+static void rv6xx_enable_static_voltage_control(struct radeon_device *rdev, bool enable) +{
- struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps);
- if (enable)
radeon_atom_set_voltage(rdev,
new_state->low.vddc,
SET_VOLTAGE_TYPE_ASIC_VDDC);
- else
r600_voltage_control_deactivate_static_control(rdev,
rv6xx_get_master_voltage_mask(rdev));
+}
+static void rv6xx_enable_display_gap(struct radeon_device *rdev, bool enable) +{
- if (enable) {
u32 tmp = (DISP1_GAP(R600_PM_DISPLAY_GAP_VBLANK_OR_WM) |
DISP2_GAP(R600_PM_DISPLAY_GAP_VBLANK_OR_WM) |
DISP1_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE) |
DISP2_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE) |
VBI_TIMER_COUNT(0x3FFF) |
VBI_TIMER_UNIT(7));
WREG32(CG_DISPLAY_GAP_CNTL, tmp);
WREG32_P(MCLK_PWRMGT_CNTL, USE_DISPLAY_GAP, ~USE_DISPLAY_GAP);
- } else
WREG32_P(MCLK_PWRMGT_CNTL, 0, ~USE_DISPLAY_GAP);
+}
+static void rv6xx_program_power_level_enter_state(struct radeon_device *rdev) +{
- r600_power_level_set_enter_index(rdev, R600_POWER_LEVEL_MEDIUM);
+}
+static void rv6xx_calculate_t(u32 l_f, u32 h_f, int h,
int d_l, int d_r, u8 *l, u8 *r)
+{
- int a_n, a_d, h_r, l_r;
- h_r = d_l;
- l_r = 100 - d_r;
- a_n = (int)h_f * d_l + (int)l_f * (h - d_r);
- a_d = (int)l_f * l_r + (int)h_f * h_r;
- if (a_d != 0) {
*l = d_l - h_r * a_n / a_d;
*r = d_r + l_r * a_n / a_d;
- }
+}
+static void rv6xx_calculate_ap(struct radeon_device *rdev,
struct rv6xx_ps *state)
+{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- pi->hw.lp[0] = 0;
- pi->hw.rp[R600_PM_NUMBER_OF_ACTIVITY_LEVELS - 1]
= 100;
- rv6xx_calculate_t(state->low.sclk,
state->medium.sclk,
R600_AH_DFLT,
R600_LMP_DFLT,
R600_RLP_DFLT,
&pi->hw.lp[1],
&pi->hw.rp[0]);
- rv6xx_calculate_t(state->medium.sclk,
state->high.sclk,
R600_AH_DFLT,
R600_LHP_DFLT,
R600_RMP_DFLT,
&pi->hw.lp[2],
&pi->hw.rp[1]);
+}
+static void rv6xx_calculate_stepping_parameters(struct radeon_device *rdev) +{
- struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps);
- rv6xx_calculate_engine_speed_stepping_parameters(rdev, new_state);
- rv6xx_calculate_memory_clock_stepping_parameters(rdev, new_state);
- rv6xx_calculate_voltage_stepping_parameters(rdev, new_state);
- rv6xx_calculate_ap(rdev, new_state);
+}
+static void rv6xx_program_stepping_parameters_except_lowest_entry(struct radeon_device *rdev) +{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- rv6xx_program_mclk_stepping_parameters_except_lowest_entry(rdev);
- if (pi->voltage_control)
rv6xx_program_voltage_stepping_parameters_except_lowest_entry(rdev);
- rv6xx_program_backbias_stepping_parameters_except_lowest_entry(rdev);
- rv6xx_program_sclk_spread_spectrum_parameters_except_lowest_entry(rdev);
- rv6xx_program_mclk_spread_spectrum_parameters(rdev);
- rv6xx_program_memory_timing_parameters(rdev);
+}
+static void rv6xx_program_stepping_parameters_lowest_entry(struct radeon_device *rdev) +{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- rv6xx_program_mclk_stepping_parameters_lowest_entry(rdev);
- if (pi->voltage_control)
rv6xx_program_voltage_stepping_parameters_lowest_entry(rdev);
- rv6xx_program_backbias_stepping_parameters_lowest_entry(rdev);
- rv6xx_program_sclk_spread_spectrum_parameters_lowest_entry(rdev);
+}
+static void rv6xx_program_power_level_low(struct radeon_device *rdev) +{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_LOW,
pi->hw.low_vddc_index);
- r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_LOW,
pi->hw.low_mclk_index);
- r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_LOW,
pi->hw.low_sclk_index);
- r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_LOW,
R600_DISPLAY_WATERMARK_LOW);
- r600_power_level_set_pcie_gen2(rdev, R600_POWER_LEVEL_LOW,
pi->hw.pcie_gen2[R600_POWER_LEVEL_LOW]);
+}
+static void rv6xx_program_power_level_low_to_lowest_state(struct radeon_device *rdev) +{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_LOW, 0);
- r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_LOW, 0);
- r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_LOW, 0);
- r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_LOW,
R600_DISPLAY_WATERMARK_LOW);
- r600_power_level_set_pcie_gen2(rdev, R600_POWER_LEVEL_LOW,
pi->hw.pcie_gen2[R600_POWER_LEVEL_LOW]);
+}
+static void rv6xx_program_power_level_medium(struct radeon_device *rdev) +{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_MEDIUM,
pi->hw.medium_vddc_index);
- r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_MEDIUM,
pi->hw.medium_mclk_index);
- r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_MEDIUM,
pi->hw.medium_sclk_index);
- r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_MEDIUM,
R600_DISPLAY_WATERMARK_LOW);
- r600_power_level_set_pcie_gen2(rdev, R600_POWER_LEVEL_MEDIUM,
pi->hw.pcie_gen2[R600_POWER_LEVEL_MEDIUM]);
+}
+static void rv6xx_program_power_level_medium_for_transition(struct radeon_device *rdev) +{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- rv6xx_program_mclk_stepping_entry(rdev,
R600_POWER_LEVEL_CTXSW,
pi->hw.mclks[pi->hw.low_mclk_index]);
- r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_MEDIUM, 1);
- r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_MEDIUM,
R600_POWER_LEVEL_CTXSW);
- r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_MEDIUM,
pi->hw.medium_sclk_index);
- r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_MEDIUM,
R600_DISPLAY_WATERMARK_LOW);
- rv6xx_enable_engine_spread_spectrum(rdev, R600_POWER_LEVEL_MEDIUM, false);
- r600_power_level_set_pcie_gen2(rdev, R600_POWER_LEVEL_MEDIUM,
pi->hw.pcie_gen2[R600_POWER_LEVEL_LOW]);
+}
+static void rv6xx_program_power_level_high(struct radeon_device *rdev) +{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_HIGH,
pi->hw.high_vddc_index);
- r600_power_level_set_mem_clock_index(rdev, R600_POWER_LEVEL_HIGH,
pi->hw.high_mclk_index);
- r600_power_level_set_eng_clock_index(rdev, R600_POWER_LEVEL_HIGH,
pi->hw.high_sclk_index);
- r600_power_level_set_watermark_id(rdev, R600_POWER_LEVEL_HIGH,
R600_DISPLAY_WATERMARK_HIGH);
- r600_power_level_set_pcie_gen2(rdev, R600_POWER_LEVEL_HIGH,
pi->hw.pcie_gen2[R600_POWER_LEVEL_HIGH]);
+}
+static void rv6xx_enable_backbias(struct radeon_device *rdev, bool enable) +{
- if (enable)
WREG32_P(GENERAL_PWRMGT, BACKBIAS_PAD_EN | BACKBIAS_DPM_CNTL,
~(BACKBIAS_PAD_EN | BACKBIAS_DPM_CNTL));
- else
WREG32_P(GENERAL_PWRMGT, 0,
~(BACKBIAS_VALUE | BACKBIAS_PAD_EN | BACKBIAS_DPM_CNTL));
+}
+static void rv6xx_program_display_gap(struct radeon_device *rdev) +{
- u32 tmp = RREG32(CG_DISPLAY_GAP_CNTL);
- tmp &= ~(DISP1_GAP_MCHG_MASK | DISP2_GAP_MCHG_MASK);
- if (RREG32(AVIVO_D1CRTC_CONTROL) & AVIVO_CRTC_EN) {
tmp |= DISP1_GAP_MCHG(R600_PM_DISPLAY_GAP_VBLANK);
tmp |= DISP2_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE);
- } else if (RREG32(AVIVO_D2CRTC_CONTROL) & AVIVO_CRTC_EN) {
tmp |= DISP1_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE);
tmp |= DISP2_GAP_MCHG(R600_PM_DISPLAY_GAP_VBLANK);
- } else {
tmp |= DISP1_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE);
tmp |= DISP2_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE);
- }
- WREG32(CG_DISPLAY_GAP_CNTL, tmp);
+}
+static void rv6xx_set_sw_voltage_to_safe(struct radeon_device *rdev) +{
- struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps);
- struct rv6xx_ps *old_state = rv6xx_get_ps(rdev->pm.dpm.current_ps);
- u16 safe_voltage;
- safe_voltage = (new_state->low.vddc >= old_state->low.vddc) ?
new_state->low.vddc : old_state->low.vddc;
- rv6xx_program_voltage_stepping_entry(rdev, R600_POWER_LEVEL_CTXSW,
safe_voltage);
- WREG32_P(GENERAL_PWRMGT, SW_GPIO_INDEX(R600_POWER_LEVEL_CTXSW),
~SW_GPIO_INDEX_MASK);
+}
+static void rv6xx_set_sw_voltage_to_low(struct radeon_device *rdev) +{
- struct rv6xx_ps *old_state = rv6xx_get_ps(rdev->pm.dpm.current_ps);
- rv6xx_program_voltage_stepping_entry(rdev, R600_POWER_LEVEL_CTXSW,
old_state->low.vddc);
- WREG32_P(GENERAL_PWRMGT, SW_GPIO_INDEX(R600_POWER_LEVEL_CTXSW),
~SW_GPIO_INDEX_MASK);
+}
+static void rv6xx_set_safe_backbias(struct radeon_device *rdev) +{
- struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps);
- struct rv6xx_ps *old_state = rv6xx_get_ps(rdev->pm.dpm.current_ps);
- if ((new_state->low.flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE) &&
(old_state->low.flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE))
WREG32_P(GENERAL_PWRMGT, BACKBIAS_VALUE, ~BACKBIAS_VALUE);
- else
WREG32_P(GENERAL_PWRMGT, 0, ~BACKBIAS_VALUE);
+}
+static void rv6xx_set_safe_pcie_gen2(struct radeon_device *rdev) +{
- struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps);
- struct rv6xx_ps *old_state = rv6xx_get_ps(rdev->pm.dpm.current_ps);
- if ((new_state->low.flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) !=
(old_state->low.flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2))
rv6xx_force_pcie_gen1(rdev);
+}
+static void rv6xx_enable_dynamic_voltage_control(struct radeon_device *rdev,
bool enable)
+{
- if (enable)
WREG32_P(GENERAL_PWRMGT, VOLT_PWRMGT_EN, ~VOLT_PWRMGT_EN);
- else
WREG32_P(GENERAL_PWRMGT, 0, ~VOLT_PWRMGT_EN);
+}
+static void rv6xx_enable_dynamic_backbias_control(struct radeon_device *rdev,
bool enable)
+{
- if (enable)
WREG32_P(GENERAL_PWRMGT, BACKBIAS_DPM_CNTL, ~BACKBIAS_DPM_CNTL);
- else
WREG32_P(GENERAL_PWRMGT, 0, ~BACKBIAS_DPM_CNTL);
+}
+static int rv6xx_step_sw_voltage(struct radeon_device *rdev,
u16 initial_voltage,
u16 target_voltage)
+{
- u16 current_voltage;
- u16 true_target_voltage;
- u16 voltage_step;
- int signed_voltage_step;
- if ((radeon_atom_get_voltage_step(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC,
&voltage_step)) ||
(radeon_atom_round_to_true_voltage(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC,
initial_voltage, ¤t_voltage)) ||
(radeon_atom_round_to_true_voltage(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC,
target_voltage, &true_target_voltage)))
return -EINVAL;
- if (true_target_voltage < current_voltage)
signed_voltage_step = -(int)voltage_step;
- else
signed_voltage_step = voltage_step;
- while (current_voltage != true_target_voltage) {
current_voltage += signed_voltage_step;
rv6xx_program_voltage_stepping_entry(rdev, R600_POWER_LEVEL_CTXSW,
current_voltage);
msleep((rdev->pm.dpm.voltage_response_time + 999) / 1000);
- }
- return 0;
+}
+static int rv6xx_step_voltage_if_increasing(struct radeon_device *rdev) +{
- struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps);
- struct rv6xx_ps *old_state = rv6xx_get_ps(rdev->pm.dpm.current_ps);
- if (new_state->low.vddc > old_state->low.vddc)
return rv6xx_step_sw_voltage(rdev,
old_state->low.vddc,
new_state->low.vddc);
- return 0;
+}
+static int rv6xx_step_voltage_if_decreasing(struct radeon_device *rdev) +{
- struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps);
- struct rv6xx_ps *old_state = rv6xx_get_ps(rdev->pm.dpm.current_ps);
- if (new_state->low.vddc < old_state->low.vddc)
return rv6xx_step_sw_voltage(rdev,
old_state->low.vddc,
new_state->low.vddc);
- else
return 0;
+}
+static void rv6xx_enable_high(struct radeon_device *rdev) +{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- if ((pi->restricted_levels < 1) ||
(pi->restricted_levels == 3))
r600_power_level_enable(rdev, R600_POWER_LEVEL_HIGH, true);
+}
+static void rv6xx_enable_medium(struct radeon_device *rdev) +{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- if (pi->restricted_levels < 2)
r600_power_level_enable(rdev, R600_POWER_LEVEL_MEDIUM, true);
+}
+static void rv6xx_set_dpm_event_sources(struct radeon_device *rdev, u32 sources) +{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- bool want_thermal_protection;
- enum radeon_dpm_event_src dpm_event_src;
- switch (sources) {
case 0:
default:
want_thermal_protection = false;
break;
case (1 << RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL):
want_thermal_protection = true;
dpm_event_src = RADEON_DPM_EVENT_SRC_DIGITAL;
break;
case (1 << RADEON_DPM_AUTO_THROTTLE_SRC_EXTERNAL):
want_thermal_protection = true;
dpm_event_src = RADEON_DPM_EVENT_SRC_EXTERNAL;
break;
case ((1 << RADEON_DPM_AUTO_THROTTLE_SRC_EXTERNAL) |
(1 << RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL)):
want_thermal_protection = true;
dpm_event_src = RADEON_DPM_EVENT_SRC_DIGIAL_OR_EXTERNAL;
break;
- }
- if (want_thermal_protection) {
WREG32_P(CG_THERMAL_CTRL, DPM_EVENT_SRC(dpm_event_src), ~DPM_EVENT_SRC_MASK);
if (pi->thermal_protection)
WREG32_P(GENERAL_PWRMGT, 0, ~THERMAL_PROTECTION_DIS);
- } else {
WREG32_P(GENERAL_PWRMGT, THERMAL_PROTECTION_DIS, ~THERMAL_PROTECTION_DIS);
- }
+}
+static void rv6xx_enable_auto_throttle_source(struct radeon_device *rdev,
enum radeon_dpm_auto_throttle_src source,
bool enable)
+{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- if (enable) {
if (!(pi->active_auto_throttle_sources & (1 << source))) {
pi->active_auto_throttle_sources |= 1 << source;
rv6xx_set_dpm_event_sources(rdev, pi->active_auto_throttle_sources);
}
- } else {
if (pi->active_auto_throttle_sources & (1 << source)) {
pi->active_auto_throttle_sources &= ~(1 << source);
rv6xx_set_dpm_event_sources(rdev, pi->active_auto_throttle_sources);
}
- }
+}
+static void rv6xx_enable_thermal_protection(struct radeon_device *rdev,
bool enable)
+{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- if (pi->active_auto_throttle_sources)
r600_enable_thermal_protection(rdev, enable);
+}
+static void rv6xx_generate_transition_stepping(struct radeon_device *rdev) +{
- struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps);
- struct rv6xx_ps *old_state = rv6xx_get_ps(rdev->pm.dpm.current_ps);
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- rv6xx_generate_steps(rdev,
old_state->low.sclk,
new_state->low.sclk,
0, &pi->hw.medium_sclk_index);
+}
+static void rv6xx_generate_low_step(struct radeon_device *rdev) +{
- struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps);
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- pi->hw.low_sclk_index = 0;
- rv6xx_generate_single_step(rdev,
new_state->low.sclk,
0);
+}
+static void rv6xx_invalidate_intermediate_steps(struct radeon_device *rdev) +{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- rv6xx_invalidate_intermediate_steps_range(rdev, 0,
pi->hw.medium_sclk_index);
+}
+static void rv6xx_generate_stepping_table(struct radeon_device *rdev) +{
- struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps);
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- pi->hw.low_sclk_index = 0;
- rv6xx_generate_steps(rdev,
new_state->low.sclk,
new_state->medium.sclk,
0,
&pi->hw.medium_sclk_index);
- rv6xx_generate_steps(rdev,
new_state->medium.sclk,
new_state->high.sclk,
pi->hw.medium_sclk_index,
&pi->hw.high_sclk_index);
+}
+static void rv6xx_enable_spread_spectrum(struct radeon_device *rdev,
bool enable)
+{
- if (enable)
rv6xx_enable_dynamic_spread_spectrum(rdev, true);
- else {
rv6xx_enable_engine_spread_spectrum(rdev, R600_POWER_LEVEL_LOW, false);
rv6xx_enable_engine_spread_spectrum(rdev, R600_POWER_LEVEL_MEDIUM, false);
rv6xx_enable_engine_spread_spectrum(rdev, R600_POWER_LEVEL_HIGH, false);
rv6xx_enable_dynamic_spread_spectrum(rdev, false);
rv6xx_enable_memory_spread_spectrum(rdev, false);
- }
+}
+static void rv6xx_reset_lvtm_data_sync(struct radeon_device *rdev) +{
- if (ASIC_IS_DCE3(rdev))
WREG32_P(DCE3_LVTMA_DATA_SYNCHRONIZATION, LVTMA_PFREQCHG, ~LVTMA_PFREQCHG);
- else
WREG32_P(LVTMA_DATA_SYNCHRONIZATION, LVTMA_PFREQCHG, ~LVTMA_PFREQCHG);
+}
+static void rv6xx_enable_dynamic_pcie_gen2(struct radeon_device *rdev,
bool enable)
+{
- struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps);
- if (enable) {
rv6xx_enable_bif_dynamic_pcie_gen2(rdev, true);
rv6xx_enable_pcie_gen2_support(rdev);
r600_enable_dynamic_pcie_gen2(rdev, true);
- } else {
if (!(new_state->low.flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2))
rv6xx_force_pcie_gen1(rdev);
rv6xx_enable_bif_dynamic_pcie_gen2(rdev, false);
r600_enable_dynamic_pcie_gen2(rdev, false);
- }
+}
+int rv6xx_dpm_enable(struct radeon_device *rdev) +{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- if (r600_dynamicpm_enabled(rdev))
return -EINVAL;
- if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_BACKBIAS)
rv6xx_enable_backbias(rdev, true);
- if (pi->dynamic_ss)
rv6xx_enable_spread_spectrum(rdev, true);
- rv6xx_program_mpll_timing_parameters(rdev);
- rv6xx_program_bsp(rdev);
- rv6xx_program_git(rdev);
- rv6xx_program_tp(rdev);
- rv6xx_program_tpp(rdev);
- rv6xx_program_sstp(rdev);
- rv6xx_program_fcp(rdev);
- rv6xx_program_vddc3d_parameters(rdev);
- rv6xx_program_voltage_timing_parameters(rdev);
- rv6xx_program_engine_speed_parameters(rdev);
- rv6xx_enable_display_gap(rdev, true);
- if (pi->display_gap == false)
rv6xx_enable_display_gap(rdev, false);
- rv6xx_program_power_level_enter_state(rdev);
- rv6xx_calculate_stepping_parameters(rdev);
- if (pi->voltage_control)
rv6xx_program_voltage_gpio_pins(rdev);
- rv6xx_generate_stepping_table(rdev);
- rv6xx_program_stepping_parameters_except_lowest_entry(rdev);
- rv6xx_program_stepping_parameters_lowest_entry(rdev);
- rv6xx_program_power_level_low(rdev);
- rv6xx_program_power_level_medium(rdev);
- rv6xx_program_power_level_high(rdev);
- rv6xx_program_vc(rdev);
- rv6xx_program_at(rdev);
- r600_power_level_enable(rdev, R600_POWER_LEVEL_LOW, true);
- r600_power_level_enable(rdev, R600_POWER_LEVEL_MEDIUM, true);
- r600_power_level_enable(rdev, R600_POWER_LEVEL_HIGH, true);
- if (rdev->irq.installed &&
r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) {
r600_set_thermal_temperature_range(rdev, R600_TEMP_RANGE_MIN, R600_TEMP_RANGE_MAX);
rdev->irq.dpm_thermal = true;
radeon_irq_set(rdev);
- }
- rv6xx_enable_auto_throttle_source(rdev, RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL, true);
- r600_start_dpm(rdev);
- if (pi->voltage_control)
rv6xx_enable_static_voltage_control(rdev, false);
- if (pi->dynamic_pcie_gen2)
rv6xx_enable_dynamic_pcie_gen2(rdev, true);
- if (pi->gfx_clock_gating)
r600_gfx_clockgating_enable(rdev, true);
- return 0;
+}
+void rv6xx_dpm_disable(struct radeon_device *rdev) +{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- if (!r600_dynamicpm_enabled(rdev))
return;
- r600_power_level_enable(rdev, R600_POWER_LEVEL_LOW, true);
- r600_power_level_enable(rdev, R600_POWER_LEVEL_MEDIUM, true);
- rv6xx_enable_display_gap(rdev, false);
- rv6xx_clear_vc(rdev);
- r600_set_at(rdev, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF);
- if (pi->thermal_protection)
r600_enable_thermal_protection(rdev, false);
- r600_wait_for_power_level(rdev, R600_POWER_LEVEL_LOW);
- r600_power_level_enable(rdev, R600_POWER_LEVEL_HIGH, false);
- r600_power_level_enable(rdev, R600_POWER_LEVEL_MEDIUM, false);
- if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_BACKBIAS)
rv6xx_enable_backbias(rdev, false);
- rv6xx_enable_spread_spectrum(rdev, false);
- if (pi->voltage_control)
rv6xx_enable_static_voltage_control(rdev, true);
- if (pi->dynamic_pcie_gen2)
rv6xx_enable_dynamic_pcie_gen2(rdev, false);
- if (rdev->irq.installed &&
r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) {
rdev->irq.dpm_thermal = false;
radeon_irq_set(rdev);
- }
- if (pi->gfx_clock_gating)
r600_gfx_clockgating_enable(rdev, false);
- r600_stop_dpm(rdev);
+}
+int rv6xx_dpm_set_power_state(struct radeon_device *rdev) +{
- struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
- rv6xx_clear_vc(rdev);
- r600_power_level_enable(rdev, R600_POWER_LEVEL_LOW, true);
- r600_set_at(rdev, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF);
- if (pi->thermal_protection)
r600_enable_thermal_protection(rdev, false);
- r600_wait_for_power_level(rdev, R600_POWER_LEVEL_LOW);
- r600_power_level_enable(rdev, R600_POWER_LEVEL_HIGH, false);
- r600_power_level_enable(rdev, R600_POWER_LEVEL_MEDIUM, false);
- rv6xx_generate_transition_stepping(rdev);
- rv6xx_program_power_level_medium_for_transition(rdev);
- if (pi->voltage_control) {
rv6xx_set_sw_voltage_to_safe(rdev);
if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_STEPVDDC)
rv6xx_set_sw_voltage_to_low(rdev);
- }
- if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_BACKBIAS)
rv6xx_set_safe_backbias(rdev);
- if (pi->dynamic_pcie_gen2)
rv6xx_set_safe_pcie_gen2(rdev);
- if (pi->voltage_control)
rv6xx_enable_dynamic_voltage_control(rdev, false);
- if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_BACKBIAS)
rv6xx_enable_dynamic_backbias_control(rdev, false);
- if (pi->voltage_control) {
if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_STEPVDDC)
rv6xx_step_voltage_if_increasing(rdev);
msleep((rdev->pm.dpm.voltage_response_time + 999) / 1000);
- }
- r600_power_level_enable(rdev, R600_POWER_LEVEL_MEDIUM, true);
- r600_power_level_enable(rdev, R600_POWER_LEVEL_LOW, false);
- r600_wait_for_power_level_unequal(rdev, R600_POWER_LEVEL_LOW);
- rv6xx_generate_low_step(rdev);
- rv6xx_invalidate_intermediate_steps(rdev);
- rv6xx_calculate_stepping_parameters(rdev);
- rv6xx_program_stepping_parameters_lowest_entry(rdev);
- rv6xx_program_power_level_low_to_lowest_state(rdev);
- r600_power_level_enable(rdev, R600_POWER_LEVEL_LOW, true);
- r600_wait_for_power_level(rdev, R600_POWER_LEVEL_LOW);
- r600_power_level_enable(rdev, R600_POWER_LEVEL_MEDIUM, false);
- if (pi->voltage_control) {
if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_STEPVDDC)
rv6xx_step_voltage_if_decreasing(rdev);
rv6xx_enable_dynamic_voltage_control(rdev, true);
- }
- if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_BACKBIAS)
rv6xx_enable_dynamic_backbias_control(rdev, true);
- if (pi->dynamic_pcie_gen2)
rv6xx_enable_dynamic_pcie_gen2(rdev, true);
- rv6xx_reset_lvtm_data_sync(rdev);
- rv6xx_generate_stepping_table(rdev);
- rv6xx_program_stepping_parameters_except_lowest_entry(rdev);
- rv6xx_program_power_level_low(rdev);
- rv6xx_program_power_level_medium(rdev);
- rv6xx_program_power_level_high(rdev);
- rv6xx_enable_medium(rdev);
- rv6xx_enable_high(rdev);
- if (pi->thermal_protection)
rv6xx_enable_thermal_protection(rdev, true);
- rv6xx_program_vc(rdev);
- rv6xx_program_at(rdev);
- return 0;
+}
+void rv6xx_setup_asic(struct radeon_device *rdev) +{
- r600_enable_acpi_pm(rdev);
- if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_ASPM_L0s)
rv6xx_enable_l0s(rdev);
- if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_ASPM_L1)
rv6xx_enable_l1(rdev);
- if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_TURNOFFPLL_ASPML1)
rv6xx_enable_pll_sleep_in_l1(rdev);
+}
+void rv6xx_dpm_display_configuration_changed(struct radeon_device *rdev) +{
- rv6xx_program_display_gap(rdev);
+}
+union power_info {
- struct _ATOM_POWERPLAY_INFO info;
- struct _ATOM_POWERPLAY_INFO_V2 info_2;
- struct _ATOM_POWERPLAY_INFO_V3 info_3;
- struct _ATOM_PPLIB_POWERPLAYTABLE pplib;
- struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2;
- struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3;
+};
+union pplib_clock_info {
- struct _ATOM_PPLIB_R600_CLOCK_INFO r600;
- struct _ATOM_PPLIB_RS780_CLOCK_INFO rs780;
- struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO evergreen;
- struct _ATOM_PPLIB_SUMO_CLOCK_INFO sumo;
+};
+union pplib_power_state {
- struct _ATOM_PPLIB_STATE v1;
- struct _ATOM_PPLIB_STATE_V2 v2;
+};
+static void rv6xx_parse_pplib_non_clock_info(struct radeon_device *rdev,
struct radeon_ps *rps,
struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info)
+{
- rps->caps = le32_to_cpu(non_clock_info->ulCapsAndSettings);
- rps->class = le16_to_cpu(non_clock_info->usClassification);
- rps->class2 = le16_to_cpu(non_clock_info->usClassification2);
- if (r600_is_uvd_state(rps->class, rps->class2)) {
rps->vclk = RV6XX_DEFAULT_VCLK_FREQ;
rps->dclk = RV6XX_DEFAULT_DCLK_FREQ;
- } else {
rps->vclk = 0;
rps->dclk = 0;
- }
- if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT)
rdev->pm.dpm.boot_ps = rps;
- if (rps->class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE)
rdev->pm.dpm.uvd_ps = rps;
+}
+static void rv6xx_parse_pplib_clock_info(struct radeon_device *rdev,
struct radeon_ps *rps, int index,
union pplib_clock_info *clock_info)
+{
- struct rv6xx_ps *ps = rv6xx_get_ps(rps);
- u32 sclk, mclk;
- u16 vddc;
- struct rv6xx_pl *pl;
- switch (index) {
- case 0:
pl = &ps->low;
break;
- case 1:
pl = &ps->medium;
break;
- case 2:
- default:
pl = &ps->high;
break;
- }
- sclk = le16_to_cpu(clock_info->r600.usEngineClockLow);
- sclk |= clock_info->r600.ucEngineClockHigh << 16;
- mclk = le16_to_cpu(clock_info->r600.usMemoryClockLow);
- mclk |= clock_info->r600.ucMemoryClockHigh << 16;
- pl->mclk = mclk;
- pl->sclk = sclk;
- pl->vddc = le16_to_cpu(clock_info->r600.usVDDC);
- pl->flags = le32_to_cpu(clock_info->r600.ulFlags);
- /* patch up vddc if necessary */
- if (pl->vddc == 0xff01) {
if (radeon_atom_get_max_vddc(rdev, 0, 0, &vddc) == 0)
pl->vddc = vddc;
- }
- /* fix up pcie gen2 */
- if (pl->flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) {
if ((rdev->family == CHIP_RV610) || (rdev->family == CHIP_RV630)) {
if (pl->vddc < 1100)
pl->flags &= ~ATOM_PPLIB_R600_FLAGS_PCIEGEN2;
}
- }
- /* patch up boot state */
- if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT) {
u16 vddc, vddci;
radeon_atombios_get_default_voltages(rdev, &vddc, &vddci);
pl->mclk = rdev->clock.default_mclk;
pl->sclk = rdev->clock.default_sclk;
pl->vddc = vddc;
- }
+}
+static int rv6xx_parse_power_table(struct radeon_device *rdev) +{
- struct radeon_mode_info *mode_info = &rdev->mode_info;
- struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info;
- union pplib_power_state *power_state;
- int i, j;
- union pplib_clock_info *clock_info;
- union power_info *power_info;
- int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo);
u16 data_offset;
- u8 frev, crev;
- struct rv6xx_ps *ps;
- if (!atom_parse_data_header(mode_info->atom_context, index, NULL,
&frev, &crev, &data_offset))
return -EINVAL;
- power_info = (union power_info *)(mode_info->atom_context->bios + data_offset);
- rdev->pm.dpm.ps = kzalloc(sizeof(struct radeon_ps) *
power_info->pplib.ucNumStates, GFP_KERNEL);
- if (!rdev->pm.dpm.ps)
return -ENOMEM;
- rdev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps);
- rdev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime);
- rdev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime);
- for (i = 0; i < power_info->pplib.ucNumStates; i++) {
power_state = (union pplib_power_state *)
(mode_info->atom_context->bios + data_offset +
le16_to_cpu(power_info->pplib.usStateArrayOffset) +
i * power_info->pplib.ucStateEntrySize);
non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *)
(mode_info->atom_context->bios + data_offset +
le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset) +
(power_state->v1.ucNonClockStateIndex *
power_info->pplib.ucNonClockSize));
if (power_info->pplib.ucStateEntrySize - 1) {
ps = kzalloc(sizeof(struct rv6xx_ps), GFP_KERNEL);
if (ps == NULL) {
kfree(rdev->pm.dpm.ps);
return -ENOMEM;
}
rdev->pm.dpm.ps[i].ps_priv = ps;
rv6xx_parse_pplib_non_clock_info(rdev, &rdev->pm.dpm.ps[i],
non_clock_info);
for (j = 0; j < (power_info->pplib.ucStateEntrySize - 1); j++) {
clock_info = (union pplib_clock_info *)
(mode_info->atom_context->bios + data_offset +
le16_to_cpu(power_info->pplib.usClockInfoArrayOffset) +
(power_state->v1.ucClockStateIndices[j] *
power_info->pplib.ucClockInfoSize));
rv6xx_parse_pplib_clock_info(rdev,
&rdev->pm.dpm.ps[i], j,
clock_info);
}
}
- }
- rdev->pm.dpm.num_ps = power_info->pplib.ucNumStates;
- return 0;
+}
+int rv6xx_dpm_init(struct radeon_device *rdev) +{
- int index = GetIndexIntoMasterTable(DATA, ASIC_InternalSS_Info);
- uint16_t data_offset, size;
- uint8_t frev, crev;
- struct atom_clock_dividers dividers;
- struct rv6xx_power_info *pi;
- int ret;
- pi = kzalloc(sizeof(struct rv6xx_power_info), GFP_KERNEL);
- if (pi == NULL)
return -ENOMEM;
- rdev->pm.dpm.priv = pi;
- ret = rv6xx_parse_power_table(rdev);
- if (ret)
return ret;
- if (rdev->pm.dpm.voltage_response_time == 0)
rdev->pm.dpm.voltage_response_time = R600_VOLTAGERESPONSETIME_DFLT;
- if (rdev->pm.dpm.backbias_response_time == 0)
rdev->pm.dpm.backbias_response_time = R600_BACKBIASRESPONSETIME_DFLT;
- ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
0, false, ÷rs);
- if (ret)
pi->spll_ref_div = dividers.ref_div + 1;
- else
pi->spll_ref_div = R600_REFERENCEDIVIDER_DFLT;
- ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_MEMORY_PLL_PARAM,
0, false, ÷rs);
- if (ret)
pi->mpll_ref_div = dividers.ref_div + 1;
- else
pi->mpll_ref_div = R600_REFERENCEDIVIDER_DFLT;
- if (rdev->family >= CHIP_RV670)
pi->fb_div_scale = 1;
- else
pi->fb_div_scale = 0;
- pi->voltage_control =
radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC);
- pi->gfx_clock_gating = true;
- if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size,
&frev, &crev, &data_offset)) {
pi->sclk_ss = true;
pi->mclk_ss = true;
pi->dynamic_ss = true;
- } else {
pi->sclk_ss = false;
pi->mclk_ss = false;
pi->dynamic_ss = false;
- }
- pi->dynamic_pcie_gen2 = true;
- if (pi->gfx_clock_gating &&
(rdev->pm.int_thermal_type != THERMAL_TYPE_NONE))
pi->thermal_protection = true;
- else
pi->thermal_protection = false;
- pi->display_gap = true;
- return 0;
+}
+void rv6xx_dpm_print_power_state(struct radeon_device *rdev,
struct radeon_ps *rps)
+{
- struct rv6xx_ps *ps = rv6xx_get_ps(rps);
- struct rv6xx_pl *pl;
- r600_dpm_print_class_info(rps->class, rps->class2);
- r600_dpm_print_cap_info(rps->caps);
- printk("\tuvd vclk: %d dclk: %d\n", rps->vclk, rps->dclk);
- pl = &ps->low;
- printk("\t\tpower level 0 sclk: %u mclk: %u vddc: %u\n",
pl->sclk, pl->mclk, pl->vddc);
- pl = &ps->medium;
- printk("\t\tpower level 1 sclk: %u mclk: %u vddc: %u\n",
pl->sclk, pl->mclk, pl->vddc);
- pl = &ps->high;
- printk("\t\tpower level 2 sclk: %u mclk: %u vddc: %u\n",
pl->sclk, pl->mclk, pl->vddc);
- r600_dpm_print_ps_status(rdev, rps);
+}
+void rv6xx_dpm_fini(struct radeon_device *rdev) +{
- int i;
- for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
kfree(rdev->pm.dpm.ps[i].ps_priv);
- }
- kfree(rdev->pm.dpm.ps);
- kfree(rdev->pm.dpm.priv);
+}
+u32 rv6xx_dpm_get_sclk(struct radeon_device *rdev, bool low) +{
- struct rv6xx_ps *requested_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps);
- if (low)
return requested_state->low.sclk;
- else
return requested_state->high.sclk;
+}
+u32 rv6xx_dpm_get_mclk(struct radeon_device *rdev, bool low) +{
- struct rv6xx_ps *requested_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps);
- if (low)
return requested_state->low.mclk;
- else
return requested_state->high.mclk;
+} diff --git a/drivers/gpu/drm/radeon/rv6xx_dpm.h b/drivers/gpu/drm/radeon/rv6xx_dpm.h new file mode 100644 index 0000000..8035d53 --- /dev/null +++ b/drivers/gpu/drm/radeon/rv6xx_dpm.h @@ -0,0 +1,95 @@ +/*
- Copyright 2011 Advanced Micro Devices, 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: Alex Deucher
- */
+#ifndef __RV6XX_DPM_H__ +#define __RV6XX_DPM_H__
+#include "r600_dpm.h"
+/* Represents a single SCLK step. */ +struct rv6xx_sclk_stepping +{
- u32 vco_frequency;
- u32 post_divider;
+};
+struct rv6xx_pm_hw_state {
- u32 sclks[R600_PM_NUMBER_OF_ACTIVITY_LEVELS];
- u32 mclks[R600_PM_NUMBER_OF_MCLKS];
- u16 vddc[R600_PM_NUMBER_OF_VOLTAGE_LEVELS];
- bool backbias[R600_PM_NUMBER_OF_VOLTAGE_LEVELS];
- bool pcie_gen2[R600_PM_NUMBER_OF_ACTIVITY_LEVELS];
- u8 high_sclk_index;
- u8 medium_sclk_index;
- u8 low_sclk_index;
- u8 high_mclk_index;
- u8 medium_mclk_index;
- u8 low_mclk_index;
- u8 high_vddc_index;
- u8 medium_vddc_index;
- u8 low_vddc_index;
- u8 rp[R600_PM_NUMBER_OF_ACTIVITY_LEVELS];
- u8 lp[R600_PM_NUMBER_OF_ACTIVITY_LEVELS];
+};
+struct rv6xx_power_info {
- /* flags */
- bool voltage_control;
- bool sclk_ss;
- bool mclk_ss;
- bool dynamic_ss;
- bool dynamic_pcie_gen2;
- bool thermal_protection;
- bool display_gap;
- bool gfx_clock_gating;
- /* clk values */
- u32 fb_div_scale;
- u32 spll_ref_div;
- u32 mpll_ref_div;
- u32 bsu;
- u32 bsp;
- /* */
- u32 active_auto_throttle_sources;
- /* current power state */
- u32 restricted_levels;
- struct rv6xx_pm_hw_state hw;
+};
+struct rv6xx_pl {
- u32 sclk;
- u32 mclk;
- u16 vddc;
- u32 flags;
+};
+struct rv6xx_ps {
- struct rv6xx_pl high;
- struct rv6xx_pl medium;
- struct rv6xx_pl low;
+};
+#define RV6XX_DEFAULT_VCLK_FREQ 40000 /* 10 khz */ +#define RV6XX_DEFAULT_DCLK_FREQ 30000 /* 10 khz */
+#endif diff --git a/drivers/gpu/drm/radeon/rv6xxd.h b/drivers/gpu/drm/radeon/rv6xxd.h new file mode 100644 index 0000000..34e86f9 --- /dev/null +++ b/drivers/gpu/drm/radeon/rv6xxd.h @@ -0,0 +1,246 @@ +/*
- Copyright 2011 Advanced Micro Devices, Inc.
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- OTHER DEALINGS IN THE SOFTWARE.
- */
+#ifndef RV6XXD_H +#define RV6XXD_H
+/* RV6xx power management */ +#define SPLL_CNTL_MODE 0x60c +# define SPLL_DIV_SYNC (1 << 5)
+#define GENERAL_PWRMGT 0x618 +# define GLOBAL_PWRMGT_EN (1 << 0) +# define STATIC_PM_EN (1 << 1) +# define MOBILE_SU (1 << 2) +# define THERMAL_PROTECTION_DIS (1 << 3) +# define THERMAL_PROTECTION_TYPE (1 << 4) +# define ENABLE_GEN2PCIE (1 << 5) +# define SW_GPIO_INDEX(x) ((x) << 6) +# define SW_GPIO_INDEX_MASK (3 << 6) +# define LOW_VOLT_D2_ACPI (1 << 8) +# define LOW_VOLT_D3_ACPI (1 << 9) +# define VOLT_PWRMGT_EN (1 << 10) +# define BACKBIAS_PAD_EN (1 << 16) +# define BACKBIAS_VALUE (1 << 17) +# define BACKBIAS_DPM_CNTL (1 << 18) +# define DYN_SPREAD_SPECTRUM_EN (1 << 21)
+#define MCLK_PWRMGT_CNTL 0x624 +# define MPLL_PWRMGT_OFF (1 << 0) +# define YCLK_TURNOFF (1 << 1) +# define MPLL_TURNOFF (1 << 2) +# define SU_MCLK_USE_BCLK (1 << 3) +# define DLL_READY (1 << 4) +# define MC_BUSY (1 << 5) +# define MC_INT_CNTL (1 << 7) +# define MRDCKA_SLEEP (1 << 8) +# define MRDCKB_SLEEP (1 << 9) +# define MRDCKC_SLEEP (1 << 10) +# define MRDCKD_SLEEP (1 << 11) +# define MRDCKE_SLEEP (1 << 12) +# define MRDCKF_SLEEP (1 << 13) +# define MRDCKG_SLEEP (1 << 14) +# define MRDCKH_SLEEP (1 << 15) +# define MRDCKA_RESET (1 << 16) +# define MRDCKB_RESET (1 << 17) +# define MRDCKC_RESET (1 << 18) +# define MRDCKD_RESET (1 << 19) +# define MRDCKE_RESET (1 << 20) +# define MRDCKF_RESET (1 << 21) +# define MRDCKG_RESET (1 << 22) +# define MRDCKH_RESET (1 << 23) +# define DLL_READY_READ (1 << 24) +# define USE_DISPLAY_GAP (1 << 25) +# define USE_DISPLAY_URGENT_NORMAL (1 << 26) +# define USE_DISPLAY_GAP_CTXSW (1 << 27) +# define MPLL_TURNOFF_D2 (1 << 28) +# define USE_DISPLAY_URGENT_CTXSW (1 << 29)
+#define MPLL_FREQ_LEVEL_0 0x6e8 +# define LEVEL0_MPLL_POST_DIV(x) ((x) << 0) +# define LEVEL0_MPLL_POST_DIV_MASK (0xff << 0) +# define LEVEL0_MPLL_FB_DIV(x) ((x) << 8) +# define LEVEL0_MPLL_FB_DIV_MASK (0xfff << 8) +# define LEVEL0_MPLL_REF_DIV(x) ((x) << 20) +# define LEVEL0_MPLL_REF_DIV_MASK (0x3f << 20) +# define LEVEL0_MPLL_DIV_EN (1 << 28) +# define LEVEL0_DLL_BYPASS (1 << 29) +# define LEVEL0_DLL_RESET (1 << 30)
+#define VID_RT 0x6f8 +# define VID_CRT(x) ((x) << 0) +# define VID_CRT_MASK (0x1fff << 0) +# define VID_CRTU(x) ((x) << 13) +# define VID_CRTU_MASK (7 << 13) +# define SSTU(x) ((x) << 16) +# define SSTU_MASK (7 << 16) +# define VID_SWT(x) ((x) << 19) +# define VID_SWT_MASK (0x1f << 19) +# define BRT(x) ((x) << 24) +# define BRT_MASK (0xff << 24)
+#define TARGET_AND_CURRENT_PROFILE_INDEX 0x70c +# define TARGET_PROFILE_INDEX_MASK (3 << 0) +# define TARGET_PROFILE_INDEX_SHIFT 0 +# define CURRENT_PROFILE_INDEX_MASK (3 << 2) +# define CURRENT_PROFILE_INDEX_SHIFT 2 +# define DYN_PWR_ENTER_INDEX(x) ((x) << 4) +# define DYN_PWR_ENTER_INDEX_MASK (3 << 4) +# define DYN_PWR_ENTER_INDEX_SHIFT 4 +# define CURR_MCLK_INDEX_MASK (3 << 6) +# define CURR_MCLK_INDEX_SHIFT 6 +# define CURR_SCLK_INDEX_MASK (0x1f << 8) +# define CURR_SCLK_INDEX_SHIFT 8 +# define CURR_VID_INDEX_MASK (3 << 13) +# define CURR_VID_INDEX_SHIFT 13
+#define VID_UPPER_GPIO_CNTL 0x740 +# define CTXSW_UPPER_GPIO_VALUES(x) ((x) << 0) +# define CTXSW_UPPER_GPIO_VALUES_MASK (7 << 0) +# define HIGH_UPPER_GPIO_VALUES(x) ((x) << 3) +# define HIGH_UPPER_GPIO_VALUES_MASK (7 << 3) +# define MEDIUM_UPPER_GPIO_VALUES(x) ((x) << 6) +# define MEDIUM_UPPER_GPIO_VALUES_MASK (7 << 6) +# define LOW_UPPER_GPIO_VALUES(x) ((x) << 9) +# define LOW_UPPER_GPIO_VALUES_MASK (7 << 9) +# define CTXSW_BACKBIAS_VALUE (1 << 12) +# define HIGH_BACKBIAS_VALUE (1 << 13) +# define MEDIUM_BACKBIAS_VALUE (1 << 14) +# define LOW_BACKBIAS_VALUE (1 << 15)
+#define CG_DISPLAY_GAP_CNTL 0x7dc +# define DISP1_GAP(x) ((x) << 0) +# define DISP1_GAP_MASK (3 << 0) +# define DISP2_GAP(x) ((x) << 2) +# define DISP2_GAP_MASK (3 << 2) +# define VBI_TIMER_COUNT(x) ((x) << 4) +# define VBI_TIMER_COUNT_MASK (0x3fff << 4) +# define VBI_TIMER_UNIT(x) ((x) << 20) +# define VBI_TIMER_UNIT_MASK (7 << 20) +# define DISP1_GAP_MCHG(x) ((x) << 24) +# define DISP1_GAP_MCHG_MASK (3 << 24) +# define DISP2_GAP_MCHG(x) ((x) << 26) +# define DISP2_GAP_MCHG_MASK (3 << 26)
+#define CG_THERMAL_CTRL 0x7f0 +# define DPM_EVENT_SRC(x) ((x) << 0) +# define DPM_EVENT_SRC_MASK (7 << 0) +# define THERM_INC_CLK (1 << 3) +# define TOFFSET(x) ((x) << 4) +# define TOFFSET_MASK (0xff << 4) +# define DIG_THERM_DPM(x) ((x) << 12) +# define DIG_THERM_DPM_MASK (0xff << 12) +# define CTF_SEL(x) ((x) << 20) +# define CTF_SEL_MASK (7 << 20) +# define CTF_PAD_POLARITY (1 << 23) +# define CTF_PAD_EN (1 << 24)
+#define CG_SPLL_SPREAD_SPECTRUM_LOW 0x820 +# define SSEN (1 << 0) +# define CLKS(x) ((x) << 3) +# define CLKS_MASK (0xff << 3) +# define CLKS_SHIFT 3 +# define CLKV(x) ((x) << 11) +# define CLKV_MASK (0x7ff << 11) +# define CLKV_SHIFT 11 +#define CG_MPLL_SPREAD_SPECTRUM 0x830
+#define CITF_CNTL 0x200c +# define BLACKOUT_RD (1 << 0) +# define BLACKOUT_WR (1 << 1)
+#define RAMCFG 0x2408 +#define NOOFBANK_SHIFT 0 +#define NOOFBANK_MASK 0x00000001 +#define NOOFRANK_SHIFT 1 +#define NOOFRANK_MASK 0x00000002 +#define NOOFROWS_SHIFT 2 +#define NOOFROWS_MASK 0x0000001C +#define NOOFCOLS_SHIFT 5 +#define NOOFCOLS_MASK 0x00000060 +#define CHANSIZE_SHIFT 7 +#define CHANSIZE_MASK 0x00000080 +#define BURSTLENGTH_SHIFT 8 +#define BURSTLENGTH_MASK 0x00000100 +#define CHANSIZE_OVERRIDE (1 << 10)
+#define SQM_RATIO 0x2424 +# define STATE0(x) ((x) << 0) +# define STATE0_MASK (0xff << 0) +# define STATE1(x) ((x) << 8) +# define STATE1_MASK (0xff << 8) +# define STATE2(x) ((x) << 16) +# define STATE2_MASK (0xff << 16) +# define STATE3(x) ((x) << 24) +# define STATE3_MASK (0xff << 24)
+#define ARB_RFSH_CNTL 0x2460 +# define ENABLE (1 << 0) +#define ARB_RFSH_RATE 0x2464 +# define POWERMODE0(x) ((x) << 0) +# define POWERMODE0_MASK (0xff << 0) +# define POWERMODE1(x) ((x) << 8) +# define POWERMODE1_MASK (0xff << 8) +# define POWERMODE2(x) ((x) << 16) +# define POWERMODE2_MASK (0xff << 16) +# define POWERMODE3(x) ((x) << 24) +# define POWERMODE3_MASK (0xff << 24)
+#define MC_SEQ_DRAM 0x2608 +# define CKE_DYN (1 << 12)
+#define MC_SEQ_CMD 0x26c4
+#define MC_SEQ_RESERVE_S 0x2890 +#define MC_SEQ_RESERVE_M 0x2894
+#define LVTMA_DATA_SYNCHRONIZATION 0x7adc +# define LVTMA_PFREQCHG (1 << 8) +#define DCE3_LVTMA_DATA_SYNCHRONIZATION 0x7f98
+/* PCIE indirect regs */ +#define PCIE_P_CNTL 0x40 +# define P_PLL_PWRDN_IN_L1L23 (1 << 3) +# define P_PLL_BUF_PDNB (1 << 4) +# define P_PLL_PDNB (1 << 9) +# define P_ALLOW_PRX_FRONTEND_SHUTOFF (1 << 12) +/* PCIE PORT indirect regs */ +#define PCIE_LC_CNTL 0xa0 +# define LC_L0S_INACTIVITY(x) ((x) << 8) +# define LC_L0S_INACTIVITY_MASK (0xf << 8) +# define LC_L0S_INACTIVITY_SHIFT 8 +# define LC_L1_INACTIVITY(x) ((x) << 12) +# define LC_L1_INACTIVITY_MASK (0xf << 12) +# define LC_L1_INACTIVITY_SHIFT 12 +# define LC_PMI_TO_L1_DIS (1 << 16) +# define LC_ASPM_TO_L1_DIS (1 << 24) +#define PCIE_LC_SPEED_CNTL 0xa4 +# define LC_GEN2_EN (1 << 0) +# define LC_INITIATE_LINK_SPEED_CHANGE (1 << 7) +# define LC_CURRENT_DATA_RATE (1 << 11) +# define LC_HW_VOLTAGE_IF_CONTROL(x) ((x) << 12) +# define LC_HW_VOLTAGE_IF_CONTROL_MASK (3 << 12) +# define LC_HW_VOLTAGE_IF_CONTROL_SHIFT 12 +# define LC_OTHER_SIDE_EVER_SENT_GEN2 (1 << 23) +# define LC_OTHER_SIDE_SUPPORTS_GEN2 (1 << 24)
+#endif
From: Alex Deucher alexander.deucher@amd.com
This adds dpm support for rv7xx asics. This includes: - clockgating - dynamic engine clock scaling - dynamic memory clock scaling - dynamic voltage scaling - dynamic pcie gen1/gen2 switching
Set radeon.dpm=1 to enable.
v2: reduce stack usage
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/Makefile | 3 +- drivers/gpu/drm/radeon/ppsmc.h | 76 ++ drivers/gpu/drm/radeon/r600.c | 48 +- drivers/gpu/drm/radeon/r600d.h | 2 + drivers/gpu/drm/radeon/radeon.h | 1 + drivers/gpu/drm/radeon/radeon_asic.c | 12 + drivers/gpu/drm/radeon/radeon_asic.h | 12 + drivers/gpu/drm/radeon/radeon_pm.c | 4 + drivers/gpu/drm/radeon/radeon_ucode.h | 21 + drivers/gpu/drm/radeon/rv730_dpm.c | 508 +++++++ drivers/gpu/drm/radeon/rv730d.h | 165 +++ drivers/gpu/drm/radeon/rv740_dpm.c | 417 ++++++ drivers/gpu/drm/radeon/rv740d.h | 117 ++ drivers/gpu/drm/radeon/rv770_dpm.c | 2325 +++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/rv770_dpm.h | 272 ++++ drivers/gpu/drm/radeon/rv770_smc.c | 404 ++++++ drivers/gpu/drm/radeon/rv770_smc.h | 208 +++ drivers/gpu/drm/radeon/rv770d.h | 279 ++++- 18 files changed, 4861 insertions(+), 13 deletions(-) create mode 100644 drivers/gpu/drm/radeon/ppsmc.h create mode 100644 drivers/gpu/drm/radeon/rv730_dpm.c create mode 100644 drivers/gpu/drm/radeon/rv730d.h create mode 100644 drivers/gpu/drm/radeon/rv740_dpm.c create mode 100644 drivers/gpu/drm/radeon/rv740d.h create mode 100644 drivers/gpu/drm/radeon/rv770_dpm.c create mode 100644 drivers/gpu/drm/radeon/rv770_dpm.h create mode 100644 drivers/gpu/drm/radeon/rv770_smc.c create mode 100644 drivers/gpu/drm/radeon/rv770_smc.h
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index 3aa20dc..c97753d 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile @@ -77,7 +77,8 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \ evergreen_hdmi.o radeon_trace_points.o ni.o cayman_blit_shaders.o \ atombios_encoders.o radeon_semaphore.o radeon_sa.o atombios_i2c.o si.o \ si_blit_shaders.o radeon_prime.o radeon_uvd.o cik.o cik_blit_shaders.o \ - r600_dpm.o rs780_dpm.o rv6xx_dpm.o + r600_dpm.o rs780_dpm.o rv6xx_dpm.o rv770_dpm.o rv730_dpm.o rv740_dpm.o \ + rv770_smc.o
radeon-$(CONFIG_COMPAT) += radeon_ioc32.o radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o diff --git a/drivers/gpu/drm/radeon/ppsmc.h b/drivers/gpu/drm/radeon/ppsmc.h new file mode 100644 index 0000000..638de17 --- /dev/null +++ b/drivers/gpu/drm/radeon/ppsmc.h @@ -0,0 +1,76 @@ +/* + * Copyright 2011 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ +#ifndef PP_SMC_H +#define PP_SMC_H + +#pragma pack(push, 1) + +#define PPSMC_SWSTATE_FLAG_DC 0x01 + +#define PPSMC_THERMAL_PROTECT_TYPE_INTERNAL 0x00 +#define PPSMC_THERMAL_PROTECT_TYPE_EXTERNAL 0x01 +#define PPSMC_THERMAL_PROTECT_TYPE_NONE 0xff + +#define PPSMC_SYSTEMFLAG_GPIO_DC 0x01 +#define PPSMC_SYSTEMFLAG_STEPVDDC 0x02 +#define PPSMC_SYSTEMFLAG_GDDR5 0x04 +#define PPSMC_SYSTEMFLAG_DISABLE_BABYSTEP 0x08 +#define PPSMC_SYSTEMFLAG_REGULATOR_HOT 0x10 + +#define PPSMC_EXTRAFLAGS_AC2DC_ACTION_MASK 0x07 +#define PPSMC_EXTRAFLAGS_AC2DC_DONT_WAIT_FOR_VBLANK 0x08 +#define PPSMC_EXTRAFLAGS_AC2DC_ACTION_GOTODPMLOWSTATE 0x00 +#define PPSMC_EXTRAFLAGS_AC2DC_ACTION_GOTOINITIALSTATE 0x01 + +#define PPSMC_DISPLAY_WATERMARK_LOW 0 +#define PPSMC_DISPLAY_WATERMARK_HIGH 1 + +#define PPSMC_STATEFLAG_AUTO_PULSE_SKIP 0x01 + +#define PPSMC_Result_OK ((uint8_t)0x01) +#define PPSMC_Result_Failed ((uint8_t)0xFF) + +typedef uint8_t PPSMC_Result; + +#define PPSMC_MSG_Halt ((uint8_t)0x10) +#define PPSMC_MSG_Resume ((uint8_t)0x11) +#define PPSMC_MSG_TwoLevelsDisabled ((uint8_t)0x15) +#define PPSMC_MSG_EnableThermalInterrupt ((uint8_t)0x16) +#define PPSMC_MSG_SwitchToSwState ((uint8_t)0x20) +#define PPSMC_MSG_SwitchToInitialState ((uint8_t)0x40) +#define PPSMC_MSG_NoForcedLevel ((uint8_t)0x41) +#define PPSMC_MSG_SwitchToMinimumPower ((uint8_t)0x51) +#define PPSMC_MSG_ResumeFromMinimumPower ((uint8_t)0x52) +#define PPSMC_MSG_NoDisplay ((uint8_t)0x5D) +#define PPSMC_MSG_HasDisplay ((uint8_t)0x5E) +#define PPSMC_MSG_EnableULV ((uint8_t)0x62) +#define PPSMC_MSG_DisableULV ((uint8_t)0x63) +#define PPSMC_MSG_EnterULV ((uint8_t)0x64) +#define PPSMC_MSG_ExitULV ((uint8_t)0x65) +#define PPSMC_MSG_ResetToDefaults ((uint8_t)0x84) + +typedef uint8_t PPSMC_Msg; + +#pragma pack(pop) + +#endif diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index 7ea81c8..0688016 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -57,10 +57,14 @@ MODULE_FIRMWARE("radeon/RS780_pfp.bin"); MODULE_FIRMWARE("radeon/RS780_me.bin"); MODULE_FIRMWARE("radeon/RV770_pfp.bin"); MODULE_FIRMWARE("radeon/RV770_me.bin"); +MODULE_FIRMWARE("radeon/RV770_smc.bin"); MODULE_FIRMWARE("radeon/RV730_pfp.bin"); MODULE_FIRMWARE("radeon/RV730_me.bin"); +MODULE_FIRMWARE("radeon/RV730_smc.bin"); +MODULE_FIRMWARE("radeon/RV740_smc.bin"); MODULE_FIRMWARE("radeon/RV710_pfp.bin"); MODULE_FIRMWARE("radeon/RV710_me.bin"); +MODULE_FIRMWARE("radeon/RV710_smc.bin"); MODULE_FIRMWARE("radeon/R600_rlc.bin"); MODULE_FIRMWARE("radeon/R700_rlc.bin"); MODULE_FIRMWARE("radeon/CEDAR_pfp.bin"); @@ -2139,7 +2143,8 @@ int r600_init_microcode(struct radeon_device *rdev) struct platform_device *pdev; const char *chip_name; const char *rlc_chip_name; - size_t pfp_req_size, me_req_size, rlc_req_size; + const char *smc_chip_name = "RV770"; + size_t pfp_req_size, me_req_size, rlc_req_size, smc_req_size = 0; char fw_name[30]; int err;
@@ -2185,15 +2190,26 @@ int r600_init_microcode(struct radeon_device *rdev) case CHIP_RV770: chip_name = "RV770"; rlc_chip_name = "R700"; + smc_chip_name = "RV770"; + smc_req_size = ALIGN(RV770_SMC_UCODE_SIZE, 4); break; case CHIP_RV730: - case CHIP_RV740: chip_name = "RV730"; rlc_chip_name = "R700"; + smc_chip_name = "RV730"; + smc_req_size = ALIGN(RV730_SMC_UCODE_SIZE, 4); break; case CHIP_RV710: chip_name = "RV710"; rlc_chip_name = "R700"; + smc_chip_name = "RV710"; + smc_req_size = ALIGN(RV710_SMC_UCODE_SIZE, 4); + break; + case CHIP_RV740: + chip_name = "RV730"; + rlc_chip_name = "R700"; + smc_chip_name = "RV740"; + smc_req_size = ALIGN(RV740_SMC_UCODE_SIZE, 4); break; case CHIP_CEDAR: chip_name = "CEDAR"; @@ -2277,6 +2293,19 @@ int r600_init_microcode(struct radeon_device *rdev) err = -EINVAL; }
+ if ((rdev->family >= CHIP_RV770) && (rdev->family <= CHIP_RV740)) { + snprintf(fw_name, sizeof(fw_name), "radeon/%s_smc.bin", smc_chip_name); + err = request_firmware(&rdev->smc_fw, fw_name, &pdev->dev); + if (err) + goto out; + if (rdev->smc_fw->size != smc_req_size) { + printk(KERN_ERR + "smc: Bogus length %zu in firmware "%s"\n", + rdev->smc_fw->size, fw_name); + err = -EINVAL; + } + } + out: platform_device_unregister(pdev);
@@ -2291,6 +2320,8 @@ out: rdev->me_fw = NULL; release_firmware(rdev->rlc_fw); rdev->rlc_fw = NULL; + release_firmware(rdev->smc_fw); + rdev->smc_fw = NULL; } return err; } @@ -4039,10 +4070,13 @@ int r600_irq_set(struct radeon_device *rdev) if ((rdev->family > CHIP_R600) && (rdev->family < CHIP_RV770)) { thermal_int = RREG32(CG_THERMAL_INT) & ~(THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW); - if (rdev->irq.dpm_thermal) { - DRM_DEBUG("dpm thermal\n"); - thermal_int |= THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW; - } + } else if (rdev->family >= CHIP_RV770) { + thermal_int = RREG32(RV770_CG_THERMAL_INT) & + ~(THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW); + } + if (rdev->irq.dpm_thermal) { + DRM_DEBUG("dpm thermal\n"); + thermal_int |= THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW; }
if (atomic_read(&rdev->irq.ring_int[RADEON_RING_TYPE_GFX_INDEX])) { @@ -4128,6 +4162,8 @@ int r600_irq_set(struct radeon_device *rdev) } if ((rdev->family > CHIP_R600) && (rdev->family < CHIP_RV770)) { WREG32(CG_THERMAL_INT, thermal_int); + } else if (rdev->family >= CHIP_RV770) { + WREG32(RV770_CG_THERMAL_INT, thermal_int); }
return 0; diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h index 3bca4db..f1b3084 100644 --- a/drivers/gpu/drm/radeon/r600d.h +++ b/drivers/gpu/drm/radeon/r600d.h @@ -320,6 +320,8 @@ #define THERM_INT_MASK_HIGH (1 << 24) #define THERM_INT_MASK_LOW (1 << 25)
+#define RV770_CG_THERMAL_INT 0x734 + #define HDP_HOST_PATH_CNTL 0x2C00 #define HDP_NONSURFACE_BASE 0x2C04 #define HDP_NONSURFACE_INFO 0x2C08 diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index ce36130..3e94920 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1914,6 +1914,7 @@ struct radeon_device { const struct firmware *uvd_fw; /* UVD firmware */ const struct firmware *mec_fw; /* CIK MEC firmware */ const struct firmware *sdma_fw; /* CIK SDMA firmware */ + const struct firmware *smc_fw; /* SMC firmware */ struct r600_blit r600_blit; struct r600_vram_scratch vram_scratch; int msi_enabled; /* msi enabled */ diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index 4705b02..dbbcf33 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1317,6 +1317,18 @@ static struct radeon_asic rv770_asic = { .set_uvd_clocks = &rv770_set_uvd_clocks, .get_temperature = &rv770_get_temp, }, + .dpm = { + .init = &rv770_dpm_init, + .setup_asic = &rv770_dpm_setup_asic, + .enable = &rv770_dpm_enable, + .disable = &rv770_dpm_disable, + .set_power_state = &rv770_dpm_set_power_state, + .display_configuration_changed = &rv770_dpm_display_configuration_changed, + .fini = &rv770_dpm_fini, + .get_sclk = &rv770_dpm_get_sclk, + .get_mclk = &rv770_dpm_get_mclk, + .print_power_state = &rv770_dpm_print_power_state, + }, .pflip = { .pre_page_flip = &rs600_pre_page_flip, .page_flip = &rv770_page_flip, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 878766624..f7041a7 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -454,6 +454,18 @@ u32 rv770_get_xclk(struct radeon_device *rdev); int rv770_uvd_resume(struct radeon_device *rdev); int rv770_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk); int rv770_get_temp(struct radeon_device *rdev); +/* rv7xx pm */ +int rv770_dpm_init(struct radeon_device *rdev); +int rv770_dpm_enable(struct radeon_device *rdev); +void rv770_dpm_disable(struct radeon_device *rdev); +int rv770_dpm_set_power_state(struct radeon_device *rdev); +void rv770_dpm_setup_asic(struct radeon_device *rdev); +void rv770_dpm_display_configuration_changed(struct radeon_device *rdev); +void rv770_dpm_fini(struct radeon_device *rdev); +u32 rv770_dpm_get_sclk(struct radeon_device *rdev, bool low); +u32 rv770_dpm_get_mclk(struct radeon_device *rdev, bool low); +void rv770_dpm_print_power_state(struct radeon_device *rdev, + struct radeon_ps *ps);
/* * evergreen diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 5cb01f2..595607c 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -1040,6 +1040,10 @@ int radeon_pm_init(struct radeon_device *rdev) case CHIP_RV670: case CHIP_RS780: case CHIP_RS880: + case CHIP_RV770: + case CHIP_RV730: + case CHIP_RV710: + case CHIP_RV740: if (radeon_dpm == 1) rdev->pm.pm_method = PM_METHOD_DPM; else diff --git a/drivers/gpu/drm/radeon/radeon_ucode.h b/drivers/gpu/drm/radeon/radeon_ucode.h index d2642b0..1910545 100644 --- a/drivers/gpu/drm/radeon/radeon_ucode.h +++ b/drivers/gpu/drm/radeon/radeon_ucode.h @@ -44,4 +44,25 @@ #define BTC_MC_UCODE_SIZE 6024 #define CAYMAN_MC_UCODE_SIZE 6037
+/* SMC */ +#define RV770_SMC_UCODE_START 0x0100 +#define RV770_SMC_UCODE_SIZE 0x410d +#define RV770_SMC_INT_VECTOR_START 0xffc0 +#define RV770_SMC_INT_VECTOR_SIZE 0x0040 + +#define RV730_SMC_UCODE_START 0x0100 +#define RV730_SMC_UCODE_SIZE 0x412c +#define RV730_SMC_INT_VECTOR_START 0xffc0 +#define RV730_SMC_INT_VECTOR_SIZE 0x0040 + +#define RV710_SMC_UCODE_START 0x0100 +#define RV710_SMC_UCODE_SIZE 0x3f1f +#define RV710_SMC_INT_VECTOR_START 0xffc0 +#define RV710_SMC_INT_VECTOR_SIZE 0x0040 + +#define RV740_SMC_UCODE_START 0x0100 +#define RV740_SMC_UCODE_SIZE 0x41c5 +#define RV740_SMC_INT_VECTOR_START 0xffc0 +#define RV740_SMC_INT_VECTOR_SIZE 0x0040 + #endif diff --git a/drivers/gpu/drm/radeon/rv730_dpm.c b/drivers/gpu/drm/radeon/rv730_dpm.c new file mode 100644 index 0000000..b23be71 --- /dev/null +++ b/drivers/gpu/drm/radeon/rv730_dpm.c @@ -0,0 +1,508 @@ +/* + * Copyright 2011 Advanced Micro Devices, 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: Alex Deucher + */ + +#include "drmP.h" +#include "radeon.h" +#include "rv730d.h" +#include "r600_dpm.h" +#include "rv770_dpm.h" +#include "atom.h" + +#define MC_CG_ARB_FREQ_F0 0x0a +#define MC_CG_ARB_FREQ_F1 0x0b +#define MC_CG_ARB_FREQ_F2 0x0c +#define MC_CG_ARB_FREQ_F3 0x0d + +struct rv7xx_ps *rv770_get_ps(struct radeon_ps *rps); +struct rv7xx_power_info *rv770_get_pi(struct radeon_device *rdev); + +int rv730_populate_sclk_value(struct radeon_device *rdev, + u32 engine_clock, + RV770_SMC_SCLK_VALUE *sclk) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct atom_clock_dividers dividers; + u32 spll_func_cntl = pi->clk_regs.rv730.cg_spll_func_cntl; + u32 spll_func_cntl_2 = pi->clk_regs.rv730.cg_spll_func_cntl_2; + u32 spll_func_cntl_3 = pi->clk_regs.rv730.cg_spll_func_cntl_3; + u32 cg_spll_spread_spectrum = pi->clk_regs.rv730.cg_spll_spread_spectrum; + u32 cg_spll_spread_spectrum_2 = pi->clk_regs.rv730.cg_spll_spread_spectrum_2; + u64 tmp; + u32 reference_clock = rdev->clock.spll.reference_freq; + u32 reference_divider, post_divider; + u32 fbdiv; + int ret; + + ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM, + engine_clock, false, ÷rs); + if (ret) + return ret; + + reference_divider = 1 + dividers.ref_div; + + if (dividers.enable_post_div) + post_divider = ((dividers.post_div >> 4) & 0xf) + + (dividers.post_div & 0xf) + 2; + else + post_divider = 1; + + tmp = (u64) engine_clock * reference_divider * post_divider; + + fbdiv = (u32) ((16384 * tmp) / reference_clock); + + /* set up registers */ + if (dividers.enable_post_div) + spll_func_cntl |= SPLL_DIVEN; + else + spll_func_cntl &= ~SPLL_DIVEN; + spll_func_cntl &= ~(SPLL_HILEN_MASK | SPLL_LOLEN_MASK | SPLL_REF_DIV_MASK); + spll_func_cntl |= SPLL_REF_DIV(dividers.ref_div); + spll_func_cntl |= SPLL_HILEN((dividers.post_div >> 4) & 0xf); + spll_func_cntl |= SPLL_LOLEN(dividers.post_div & 0xf); + + spll_func_cntl_2 &= ~SCLK_MUX_SEL_MASK; + spll_func_cntl_2 |= SCLK_MUX_SEL(2); + + spll_func_cntl_3 &= ~SPLL_FB_DIV_MASK; + spll_func_cntl_3 |= SPLL_FB_DIV(fbdiv); + spll_func_cntl_3 |= SPLL_DITHEN; + + if (pi->sclk_ss) { + struct radeon_atom_ss ss; + u32 vco_freq = engine_clock * post_divider; + + if (radeon_atombios_get_asic_ss_info(rdev, &ss, + ASIC_INTERNAL_ENGINE_SS, vco_freq)) { + u32 clk_s = reference_clock * 5 / (reference_divider * ss.rate); + u32 clk_v = ss.percentage * fbdiv / (clk_s * 10000); + + cg_spll_spread_spectrum &= ~CLK_S_MASK; + cg_spll_spread_spectrum |= CLK_S(clk_s); + cg_spll_spread_spectrum |= SSEN; + + cg_spll_spread_spectrum_2 &= ~CLK_V_MASK; + cg_spll_spread_spectrum_2 |= CLK_V(clk_v); + } + } + + sclk->sclk_value = cpu_to_be32(engine_clock); + sclk->vCG_SPLL_FUNC_CNTL = cpu_to_be32(spll_func_cntl); + sclk->vCG_SPLL_FUNC_CNTL_2 = cpu_to_be32(spll_func_cntl_2); + sclk->vCG_SPLL_FUNC_CNTL_3 = cpu_to_be32(spll_func_cntl_3); + sclk->vCG_SPLL_SPREAD_SPECTRUM = cpu_to_be32(cg_spll_spread_spectrum); + sclk->vCG_SPLL_SPREAD_SPECTRUM_2 = cpu_to_be32(cg_spll_spread_spectrum_2); + + return 0; +} + +int rv730_populate_mclk_value(struct radeon_device *rdev, + u32 engine_clock, u32 memory_clock, + LPRV7XX_SMC_MCLK_VALUE mclk) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + u32 mclk_pwrmgt_cntl = pi->clk_regs.rv730.mclk_pwrmgt_cntl; + u32 dll_cntl = pi->clk_regs.rv730.dll_cntl; + u32 mpll_func_cntl = pi->clk_regs.rv730.mpll_func_cntl; + u32 mpll_func_cntl_2 = pi->clk_regs.rv730.mpll_func_cntl2; + u32 mpll_func_cntl_3 = pi->clk_regs.rv730.mpll_func_cntl3; + u32 mpll_ss = pi->clk_regs.rv730.mpll_ss; + u32 mpll_ss2 = pi->clk_regs.rv730.mpll_ss2; + struct atom_clock_dividers dividers; + u32 post_divider, reference_divider; + int ret; + + ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_MEMORY_PLL_PARAM, + memory_clock, false, ÷rs); + if (ret) + return ret; + + reference_divider = dividers.ref_div + 1; + + if (dividers.enable_post_div) + post_divider = ((dividers.post_div >> 4) & 0xf) + + (dividers.post_div & 0xf) + 2; + else + post_divider = 1; + + /* setup the registers */ + if (dividers.enable_post_div) + mpll_func_cntl |= MPLL_DIVEN; + else + mpll_func_cntl &= ~MPLL_DIVEN; + + mpll_func_cntl &= ~(MPLL_REF_DIV_MASK | MPLL_HILEN_MASK | MPLL_LOLEN_MASK); + mpll_func_cntl |= MPLL_REF_DIV(dividers.ref_div); + mpll_func_cntl |= MPLL_HILEN((dividers.post_div >> 4) & 0xf); + mpll_func_cntl |= MPLL_LOLEN(dividers.post_div & 0xf); + + mpll_func_cntl_3 &= ~MPLL_FB_DIV_MASK; + mpll_func_cntl_3 |= MPLL_FB_DIV(dividers.fb_div); + if (dividers.enable_dithen) + mpll_func_cntl_3 |= MPLL_DITHEN; + else + mpll_func_cntl_3 &= ~MPLL_DITHEN; + + if (pi->mclk_ss) { + struct radeon_atom_ss ss; + u32 vco_freq = memory_clock * post_divider; + + if (radeon_atombios_get_asic_ss_info(rdev, &ss, + ASIC_INTERNAL_MEMORY_SS, vco_freq)) { + u32 reference_clock = rdev->clock.mpll.reference_freq; + u32 clk_s = reference_clock * 5 / (reference_divider * ss.rate); + u32 clk_v = ss.percentage * dividers.fb_div / (clk_s * 10000); + + mpll_ss &= ~CLK_S_MASK; + mpll_ss |= CLK_S(clk_s); + mpll_ss |= SSEN; + + mpll_ss2 &= ~CLK_V_MASK; + mpll_ss |= CLK_V(clk_v); + } + } + + + mclk->mclk730.vMCLK_PWRMGT_CNTL = cpu_to_be32(mclk_pwrmgt_cntl); + mclk->mclk730.vDLL_CNTL = cpu_to_be32(dll_cntl); + mclk->mclk730.mclk_value = cpu_to_be32(memory_clock); + mclk->mclk730.vMPLL_FUNC_CNTL = cpu_to_be32(mpll_func_cntl); + mclk->mclk730.vMPLL_FUNC_CNTL2 = cpu_to_be32(mpll_func_cntl_2); + mclk->mclk730.vMPLL_FUNC_CNTL3 = cpu_to_be32(mpll_func_cntl_3); + mclk->mclk730.vMPLL_SS = cpu_to_be32(mpll_ss); + mclk->mclk730.vMPLL_SS2 = cpu_to_be32(mpll_ss2); + + return 0; +} + +void rv730_read_clock_registers(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + + pi->clk_regs.rv730.cg_spll_func_cntl = + RREG32(CG_SPLL_FUNC_CNTL); + pi->clk_regs.rv730.cg_spll_func_cntl_2 = + RREG32(CG_SPLL_FUNC_CNTL_2); + pi->clk_regs.rv730.cg_spll_func_cntl_3 = + RREG32(CG_SPLL_FUNC_CNTL_3); + pi->clk_regs.rv730.cg_spll_spread_spectrum = + RREG32(CG_SPLL_SPREAD_SPECTRUM); + pi->clk_regs.rv730.cg_spll_spread_spectrum_2 = + RREG32(CG_SPLL_SPREAD_SPECTRUM_2); + + pi->clk_regs.rv730.mclk_pwrmgt_cntl = + RREG32(TCI_MCLK_PWRMGT_CNTL); + pi->clk_regs.rv730.dll_cntl = + RREG32(TCI_DLL_CNTL); + pi->clk_regs.rv730.mpll_func_cntl = + RREG32(CG_MPLL_FUNC_CNTL); + pi->clk_regs.rv730.mpll_func_cntl2 = + RREG32(CG_MPLL_FUNC_CNTL_2); + pi->clk_regs.rv730.mpll_func_cntl3 = + RREG32(CG_MPLL_FUNC_CNTL_3); + pi->clk_regs.rv730.mpll_ss = + RREG32(CG_TCI_MPLL_SPREAD_SPECTRUM); + pi->clk_regs.rv730.mpll_ss2 = + RREG32(CG_TCI_MPLL_SPREAD_SPECTRUM_2); +} + +int rv730_populate_smc_acpi_state(struct radeon_device *rdev, + RV770_SMC_STATETABLE *table) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + u32 mpll_func_cntl = 0; + u32 mpll_func_cntl_2 = 0 ; + u32 mpll_func_cntl_3 = 0; + u32 mclk_pwrmgt_cntl; + u32 dll_cntl; + u32 spll_func_cntl; + u32 spll_func_cntl_2; + u32 spll_func_cntl_3; + + table->ACPIState = table->initialState; + table->ACPIState.flags &= ~PPSMC_SWSTATE_FLAG_DC; + + if (pi->acpi_vddc) { + rv770_populate_vddc_value(rdev, pi->acpi_vddc, + &table->ACPIState.levels[0].vddc); + table->ACPIState.levels[0].gen2PCIE = pi->pcie_gen2 ? + pi->acpi_pcie_gen2 : 0; + table->ACPIState.levels[0].gen2XSP = + pi->acpi_pcie_gen2; + } else { + rv770_populate_vddc_value(rdev, pi->min_vddc_in_table, + &table->ACPIState.levels[0].vddc); + table->ACPIState.levels[0].gen2PCIE = 0; + } + + mpll_func_cntl = pi->clk_regs.rv730.mpll_func_cntl; + mpll_func_cntl_2 = pi->clk_regs.rv730.mpll_func_cntl2; + mpll_func_cntl_3 = pi->clk_regs.rv730.mpll_func_cntl3; + + mpll_func_cntl |= MPLL_RESET | MPLL_BYPASS_EN; + mpll_func_cntl &= ~MPLL_SLEEP; + + mpll_func_cntl_2 &= ~MCLK_MUX_SEL_MASK; + mpll_func_cntl_2 |= MCLK_MUX_SEL(1); + + mclk_pwrmgt_cntl = (MRDCKA_RESET | + MRDCKB_RESET | + MRDCKC_RESET | + MRDCKD_RESET | + MRDCKE_RESET | + MRDCKF_RESET | + MRDCKG_RESET | + MRDCKH_RESET | + MRDCKA_SLEEP | + MRDCKB_SLEEP | + MRDCKC_SLEEP | + MRDCKD_SLEEP | + MRDCKE_SLEEP | + MRDCKF_SLEEP | + MRDCKG_SLEEP | + MRDCKH_SLEEP); + + dll_cntl = 0xff000000; + + spll_func_cntl = pi->clk_regs.rv730.cg_spll_func_cntl; + spll_func_cntl_2 = pi->clk_regs.rv730.cg_spll_func_cntl_2; + spll_func_cntl_3 = pi->clk_regs.rv730.cg_spll_func_cntl_3; + + spll_func_cntl |= SPLL_RESET | SPLL_BYPASS_EN; + spll_func_cntl &= ~SPLL_SLEEP; + + spll_func_cntl_2 &= ~SCLK_MUX_SEL_MASK; + spll_func_cntl_2 |= SCLK_MUX_SEL(4); + + table->ACPIState.levels[0].mclk.mclk730.vMPLL_FUNC_CNTL = cpu_to_be32(mpll_func_cntl); + table->ACPIState.levels[0].mclk.mclk730.vMPLL_FUNC_CNTL2 = cpu_to_be32(mpll_func_cntl_2); + table->ACPIState.levels[0].mclk.mclk730.vMPLL_FUNC_CNTL3 = cpu_to_be32(mpll_func_cntl_3); + table->ACPIState.levels[0].mclk.mclk730.vMCLK_PWRMGT_CNTL = cpu_to_be32(mclk_pwrmgt_cntl); + table->ACPIState.levels[0].mclk.mclk730.vDLL_CNTL = cpu_to_be32(dll_cntl); + + table->ACPIState.levels[0].mclk.mclk730.mclk_value = 0; + + table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL = cpu_to_be32(spll_func_cntl); + table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_2 = cpu_to_be32(spll_func_cntl_2); + table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_3 = cpu_to_be32(spll_func_cntl_3); + + table->ACPIState.levels[0].sclk.sclk_value = 0; + + rv770_populate_mvdd_value(rdev, 0, &table->ACPIState.levels[0].mvdd); + + table->ACPIState.levels[1] = table->ACPIState.levels[0]; + table->ACPIState.levels[2] = table->ACPIState.levels[0]; + + return 0; +} + +int rv730_populate_smc_initial_state(struct radeon_device *rdev, + struct radeon_ps *radeon_state, + RV770_SMC_STATETABLE *table) +{ + struct rv7xx_ps *initial_state = rv770_get_ps(radeon_state); + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + u32 a_t; + + table->initialState.levels[0].mclk.mclk730.vMPLL_FUNC_CNTL = + cpu_to_be32(pi->clk_regs.rv730.mpll_func_cntl); + table->initialState.levels[0].mclk.mclk730.vMPLL_FUNC_CNTL2 = + cpu_to_be32(pi->clk_regs.rv730.mpll_func_cntl2); + table->initialState.levels[0].mclk.mclk730.vMPLL_FUNC_CNTL3 = + cpu_to_be32(pi->clk_regs.rv730.mpll_func_cntl3); + table->initialState.levels[0].mclk.mclk730.vMCLK_PWRMGT_CNTL = + cpu_to_be32(pi->clk_regs.rv730.mclk_pwrmgt_cntl); + table->initialState.levels[0].mclk.mclk730.vDLL_CNTL = + cpu_to_be32(pi->clk_regs.rv730.dll_cntl); + table->initialState.levels[0].mclk.mclk730.vMPLL_SS = + cpu_to_be32(pi->clk_regs.rv730.mpll_ss); + table->initialState.levels[0].mclk.mclk730.vMPLL_SS2 = + cpu_to_be32(pi->clk_regs.rv730.mpll_ss2); + + table->initialState.levels[0].mclk.mclk730.mclk_value = + cpu_to_be32(initial_state->low.mclk); + + table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL = + cpu_to_be32(pi->clk_regs.rv730.cg_spll_func_cntl); + table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_2 = + cpu_to_be32(pi->clk_regs.rv730.cg_spll_func_cntl_2); + table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_3 = + cpu_to_be32(pi->clk_regs.rv730.cg_spll_func_cntl_3); + table->initialState.levels[0].sclk.vCG_SPLL_SPREAD_SPECTRUM = + cpu_to_be32(pi->clk_regs.rv730.cg_spll_spread_spectrum); + table->initialState.levels[0].sclk.vCG_SPLL_SPREAD_SPECTRUM_2 = + cpu_to_be32(pi->clk_regs.rv730.cg_spll_spread_spectrum_2); + + table->initialState.levels[0].sclk.sclk_value = + cpu_to_be32(initial_state->low.sclk); + + table->initialState.levels[0].arbValue = MC_CG_ARB_FREQ_F0; + + table->initialState.levels[0].seqValue = + rv770_get_seq_value(rdev, &initial_state->low); + + rv770_populate_vddc_value(rdev, + initial_state->low.vddc, + &table->initialState.levels[0].vddc); + rv770_populate_initial_mvdd_value(rdev, + &table->initialState.levels[0].mvdd); + + a_t = CG_R(0xffff) | CG_L(0); + + table->initialState.levels[0].aT = cpu_to_be32(a_t); + + table->initialState.levels[0].bSP = cpu_to_be32(pi->dsp); + + if (pi->boot_in_gen2) + table->initialState.levels[0].gen2PCIE = 1; + else + table->initialState.levels[0].gen2PCIE = 0; + if (initial_state->low.flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) + table->initialState.levels[0].gen2XSP = 1; + else + table->initialState.levels[0].gen2XSP = 0; + + table->initialState.levels[1] = table->initialState.levels[0]; + table->initialState.levels[2] = table->initialState.levels[0]; + + table->initialState.flags |= PPSMC_SWSTATE_FLAG_DC; + + return 0; +} + +void rv730_program_memory_timing_parameters(struct radeon_device *rdev, + struct radeon_ps *radeon_state) +{ + struct rv7xx_ps *state = rv770_get_ps(radeon_state); + u32 arb_refresh_rate = 0; + u32 dram_timing = 0; + u32 dram_timing2 = 0; + u32 old_dram_timing = 0; + u32 old_dram_timing2 = 0; + + arb_refresh_rate = RREG32(MC_ARB_RFSH_RATE) & + ~(POWERMODE1_MASK | POWERMODE2_MASK | POWERMODE3_MASK); + arb_refresh_rate |= + (POWERMODE1(rv770_calculate_memory_refresh_rate(rdev, state->low.sclk)) | + POWERMODE2(rv770_calculate_memory_refresh_rate(rdev, state->medium.sclk)) | + POWERMODE3(rv770_calculate_memory_refresh_rate(rdev, state->high.sclk))); + WREG32(MC_ARB_RFSH_RATE, arb_refresh_rate); + + /* save the boot dram timings */ + old_dram_timing = RREG32(MC_ARB_DRAM_TIMING); + old_dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2); + + radeon_atom_set_engine_dram_timings(rdev, + state->high.sclk, + state->high.mclk); + + dram_timing = RREG32(MC_ARB_DRAM_TIMING); + dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2); + + WREG32(MC_ARB_DRAM_TIMING_3, dram_timing); + WREG32(MC_ARB_DRAM_TIMING2_3, dram_timing2); + + radeon_atom_set_engine_dram_timings(rdev, + state->medium.sclk, + state->medium.mclk); + + dram_timing = RREG32(MC_ARB_DRAM_TIMING); + dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2); + + WREG32(MC_ARB_DRAM_TIMING_2, dram_timing); + WREG32(MC_ARB_DRAM_TIMING2_2, dram_timing2); + + radeon_atom_set_engine_dram_timings(rdev, + state->low.sclk, + state->low.mclk); + + dram_timing = RREG32(MC_ARB_DRAM_TIMING); + dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2); + + WREG32(MC_ARB_DRAM_TIMING_1, dram_timing); + WREG32(MC_ARB_DRAM_TIMING2_1, dram_timing2); + + /* restore the boot dram timings */ + WREG32(MC_ARB_DRAM_TIMING, old_dram_timing); + WREG32(MC_ARB_DRAM_TIMING2, old_dram_timing2); + +} + +void rv730_start_dpm(struct radeon_device *rdev) +{ + WREG32_P(SCLK_PWRMGT_CNTL, 0, ~SCLK_PWRMGT_OFF); + + WREG32_P(TCI_MCLK_PWRMGT_CNTL, 0, ~MPLL_PWRMGT_OFF); + + WREG32_P(GENERAL_PWRMGT, GLOBAL_PWRMGT_EN, ~GLOBAL_PWRMGT_EN); +} + +void rv730_stop_dpm(struct radeon_device *rdev) +{ + PPSMC_Result result; + + result = rv770_send_msg_to_smc(rdev, PPSMC_MSG_TwoLevelsDisabled); + + if (result != PPSMC_Result_OK) + DRM_ERROR("Could not force DPM to low\n"); + + WREG32_P(GENERAL_PWRMGT, 0, ~GLOBAL_PWRMGT_EN); + + WREG32_P(SCLK_PWRMGT_CNTL, SCLK_PWRMGT_OFF, ~SCLK_PWRMGT_OFF); + + WREG32_P(TCI_MCLK_PWRMGT_CNTL, MPLL_PWRMGT_OFF, ~MPLL_PWRMGT_OFF); +} + +void rv730_program_dcodt(struct radeon_device *rdev, bool use_dcodt) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + u32 i = use_dcodt ? 0 : 1; + u32 mc4_io_pad_cntl; + + mc4_io_pad_cntl = RREG32(MC4_IO_DQ_PAD_CNTL_D0_I0); + mc4_io_pad_cntl &= 0xFFFFFF00; + mc4_io_pad_cntl |= pi->odt_value_0[i]; + WREG32(MC4_IO_DQ_PAD_CNTL_D0_I0, mc4_io_pad_cntl); + WREG32(MC4_IO_DQ_PAD_CNTL_D0_I1, mc4_io_pad_cntl); + + mc4_io_pad_cntl = RREG32(MC4_IO_QS_PAD_CNTL_D0_I0); + mc4_io_pad_cntl &= 0xFFFFFF00; + mc4_io_pad_cntl |= pi->odt_value_1[i]; + WREG32(MC4_IO_QS_PAD_CNTL_D0_I0, mc4_io_pad_cntl); + WREG32(MC4_IO_QS_PAD_CNTL_D0_I1, mc4_io_pad_cntl); +} + +void rv730_get_odt_values(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + u32 mc4_io_pad_cntl; + + pi->odt_value_0[0] = (u8)0; + pi->odt_value_1[0] = (u8)0x80; + + mc4_io_pad_cntl = RREG32(MC4_IO_DQ_PAD_CNTL_D0_I0); + pi->odt_value_0[1] = (u8)(mc4_io_pad_cntl & 0xff); + + mc4_io_pad_cntl = RREG32(MC4_IO_QS_PAD_CNTL_D0_I0); + pi->odt_value_1[1] = (u8)(mc4_io_pad_cntl & 0xff); +} diff --git a/drivers/gpu/drm/radeon/rv730d.h b/drivers/gpu/drm/radeon/rv730d.h new file mode 100644 index 0000000..f0a7954 --- /dev/null +++ b/drivers/gpu/drm/radeon/rv730d.h @@ -0,0 +1,165 @@ +/* + * Copyright 2011 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ +#ifndef RV730_H +#define RV730_H + +#define CG_SPLL_FUNC_CNTL 0x600 +#define SPLL_RESET (1 << 0) +#define SPLL_SLEEP (1 << 1) +#define SPLL_DIVEN (1 << 2) +#define SPLL_BYPASS_EN (1 << 3) +#define SPLL_REF_DIV(x) ((x) << 4) +#define SPLL_REF_DIV_MASK (0x3f << 4) +#define SPLL_HILEN(x) ((x) << 12) +#define SPLL_HILEN_MASK (0xf << 12) +#define SPLL_LOLEN(x) ((x) << 16) +#define SPLL_LOLEN_MASK (0xf << 16) +#define CG_SPLL_FUNC_CNTL_2 0x604 +#define SCLK_MUX_SEL(x) ((x) << 0) +#define SCLK_MUX_SEL_MASK (0x1ff << 0) +#define CG_SPLL_FUNC_CNTL_3 0x608 +#define SPLL_FB_DIV(x) ((x) << 0) +#define SPLL_FB_DIV_MASK (0x3ffffff << 0) +#define SPLL_DITHEN (1 << 28) + +#define CG_MPLL_FUNC_CNTL 0x624 +#define MPLL_RESET (1 << 0) +#define MPLL_SLEEP (1 << 1) +#define MPLL_DIVEN (1 << 2) +#define MPLL_BYPASS_EN (1 << 3) +#define MPLL_REF_DIV(x) ((x) << 4) +#define MPLL_REF_DIV_MASK (0x3f << 4) +#define MPLL_HILEN(x) ((x) << 12) +#define MPLL_HILEN_MASK (0xf << 12) +#define MPLL_LOLEN(x) ((x) << 16) +#define MPLL_LOLEN_MASK (0xf << 16) +#define CG_MPLL_FUNC_CNTL_2 0x628 +#define MCLK_MUX_SEL(x) ((x) << 0) +#define MCLK_MUX_SEL_MASK (0x1ff << 0) +#define CG_MPLL_FUNC_CNTL_3 0x62c +#define MPLL_FB_DIV(x) ((x) << 0) +#define MPLL_FB_DIV_MASK (0x3ffffff << 0) +#define MPLL_DITHEN (1 << 28) + +#define CG_TCI_MPLL_SPREAD_SPECTRUM 0x634 +#define CG_TCI_MPLL_SPREAD_SPECTRUM_2 0x638 +#define GENERAL_PWRMGT 0x63c +# define GLOBAL_PWRMGT_EN (1 << 0) +# define STATIC_PM_EN (1 << 1) +# define THERMAL_PROTECTION_DIS (1 << 2) +# define THERMAL_PROTECTION_TYPE (1 << 3) +# define ENABLE_GEN2PCIE (1 << 4) +# define ENABLE_GEN2XSP (1 << 5) +# define SW_SMIO_INDEX(x) ((x) << 6) +# define SW_SMIO_INDEX_MASK (3 << 6) +# define LOW_VOLT_D2_ACPI (1 << 8) +# define LOW_VOLT_D3_ACPI (1 << 9) +# define VOLT_PWRMGT_EN (1 << 10) +# define BACKBIAS_PAD_EN (1 << 18) +# define BACKBIAS_VALUE (1 << 19) +# define DYN_SPREAD_SPECTRUM_EN (1 << 23) +# define AC_DC_SW (1 << 24) + +#define SCLK_PWRMGT_CNTL 0x644 +# define SCLK_PWRMGT_OFF (1 << 0) +# define SCLK_LOW_D1 (1 << 1) +# define FIR_RESET (1 << 4) +# define FIR_FORCE_TREND_SEL (1 << 5) +# define FIR_TREND_MODE (1 << 6) +# define DYN_GFX_CLK_OFF_EN (1 << 7) +# define GFX_CLK_FORCE_ON (1 << 8) +# define GFX_CLK_REQUEST_OFF (1 << 9) +# define GFX_CLK_FORCE_OFF (1 << 10) +# define GFX_CLK_OFF_ACPI_D1 (1 << 11) +# define GFX_CLK_OFF_ACPI_D2 (1 << 12) +# define GFX_CLK_OFF_ACPI_D3 (1 << 13) + +#define TCI_MCLK_PWRMGT_CNTL 0x648 +# define MPLL_PWRMGT_OFF (1 << 5) +# define DLL_READY (1 << 6) +# define MC_INT_CNTL (1 << 7) +# define MRDCKA_SLEEP (1 << 8) +# define MRDCKB_SLEEP (1 << 9) +# define MRDCKC_SLEEP (1 << 10) +# define MRDCKD_SLEEP (1 << 11) +# define MRDCKE_SLEEP (1 << 12) +# define MRDCKF_SLEEP (1 << 13) +# define MRDCKG_SLEEP (1 << 14) +# define MRDCKH_SLEEP (1 << 15) +# define MRDCKA_RESET (1 << 16) +# define MRDCKB_RESET (1 << 17) +# define MRDCKC_RESET (1 << 18) +# define MRDCKD_RESET (1 << 19) +# define MRDCKE_RESET (1 << 20) +# define MRDCKF_RESET (1 << 21) +# define MRDCKG_RESET (1 << 22) +# define MRDCKH_RESET (1 << 23) +# define DLL_READY_READ (1 << 24) +# define USE_DISPLAY_GAP (1 << 25) +# define USE_DISPLAY_URGENT_NORMAL (1 << 26) +# define MPLL_TURNOFF_D2 (1 << 28) +#define TCI_DLL_CNTL 0x64c + +#define CG_PG_CNTL 0x858 +# define PWRGATE_ENABLE (1 << 0) + +#define CG_AT 0x6d4 +#define CG_R(x) ((x) << 0) +#define CG_R_MASK (0xffff << 0) +#define CG_L(x) ((x) << 16) +#define CG_L_MASK (0xffff << 16) + +#define CG_SPLL_SPREAD_SPECTRUM 0x790 +#define SSEN (1 << 0) +#define CLK_S(x) ((x) << 4) +#define CLK_S_MASK (0xfff << 4) +#define CG_SPLL_SPREAD_SPECTRUM_2 0x794 +#define CLK_V(x) ((x) << 0) +#define CLK_V_MASK (0x3ffffff << 0) + +#define MC_ARB_DRAM_TIMING 0x2774 +#define MC_ARB_DRAM_TIMING2 0x2778 + +#define MC_ARB_RFSH_RATE 0x27b0 +#define POWERMODE0(x) ((x) << 0) +#define POWERMODE0_MASK (0xff << 0) +#define POWERMODE1(x) ((x) << 8) +#define POWERMODE1_MASK (0xff << 8) +#define POWERMODE2(x) ((x) << 16) +#define POWERMODE2_MASK (0xff << 16) +#define POWERMODE3(x) ((x) << 24) +#define POWERMODE3_MASK (0xff << 24) + +#define MC_ARB_DRAM_TIMING_1 0x27f0 +#define MC_ARB_DRAM_TIMING_2 0x27f4 +#define MC_ARB_DRAM_TIMING_3 0x27f8 +#define MC_ARB_DRAM_TIMING2_1 0x27fc +#define MC_ARB_DRAM_TIMING2_2 0x2800 +#define MC_ARB_DRAM_TIMING2_3 0x2804 + +#define MC4_IO_DQ_PAD_CNTL_D0_I0 0x2978 +#define MC4_IO_DQ_PAD_CNTL_D0_I1 0x297c +#define MC4_IO_QS_PAD_CNTL_D0_I0 0x2980 +#define MC4_IO_QS_PAD_CNTL_D0_I1 0x2984 + +#endif diff --git a/drivers/gpu/drm/radeon/rv740_dpm.c b/drivers/gpu/drm/radeon/rv740_dpm.c new file mode 100644 index 0000000..d0d750e --- /dev/null +++ b/drivers/gpu/drm/radeon/rv740_dpm.c @@ -0,0 +1,417 @@ +/* + * Copyright 2011 Advanced Micro Devices, 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: Alex Deucher + */ + +#include "drmP.h" +#include "radeon.h" +#include "rv740d.h" +#include "r600_dpm.h" +#include "rv770_dpm.h" +#include "atom.h" + +struct rv7xx_ps *rv770_get_ps(struct radeon_ps *rps); +struct rv7xx_power_info *rv770_get_pi(struct radeon_device *rdev); + +u32 rv740_get_decoded_reference_divider(u32 encoded_ref) +{ + u32 ref = 0; + + switch (encoded_ref) { + case 0: + ref = 1; + break; + case 16: + ref = 2; + break; + case 17: + ref = 3; + break; + case 18: + ref = 2; + break; + case 19: + ref = 3; + break; + case 20: + ref = 4; + break; + case 21: + ref = 5; + break; + default: + DRM_ERROR("Invalid encoded Reference Divider\n"); + ref = 0; + break; + } + + return ref; +} + +struct dll_speed_setting { + u16 min; + u16 max; + u32 dll_speed; +}; + +static struct dll_speed_setting dll_speed_table[16] = +{ + { 270, 320, 0x0f }, + { 240, 270, 0x0e }, + { 200, 240, 0x0d }, + { 180, 200, 0x0c }, + { 160, 180, 0x0b }, + { 140, 160, 0x0a }, + { 120, 140, 0x09 }, + { 110, 120, 0x08 }, + { 95, 110, 0x07 }, + { 85, 95, 0x06 }, + { 78, 85, 0x05 }, + { 70, 78, 0x04 }, + { 65, 70, 0x03 }, + { 60, 65, 0x02 }, + { 42, 60, 0x01 }, + { 00, 42, 0x00 } +}; + +u32 rv740_get_dll_speed(bool is_gddr5, u32 memory_clock) +{ + int i; + u32 factor; + u16 data_rate; + + if (is_gddr5) + factor = 4; + else + factor = 2; + + data_rate = (u16)(memory_clock * factor / 1000); + + if (data_rate < dll_speed_table[0].max) { + for (i = 0; i < 16; i++) { + if (data_rate > dll_speed_table[i].min && + data_rate <= dll_speed_table[i].max) + return dll_speed_table[i].dll_speed; + } + } + + DRM_DEBUG_KMS("Target MCLK greater than largest MCLK in DLL speed table\n"); + + return 0x0f; +} + +int rv740_populate_sclk_value(struct radeon_device *rdev, u32 engine_clock, + RV770_SMC_SCLK_VALUE *sclk) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct atom_clock_dividers dividers; + u32 spll_func_cntl = pi->clk_regs.rv770.cg_spll_func_cntl; + u32 spll_func_cntl_2 = pi->clk_regs.rv770.cg_spll_func_cntl_2; + u32 spll_func_cntl_3 = pi->clk_regs.rv770.cg_spll_func_cntl_3; + u32 cg_spll_spread_spectrum = pi->clk_regs.rv770.cg_spll_spread_spectrum; + u32 cg_spll_spread_spectrum_2 = pi->clk_regs.rv770.cg_spll_spread_spectrum_2; + u64 tmp; + u32 reference_clock = rdev->clock.spll.reference_freq; + u32 reference_divider; + u32 fbdiv; + int ret; + + ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM, + engine_clock, false, ÷rs); + if (ret) + return ret; + + reference_divider = 1 + dividers.ref_div; + + tmp = (u64) engine_clock * reference_divider * dividers.post_div; + + fbdiv = (u32) ((16384 * tmp) / reference_clock); + + spll_func_cntl &= ~(SPLL_PDIV_A_MASK | SPLL_REF_DIV_MASK); + spll_func_cntl |= SPLL_REF_DIV(dividers.ref_div); + spll_func_cntl |= SPLL_PDIV_A(dividers.post_div); + + spll_func_cntl_2 &= ~SCLK_MUX_SEL_MASK; + spll_func_cntl_2 |= SCLK_MUX_SEL(2); + + spll_func_cntl_3 &= ~SPLL_FB_DIV_MASK; + spll_func_cntl_3 |= SPLL_FB_DIV(fbdiv); + spll_func_cntl_3 |= SPLL_DITHEN; + + if (pi->sclk_ss) { + struct radeon_atom_ss ss; + u32 vco_freq = engine_clock * dividers.post_div; + + if (radeon_atombios_get_asic_ss_info(rdev, &ss, + ASIC_INTERNAL_ENGINE_SS, vco_freq)) { + u32 clk_s = reference_clock * 5 / (reference_divider * ss.rate); + u32 clk_v = 4 * ss.percentage * fbdiv / (clk_s * 10000); + + cg_spll_spread_spectrum &= ~CLK_S_MASK; + cg_spll_spread_spectrum |= CLK_S(clk_s); + cg_spll_spread_spectrum |= SSEN; + + cg_spll_spread_spectrum_2 &= ~CLK_V_MASK; + cg_spll_spread_spectrum_2 |= CLK_V(clk_v); + } + } + + sclk->sclk_value = cpu_to_be32(engine_clock); + sclk->vCG_SPLL_FUNC_CNTL = cpu_to_be32(spll_func_cntl); + sclk->vCG_SPLL_FUNC_CNTL_2 = cpu_to_be32(spll_func_cntl_2); + sclk->vCG_SPLL_FUNC_CNTL_3 = cpu_to_be32(spll_func_cntl_3); + sclk->vCG_SPLL_SPREAD_SPECTRUM = cpu_to_be32(cg_spll_spread_spectrum); + sclk->vCG_SPLL_SPREAD_SPECTRUM_2 = cpu_to_be32(cg_spll_spread_spectrum_2); + + return 0; +} + +int rv740_populate_mclk_value(struct radeon_device *rdev, + u32 engine_clock, u32 memory_clock, + RV7XX_SMC_MCLK_VALUE *mclk) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + u32 mpll_ad_func_cntl = pi->clk_regs.rv770.mpll_ad_func_cntl; + u32 mpll_ad_func_cntl_2 = pi->clk_regs.rv770.mpll_ad_func_cntl_2; + u32 mpll_dq_func_cntl = pi->clk_regs.rv770.mpll_dq_func_cntl; + u32 mpll_dq_func_cntl_2 = pi->clk_regs.rv770.mpll_dq_func_cntl_2; + u32 mclk_pwrmgt_cntl = pi->clk_regs.rv770.mclk_pwrmgt_cntl; + u32 dll_cntl = pi->clk_regs.rv770.dll_cntl; + u32 mpll_ss1 = pi->clk_regs.rv770.mpll_ss1; + u32 mpll_ss2 = pi->clk_regs.rv770.mpll_ss2; + struct atom_clock_dividers dividers; + u32 ibias; + u32 dll_speed; + int ret; + + ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_MEMORY_PLL_PARAM, + memory_clock, false, ÷rs); + if (ret) + return ret; + + ibias = rv770_map_clkf_to_ibias(rdev, dividers.whole_fb_div); + + mpll_ad_func_cntl &= ~(CLKR_MASK | + YCLK_POST_DIV_MASK | + CLKF_MASK | + CLKFRAC_MASK | + IBIAS_MASK); + mpll_ad_func_cntl |= CLKR(dividers.ref_div); + mpll_ad_func_cntl |= YCLK_POST_DIV(dividers.post_div); + mpll_ad_func_cntl |= CLKF(dividers.whole_fb_div); + mpll_ad_func_cntl |= CLKFRAC(dividers.frac_fb_div); + mpll_ad_func_cntl |= IBIAS(ibias); + + if (dividers.vco_mode) + mpll_ad_func_cntl_2 |= VCO_MODE; + else + mpll_ad_func_cntl_2 &= ~VCO_MODE; + + if (pi->mem_gddr5) { + mpll_dq_func_cntl &= ~(CLKR_MASK | + YCLK_POST_DIV_MASK | + CLKF_MASK | + CLKFRAC_MASK | + IBIAS_MASK); + mpll_dq_func_cntl |= CLKR(dividers.ref_div); + mpll_dq_func_cntl |= YCLK_POST_DIV(dividers.post_div); + mpll_dq_func_cntl |= CLKF(dividers.whole_fb_div); + mpll_dq_func_cntl |= CLKFRAC(dividers.frac_fb_div); + mpll_dq_func_cntl |= IBIAS(ibias); + + if (dividers.vco_mode) + mpll_dq_func_cntl_2 |= VCO_MODE; + else + mpll_dq_func_cntl_2 &= ~VCO_MODE; + } + + if (pi->mclk_ss) { + struct radeon_atom_ss ss; + u32 vco_freq = memory_clock * dividers.post_div; + + if (radeon_atombios_get_asic_ss_info(rdev, &ss, + ASIC_INTERNAL_MEMORY_SS, vco_freq)) { + u32 reference_clock = rdev->clock.mpll.reference_freq; + u32 decoded_ref = rv740_get_decoded_reference_divider(dividers.ref_div); + u32 clk_s = reference_clock * 5 / (decoded_ref * ss.rate); + u32 clk_v = 0x40000 * ss.percentage * + (dividers.whole_fb_div + (dividers.frac_fb_div / 8)) / (clk_s * 10000); + + mpll_ss1 &= ~CLKV_MASK; + mpll_ss1 |= CLKV(clk_v); + + mpll_ss2 &= ~CLKS_MASK; + mpll_ss2 |= CLKS(clk_s); + } + } + + dll_speed = rv740_get_dll_speed(pi->mem_gddr5, + memory_clock); + + mclk_pwrmgt_cntl &= ~DLL_SPEED_MASK; + mclk_pwrmgt_cntl |= DLL_SPEED(dll_speed); + + mclk->mclk770.mclk_value = cpu_to_be32(memory_clock); + mclk->mclk770.vMPLL_AD_FUNC_CNTL = cpu_to_be32(mpll_ad_func_cntl); + mclk->mclk770.vMPLL_AD_FUNC_CNTL_2 = cpu_to_be32(mpll_ad_func_cntl_2); + mclk->mclk770.vMPLL_DQ_FUNC_CNTL = cpu_to_be32(mpll_dq_func_cntl); + mclk->mclk770.vMPLL_DQ_FUNC_CNTL_2 = cpu_to_be32(mpll_dq_func_cntl_2); + mclk->mclk770.vMCLK_PWRMGT_CNTL = cpu_to_be32(mclk_pwrmgt_cntl); + mclk->mclk770.vDLL_CNTL = cpu_to_be32(dll_cntl); + mclk->mclk770.vMPLL_SS = cpu_to_be32(mpll_ss1); + mclk->mclk770.vMPLL_SS2 = cpu_to_be32(mpll_ss2); + + return 0; +} + +void rv740_read_clock_registers(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + + pi->clk_regs.rv770.cg_spll_func_cntl = + RREG32(CG_SPLL_FUNC_CNTL); + pi->clk_regs.rv770.cg_spll_func_cntl_2 = + RREG32(CG_SPLL_FUNC_CNTL_2); + pi->clk_regs.rv770.cg_spll_func_cntl_3 = + RREG32(CG_SPLL_FUNC_CNTL_3); + pi->clk_regs.rv770.cg_spll_spread_spectrum = + RREG32(CG_SPLL_SPREAD_SPECTRUM); + pi->clk_regs.rv770.cg_spll_spread_spectrum_2 = + RREG32(CG_SPLL_SPREAD_SPECTRUM_2); + + pi->clk_regs.rv770.mpll_ad_func_cntl = + RREG32(MPLL_AD_FUNC_CNTL); + pi->clk_regs.rv770.mpll_ad_func_cntl_2 = + RREG32(MPLL_AD_FUNC_CNTL_2); + pi->clk_regs.rv770.mpll_dq_func_cntl = + RREG32(MPLL_DQ_FUNC_CNTL); + pi->clk_regs.rv770.mpll_dq_func_cntl_2 = + RREG32(MPLL_DQ_FUNC_CNTL_2); + pi->clk_regs.rv770.mclk_pwrmgt_cntl = + RREG32(MCLK_PWRMGT_CNTL); + pi->clk_regs.rv770.dll_cntl = RREG32(DLL_CNTL); + pi->clk_regs.rv770.mpll_ss1 = RREG32(MPLL_SS1); + pi->clk_regs.rv770.mpll_ss2 = RREG32(MPLL_SS2); +} + +int rv740_populate_smc_acpi_state(struct radeon_device *rdev, + RV770_SMC_STATETABLE *table) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + u32 mpll_ad_func_cntl = pi->clk_regs.rv770.mpll_ad_func_cntl; + u32 mpll_ad_func_cntl_2 = pi->clk_regs.rv770.mpll_ad_func_cntl_2; + u32 mpll_dq_func_cntl = pi->clk_regs.rv770.mpll_dq_func_cntl; + u32 mpll_dq_func_cntl_2 = pi->clk_regs.rv770.mpll_dq_func_cntl_2; + u32 spll_func_cntl = pi->clk_regs.rv770.cg_spll_func_cntl; + u32 spll_func_cntl_2 = pi->clk_regs.rv770.cg_spll_func_cntl_2; + u32 spll_func_cntl_3 = pi->clk_regs.rv770.cg_spll_func_cntl_3; + u32 mclk_pwrmgt_cntl = pi->clk_regs.rv770.mclk_pwrmgt_cntl; + u32 dll_cntl = pi->clk_regs.rv770.dll_cntl; + + table->ACPIState = table->initialState; + + table->ACPIState.flags &= ~PPSMC_SWSTATE_FLAG_DC; + + if (pi->acpi_vddc) { + rv770_populate_vddc_value(rdev, pi->acpi_vddc, + &table->ACPIState.levels[0].vddc); + table->ACPIState.levels[0].gen2PCIE = + pi->pcie_gen2 ? + pi->acpi_pcie_gen2 : 0; + table->ACPIState.levels[0].gen2XSP = + pi->acpi_pcie_gen2; + } else { + rv770_populate_vddc_value(rdev, pi->min_vddc_in_table, + &table->ACPIState.levels[0].vddc); + table->ACPIState.levels[0].gen2PCIE = 0; + } + + mpll_ad_func_cntl_2 |= BIAS_GEN_PDNB | RESET_EN; + + mpll_dq_func_cntl_2 |= BYPASS | BIAS_GEN_PDNB | RESET_EN; + + mclk_pwrmgt_cntl |= (MRDCKA0_RESET | + MRDCKA1_RESET | + MRDCKB0_RESET | + MRDCKB1_RESET | + MRDCKC0_RESET | + MRDCKC1_RESET | + MRDCKD0_RESET | + MRDCKD1_RESET); + + dll_cntl |= (MRDCKA0_BYPASS | + MRDCKA1_BYPASS | + MRDCKB0_BYPASS | + MRDCKB1_BYPASS | + MRDCKC0_BYPASS | + MRDCKC1_BYPASS | + MRDCKD0_BYPASS | + MRDCKD1_BYPASS); + + spll_func_cntl |= SPLL_RESET | SPLL_SLEEP | SPLL_BYPASS_EN; + + spll_func_cntl_2 &= ~SCLK_MUX_SEL_MASK; + spll_func_cntl_2 |= SCLK_MUX_SEL(4); + + table->ACPIState.levels[0].mclk.mclk770.vMPLL_AD_FUNC_CNTL = cpu_to_be32(mpll_ad_func_cntl); + table->ACPIState.levels[0].mclk.mclk770.vMPLL_AD_FUNC_CNTL_2 = cpu_to_be32(mpll_ad_func_cntl_2); + table->ACPIState.levels[0].mclk.mclk770.vMPLL_DQ_FUNC_CNTL = cpu_to_be32(mpll_dq_func_cntl); + table->ACPIState.levels[0].mclk.mclk770.vMPLL_DQ_FUNC_CNTL_2 = cpu_to_be32(mpll_dq_func_cntl_2); + table->ACPIState.levels[0].mclk.mclk770.vMCLK_PWRMGT_CNTL = cpu_to_be32(mclk_pwrmgt_cntl); + table->ACPIState.levels[0].mclk.mclk770.vDLL_CNTL = cpu_to_be32(dll_cntl); + + table->ACPIState.levels[0].mclk.mclk770.mclk_value = 0; + + table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL = cpu_to_be32(spll_func_cntl); + table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_2 = cpu_to_be32(spll_func_cntl_2); + table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_3 = cpu_to_be32(spll_func_cntl_3); + + table->ACPIState.levels[0].sclk.sclk_value = 0; + + table->ACPIState.levels[1] = table->ACPIState.levels[0]; + table->ACPIState.levels[2] = table->ACPIState.levels[0]; + + rv770_populate_mvdd_value(rdev, 0, &table->ACPIState.levels[0].mvdd); + + return 0; +} + +void rv740_enable_mclk_spread_spectrum(struct radeon_device *rdev, + bool enable) +{ + if (enable) + WREG32_P(MPLL_CNTL_MODE, SS_SSEN, ~SS_SSEN); + else + WREG32_P(MPLL_CNTL_MODE, 0, ~SS_SSEN); +} + +u8 rv740_get_mclk_frequency_ratio(u32 memory_clock) +{ + u8 mc_para_index; + + if ((memory_clock < 10000) || (memory_clock > 47500)) + mc_para_index = 0x00; + else + mc_para_index = (u8)((memory_clock - 10000) / 2500); + + return mc_para_index; +} diff --git a/drivers/gpu/drm/radeon/rv740d.h b/drivers/gpu/drm/radeon/rv740d.h new file mode 100644 index 0000000..fe5ab07 --- /dev/null +++ b/drivers/gpu/drm/radeon/rv740d.h @@ -0,0 +1,117 @@ +/* + * Copyright 2011 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ +#ifndef RV740_H +#define RV740_H + +#define CG_SPLL_FUNC_CNTL 0x600 +#define SPLL_RESET (1 << 0) +#define SPLL_SLEEP (1 << 1) +#define SPLL_BYPASS_EN (1 << 3) +#define SPLL_REF_DIV(x) ((x) << 4) +#define SPLL_REF_DIV_MASK (0x3f << 4) +#define SPLL_PDIV_A(x) ((x) << 20) +#define SPLL_PDIV_A_MASK (0x7f << 20) +#define CG_SPLL_FUNC_CNTL_2 0x604 +#define SCLK_MUX_SEL(x) ((x) << 0) +#define SCLK_MUX_SEL_MASK (0x1ff << 0) +#define CG_SPLL_FUNC_CNTL_3 0x608 +#define SPLL_FB_DIV(x) ((x) << 0) +#define SPLL_FB_DIV_MASK (0x3ffffff << 0) +#define SPLL_DITHEN (1 << 28) + +#define MPLL_CNTL_MODE 0x61c +#define SS_SSEN (1 << 24) + +#define MPLL_AD_FUNC_CNTL 0x624 +#define CLKF(x) ((x) << 0) +#define CLKF_MASK (0x7f << 0) +#define CLKR(x) ((x) << 7) +#define CLKR_MASK (0x1f << 7) +#define CLKFRAC(x) ((x) << 12) +#define CLKFRAC_MASK (0x1f << 12) +#define YCLK_POST_DIV(x) ((x) << 17) +#define YCLK_POST_DIV_MASK (3 << 17) +#define IBIAS(x) ((x) << 20) +#define IBIAS_MASK (0x3ff << 20) +#define RESET (1 << 30) +#define PDNB (1 << 31) +#define MPLL_AD_FUNC_CNTL_2 0x628 +#define BYPASS (1 << 19) +#define BIAS_GEN_PDNB (1 << 24) +#define RESET_EN (1 << 25) +#define VCO_MODE (1 << 29) +#define MPLL_DQ_FUNC_CNTL 0x62c +#define MPLL_DQ_FUNC_CNTL_2 0x630 + +#define MCLK_PWRMGT_CNTL 0x648 +#define DLL_SPEED(x) ((x) << 0) +#define DLL_SPEED_MASK (0x1f << 0) +# define MPLL_PWRMGT_OFF (1 << 5) +# define DLL_READY (1 << 6) +# define MC_INT_CNTL (1 << 7) +# define MRDCKA0_SLEEP (1 << 8) +# define MRDCKA1_SLEEP (1 << 9) +# define MRDCKB0_SLEEP (1 << 10) +# define MRDCKB1_SLEEP (1 << 11) +# define MRDCKC0_SLEEP (1 << 12) +# define MRDCKC1_SLEEP (1 << 13) +# define MRDCKD0_SLEEP (1 << 14) +# define MRDCKD1_SLEEP (1 << 15) +# define MRDCKA0_RESET (1 << 16) +# define MRDCKA1_RESET (1 << 17) +# define MRDCKB0_RESET (1 << 18) +# define MRDCKB1_RESET (1 << 19) +# define MRDCKC0_RESET (1 << 20) +# define MRDCKC1_RESET (1 << 21) +# define MRDCKD0_RESET (1 << 22) +# define MRDCKD1_RESET (1 << 23) +# define DLL_READY_READ (1 << 24) +# define USE_DISPLAY_GAP (1 << 25) +# define USE_DISPLAY_URGENT_NORMAL (1 << 26) +# define MPLL_TURNOFF_D2 (1 << 28) +#define DLL_CNTL 0x64c +# define MRDCKA0_BYPASS (1 << 24) +# define MRDCKA1_BYPASS (1 << 25) +# define MRDCKB0_BYPASS (1 << 26) +# define MRDCKB1_BYPASS (1 << 27) +# define MRDCKC0_BYPASS (1 << 28) +# define MRDCKC1_BYPASS (1 << 29) +# define MRDCKD0_BYPASS (1 << 30) +# define MRDCKD1_BYPASS (1 << 31) + +#define CG_SPLL_SPREAD_SPECTRUM 0x790 +#define SSEN (1 << 0) +#define CLK_S(x) ((x) << 4) +#define CLK_S_MASK (0xfff << 4) +#define CG_SPLL_SPREAD_SPECTRUM_2 0x794 +#define CLK_V(x) ((x) << 0) +#define CLK_V_MASK (0x3ffffff << 0) + +#define MPLL_SS1 0x85c +#define CLKV(x) ((x) << 0) +#define CLKV_MASK (0x3ffffff << 0) +#define MPLL_SS2 0x860 +#define CLKS(x) ((x) << 0) +#define CLKS_MASK (0xfff << 0) + +#endif diff --git a/drivers/gpu/drm/radeon/rv770_dpm.c b/drivers/gpu/drm/radeon/rv770_dpm.c new file mode 100644 index 0000000..81660a6 --- /dev/null +++ b/drivers/gpu/drm/radeon/rv770_dpm.c @@ -0,0 +1,2325 @@ +/* + * Copyright 2011 Advanced Micro Devices, 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: Alex Deucher + */ + +#include "drmP.h" +#include "radeon.h" +#include "rv770d.h" +#include "r600_dpm.h" +#include "rv770_dpm.h" +#include "atom.h" + +#define MC_CG_ARB_FREQ_F0 0x0a +#define MC_CG_ARB_FREQ_F1 0x0b +#define MC_CG_ARB_FREQ_F2 0x0c +#define MC_CG_ARB_FREQ_F3 0x0d + +#define MC_CG_SEQ_DRAMCONF_S0 0x05 +#define MC_CG_SEQ_DRAMCONF_S1 0x06 + +#define PCIE_BUS_CLK 10000 +#define TCLK (PCIE_BUS_CLK / 10) + +#define SMC_RAM_END 0xC000 + +struct rv7xx_ps *rv770_get_ps(struct radeon_ps *rps) +{ + struct rv7xx_ps *ps = rps->ps_priv; + + return ps; +} + +struct rv7xx_power_info *rv770_get_pi(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rdev->pm.dpm.priv; + + return pi; +} + +static void rv770_enable_bif_dynamic_pcie_gen2(struct radeon_device *rdev, + bool enable) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + u32 tmp; + + tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL); + if (enable) { + tmp &= ~LC_HW_VOLTAGE_IF_CONTROL_MASK; + tmp |= LC_HW_VOLTAGE_IF_CONTROL(1); + tmp |= LC_GEN2_EN_STRAP; + } else { + if (!pi->boot_in_gen2) { + tmp &= ~LC_HW_VOLTAGE_IF_CONTROL_MASK; + tmp &= ~LC_GEN2_EN_STRAP; + } + } + if ((tmp & LC_OTHER_SIDE_EVER_SENT_GEN2) || + (tmp & LC_OTHER_SIDE_SUPPORTS_GEN2)) + WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp); + +} + +static void rv770_enable_l0s(struct radeon_device *rdev) +{ + u32 tmp; + + tmp = RREG32_PCIE_PORT(PCIE_LC_CNTL) & ~LC_L0S_INACTIVITY_MASK; + tmp |= LC_L0S_INACTIVITY(3); + WREG32_PCIE_PORT(PCIE_LC_CNTL, tmp); +} + +static void rv770_enable_l1(struct radeon_device *rdev) +{ + u32 tmp; + + tmp = RREG32_PCIE_PORT(PCIE_LC_CNTL); + tmp &= ~LC_L1_INACTIVITY_MASK; + tmp |= LC_L1_INACTIVITY(4); + tmp &= ~LC_PMI_TO_L1_DIS; + tmp &= ~LC_ASPM_TO_L1_DIS; + WREG32_PCIE_PORT(PCIE_LC_CNTL, tmp); +} + +static void rv770_enable_pll_sleep_in_l1(struct radeon_device *rdev) +{ + u32 tmp; + + tmp = RREG32_PCIE_PORT(PCIE_LC_CNTL) & ~LC_L1_INACTIVITY_MASK; + tmp |= LC_L1_INACTIVITY(8); + WREG32_PCIE_PORT(PCIE_LC_CNTL, tmp); + + /* NOTE, this is a PCIE indirect reg, not PCIE PORT */ + tmp = RREG32_PCIE(PCIE_P_CNTL); + tmp |= P_PLL_PWRDN_IN_L1L23; + tmp &= ~P_PLL_BUF_PDNB; + tmp &= ~P_PLL_PDNB; + tmp |= P_ALLOW_PRX_FRONTEND_SHUTOFF; + WREG32_PCIE(PCIE_P_CNTL, tmp); +} + +static void rv770_gfx_clock_gating_enable(struct radeon_device *rdev, + bool enable) +{ + if (enable) + WREG32_P(SCLK_PWRMGT_CNTL, DYN_GFX_CLK_OFF_EN, ~DYN_GFX_CLK_OFF_EN); + else { + WREG32_P(SCLK_PWRMGT_CNTL, 0, ~DYN_GFX_CLK_OFF_EN); + WREG32_P(SCLK_PWRMGT_CNTL, GFX_CLK_FORCE_ON, ~GFX_CLK_FORCE_ON); + WREG32_P(SCLK_PWRMGT_CNTL, 0, ~GFX_CLK_FORCE_ON); + RREG32(GB_TILING_CONFIG); + } +} + +static void rv770_mg_clock_gating_enable(struct radeon_device *rdev, + bool enable) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + + if (enable) { + u32 mgcg_cgtt_local0; + + if (rdev->family == CHIP_RV770) + mgcg_cgtt_local0 = RV770_MGCGTTLOCAL0_DFLT; + else + mgcg_cgtt_local0 = RV7XX_MGCGTTLOCAL0_DFLT; + + WREG32(CG_CGTT_LOCAL_0, mgcg_cgtt_local0); + WREG32(CG_CGTT_LOCAL_1, (RV770_MGCGTTLOCAL1_DFLT & 0xFFFFCFFF)); + + if (pi->mgcgtssm) + WREG32(CGTS_SM_CTRL_REG, RV770_MGCGCGTSSMCTRL_DFLT); + } else { + WREG32(CG_CGTT_LOCAL_0, 0xFFFFFFFF); + WREG32(CG_CGTT_LOCAL_1, 0xFFFFCFFF); + } +} + +void rv770_restore_cgcg(struct radeon_device *rdev) +{ + bool dpm_en = false, cg_en = false; + + if (RREG32(GENERAL_PWRMGT) & GLOBAL_PWRMGT_EN) + dpm_en = true; + if (RREG32(SCLK_PWRMGT_CNTL) & DYN_GFX_CLK_OFF_EN) + cg_en = true; + + if (dpm_en && !cg_en) + WREG32_P(SCLK_PWRMGT_CNTL, DYN_GFX_CLK_OFF_EN, ~DYN_GFX_CLK_OFF_EN); +} + +static void rv770_start_dpm(struct radeon_device *rdev) +{ + WREG32_P(SCLK_PWRMGT_CNTL, 0, ~SCLK_PWRMGT_OFF); + + WREG32_P(MCLK_PWRMGT_CNTL, 0, ~MPLL_PWRMGT_OFF); + + WREG32_P(GENERAL_PWRMGT, GLOBAL_PWRMGT_EN, ~GLOBAL_PWRMGT_EN); +} + +void rv770_stop_dpm(struct radeon_device *rdev) +{ + PPSMC_Result result; + + result = rv770_send_msg_to_smc(rdev, PPSMC_MSG_TwoLevelsDisabled); + + if (result != PPSMC_Result_OK) + DRM_ERROR("Could not force DPM to low.\n"); + + WREG32_P(GENERAL_PWRMGT, 0, ~GLOBAL_PWRMGT_EN); + + WREG32_P(SCLK_PWRMGT_CNTL, SCLK_PWRMGT_OFF, ~SCLK_PWRMGT_OFF); + + WREG32_P(MCLK_PWRMGT_CNTL, MPLL_PWRMGT_OFF, ~MPLL_PWRMGT_OFF); +} + +bool rv770_dpm_enabled(struct radeon_device *rdev) +{ + if (RREG32(GENERAL_PWRMGT) & GLOBAL_PWRMGT_EN) + return true; + else + return false; +} + +void rv770_enable_thermal_protection(struct radeon_device *rdev, + bool enable) +{ + if (enable) + WREG32_P(GENERAL_PWRMGT, 0, ~THERMAL_PROTECTION_DIS); + else + WREG32_P(GENERAL_PWRMGT, THERMAL_PROTECTION_DIS, ~THERMAL_PROTECTION_DIS); +} + +void rv770_enable_acpi_pm(struct radeon_device *rdev) +{ + WREG32_P(GENERAL_PWRMGT, STATIC_PM_EN, ~STATIC_PM_EN); +} + +u8 rv770_get_seq_value(struct radeon_device *rdev, + struct rv7xx_pl *pl) +{ + return (pl->flags & ATOM_PPLIB_R600_FLAGS_LOWPOWER) ? + MC_CG_SEQ_DRAMCONF_S0 : MC_CG_SEQ_DRAMCONF_S1; +} + +int rv770_read_smc_soft_register(struct radeon_device *rdev, + u16 reg_offset, u32 *value) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + + return rv770_read_smc_sram_dword(rdev, + pi->soft_regs_start + reg_offset, + value, pi->sram_end); +} + +int rv770_write_smc_soft_register(struct radeon_device *rdev, + u16 reg_offset, u32 value) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + + return rv770_write_smc_sram_dword(rdev, + pi->soft_regs_start + reg_offset, + value, pi->sram_end); +} + +int rv770_populate_smc_t(struct radeon_device *rdev, + struct radeon_ps *radeon_state, + RV770_SMC_SWSTATE *smc_state) +{ + struct rv7xx_ps *state = rv770_get_ps(radeon_state); + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + int i; + int a_n; + int a_d; + u8 l[RV770_SMC_PERFORMANCE_LEVELS_PER_SWSTATE]; + u8 r[RV770_SMC_PERFORMANCE_LEVELS_PER_SWSTATE]; + u32 a_t; + + l[0] = 0; + r[2] = 100; + + a_n = (int)state->medium.sclk * RV770_LMP_DFLT + + (int)state->low.sclk * (R600_AH_DFLT - RV770_RLP_DFLT); + a_d = (int)state->low.sclk * (100 - (int)RV770_RLP_DFLT) + + (int)state->medium.sclk * RV770_LMP_DFLT; + + l[1] = (u8)(RV770_LMP_DFLT - (int)RV770_LMP_DFLT * a_n / a_d); + r[0] = (u8)(RV770_RLP_DFLT + (100 - (int)RV770_RLP_DFLT) * a_n / a_d); + + a_n = (int)state->high.sclk * RV770_LHP_DFLT + + (int)state->medium.sclk * + (R600_AH_DFLT - RV770_RMP_DFLT); + a_d = (int)state->medium.sclk * (100 - (int)RV770_RMP_DFLT) + + (int)state->high.sclk * RV770_LHP_DFLT; + + l[2] = (u8)(RV770_LHP_DFLT - (int)RV770_LHP_DFLT * a_n / a_d); + r[1] = (u8)(RV770_RMP_DFLT + (100 - (int)RV770_RMP_DFLT) * a_n / a_d); + + for (i = 0; i < (RV770_SMC_PERFORMANCE_LEVELS_PER_SWSTATE - 1); i++) { + a_t = CG_R(r[i] * pi->bsp / 200) | CG_L(l[i] * pi->bsp / 200); + smc_state->levels[i].aT = cpu_to_be32(a_t); + } + + a_t = CG_R(r[RV770_SMC_PERFORMANCE_LEVELS_PER_SWSTATE - 1] * pi->pbsp / 200) | + CG_L(l[RV770_SMC_PERFORMANCE_LEVELS_PER_SWSTATE - 1] * pi->pbsp / 200); + + smc_state->levels[RV770_SMC_PERFORMANCE_LEVELS_PER_SWSTATE - 1].aT = + cpu_to_be32(a_t); + + return 0; +} + +int rv770_populate_smc_sp(struct radeon_device *rdev, + struct radeon_ps *radeon_state, + RV770_SMC_SWSTATE *smc_state) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + int i; + + for (i = 0; i < (RV770_SMC_PERFORMANCE_LEVELS_PER_SWSTATE - 1); i++) + smc_state->levels[i].bSP = cpu_to_be32(pi->dsp); + + smc_state->levels[RV770_SMC_PERFORMANCE_LEVELS_PER_SWSTATE - 1].bSP = + cpu_to_be32(pi->psp); + + return 0; +} + +static void rv770_calculate_fractional_mpll_feedback_divider(u32 memory_clock, + u32 reference_clock, + bool gddr5, + struct atom_clock_dividers *dividers, + u32 *clkf, + u32 *clkfrac) +{ + u32 post_divider, reference_divider, feedback_divider8; + u32 fyclk; + + if (gddr5) + fyclk = (memory_clock * 8) / 2; + else + fyclk = (memory_clock * 4) / 2; + + post_divider = dividers->post_div; + reference_divider = dividers->ref_div; + + feedback_divider8 = + (8 * fyclk * reference_divider * post_divider) / reference_clock; + + *clkf = feedback_divider8 / 8; + *clkfrac = feedback_divider8 % 8; +} + +static int rv770_encode_yclk_post_div(u32 postdiv, u32 *encoded_postdiv) +{ + int ret = 0; + + switch (postdiv) { + case 1: + *encoded_postdiv = 0; + break; + case 2: + *encoded_postdiv = 1; + break; + case 4: + *encoded_postdiv = 2; + break; + case 8: + *encoded_postdiv = 3; + break; + case 16: + *encoded_postdiv = 4; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +u32 rv770_map_clkf_to_ibias(struct radeon_device *rdev, u32 clkf) +{ + if (clkf <= 0x10) + return 0x4B; + if (clkf <= 0x19) + return 0x5B; + if (clkf <= 0x21) + return 0x2B; + if (clkf <= 0x27) + return 0x6C; + if (clkf <= 0x31) + return 0x9D; + return 0xC6; +} + +static int rv770_populate_mclk_value(struct radeon_device *rdev, + u32 engine_clock, u32 memory_clock, + RV7XX_SMC_MCLK_VALUE *mclk) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + u8 encoded_reference_dividers[] = { 0, 16, 17, 20, 21 }; + u32 mpll_ad_func_cntl = + pi->clk_regs.rv770.mpll_ad_func_cntl; + u32 mpll_ad_func_cntl_2 = + pi->clk_regs.rv770.mpll_ad_func_cntl_2; + u32 mpll_dq_func_cntl = + pi->clk_regs.rv770.mpll_dq_func_cntl; + u32 mpll_dq_func_cntl_2 = + pi->clk_regs.rv770.mpll_dq_func_cntl_2; + u32 mclk_pwrmgt_cntl = + pi->clk_regs.rv770.mclk_pwrmgt_cntl; + u32 dll_cntl = pi->clk_regs.rv770.dll_cntl; + struct atom_clock_dividers dividers; + u32 reference_clock = rdev->clock.mpll.reference_freq; + u32 clkf, clkfrac; + u32 postdiv_yclk; + u32 ibias; + int ret; + + ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_MEMORY_PLL_PARAM, + memory_clock, false, ÷rs); + if (ret) + return ret; + + if ((dividers.ref_div < 1) || (dividers.ref_div > 5)) + return -EINVAL; + + rv770_calculate_fractional_mpll_feedback_divider(memory_clock, reference_clock, + pi->mem_gddr5, + ÷rs, &clkf, &clkfrac); + + ret = rv770_encode_yclk_post_div(dividers.post_div, &postdiv_yclk); + if (ret) + return ret; + + ibias = rv770_map_clkf_to_ibias(rdev, clkf); + + mpll_ad_func_cntl &= ~(CLKR_MASK | + YCLK_POST_DIV_MASK | + CLKF_MASK | + CLKFRAC_MASK | + IBIAS_MASK); + mpll_ad_func_cntl |= CLKR(encoded_reference_dividers[dividers.ref_div - 1]); + mpll_ad_func_cntl |= YCLK_POST_DIV(postdiv_yclk); + mpll_ad_func_cntl |= CLKF(clkf); + mpll_ad_func_cntl |= CLKFRAC(clkfrac); + mpll_ad_func_cntl |= IBIAS(ibias); + + if (dividers.vco_mode) + mpll_ad_func_cntl_2 |= VCO_MODE; + else + mpll_ad_func_cntl_2 &= ~VCO_MODE; + + if (pi->mem_gddr5) { + rv770_calculate_fractional_mpll_feedback_divider(memory_clock, + reference_clock, + pi->mem_gddr5, + ÷rs, &clkf, &clkfrac); + + ibias = rv770_map_clkf_to_ibias(rdev, clkf); + + ret = rv770_encode_yclk_post_div(dividers.post_div, &postdiv_yclk); + if (ret) + return ret; + + mpll_dq_func_cntl &= ~(CLKR_MASK | + YCLK_POST_DIV_MASK | + CLKF_MASK | + CLKFRAC_MASK | + IBIAS_MASK); + mpll_dq_func_cntl |= CLKR(encoded_reference_dividers[dividers.ref_div - 1]); + mpll_dq_func_cntl |= YCLK_POST_DIV(postdiv_yclk); + mpll_dq_func_cntl |= CLKF(clkf); + mpll_dq_func_cntl |= CLKFRAC(clkfrac); + mpll_dq_func_cntl |= IBIAS(ibias); + + if (dividers.vco_mode) + mpll_dq_func_cntl_2 |= VCO_MODE; + else + mpll_dq_func_cntl_2 &= ~VCO_MODE; + } + + mclk->mclk770.mclk_value = cpu_to_be32(memory_clock); + mclk->mclk770.vMPLL_AD_FUNC_CNTL = cpu_to_be32(mpll_ad_func_cntl); + mclk->mclk770.vMPLL_AD_FUNC_CNTL_2 = cpu_to_be32(mpll_ad_func_cntl_2); + mclk->mclk770.vMPLL_DQ_FUNC_CNTL = cpu_to_be32(mpll_dq_func_cntl); + mclk->mclk770.vMPLL_DQ_FUNC_CNTL_2 = cpu_to_be32(mpll_dq_func_cntl_2); + mclk->mclk770.vMCLK_PWRMGT_CNTL = cpu_to_be32(mclk_pwrmgt_cntl); + mclk->mclk770.vDLL_CNTL = cpu_to_be32(dll_cntl); + + return 0; +} + +static int rv770_populate_sclk_value(struct radeon_device *rdev, + u32 engine_clock, + RV770_SMC_SCLK_VALUE *sclk) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct atom_clock_dividers dividers; + u32 spll_func_cntl = + pi->clk_regs.rv770.cg_spll_func_cntl; + u32 spll_func_cntl_2 = + pi->clk_regs.rv770.cg_spll_func_cntl_2; + u32 spll_func_cntl_3 = + pi->clk_regs.rv770.cg_spll_func_cntl_3; + u32 cg_spll_spread_spectrum = + pi->clk_regs.rv770.cg_spll_spread_spectrum; + u32 cg_spll_spread_spectrum_2 = + pi->clk_regs.rv770.cg_spll_spread_spectrum_2; + u64 tmp; + u32 reference_clock = rdev->clock.spll.reference_freq; + u32 reference_divider, post_divider; + u32 fbdiv; + int ret; + + ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM, + engine_clock, false, ÷rs); + if (ret) + return ret; + + reference_divider = 1 + dividers.ref_div; + + if (dividers.enable_post_div) + post_divider = (0x0f & (dividers.post_div >> 4)) + (0x0f & dividers.post_div) + 2; + else + post_divider = 1; + + tmp = (u64) engine_clock * reference_divider * post_divider; + + fbdiv = (u32) ((16384 * tmp) / reference_clock); + + if (dividers.enable_post_div) + spll_func_cntl |= SPLL_DIVEN; + else + spll_func_cntl &= ~SPLL_DIVEN; + spll_func_cntl &= ~(SPLL_HILEN_MASK | SPLL_LOLEN_MASK | SPLL_REF_DIV_MASK); + spll_func_cntl |= SPLL_REF_DIV(dividers.ref_div); + spll_func_cntl |= SPLL_HILEN((dividers.post_div >> 4) & 0xf); + spll_func_cntl |= SPLL_LOLEN(dividers.post_div & 0xf); + + spll_func_cntl_2 &= ~SCLK_MUX_SEL_MASK; + spll_func_cntl_2 |= SCLK_MUX_SEL(2); + + spll_func_cntl_3 &= ~SPLL_FB_DIV_MASK; + spll_func_cntl_3 |= SPLL_FB_DIV(fbdiv); + spll_func_cntl_3 |= SPLL_DITHEN; + + if (pi->sclk_ss) { + struct radeon_atom_ss ss; + u32 vco_freq = engine_clock * post_divider; + + if (radeon_atombios_get_asic_ss_info(rdev, &ss, + ASIC_INTERNAL_ENGINE_SS, vco_freq)) { + u32 clk_s = reference_clock * 5 / (reference_divider * ss.rate); + u32 clk_v = ss.percentage * fbdiv / (clk_s * 10000); + + cg_spll_spread_spectrum &= ~CLKS_MASK; + cg_spll_spread_spectrum |= CLKS(clk_s); + cg_spll_spread_spectrum |= SSEN; + + cg_spll_spread_spectrum_2 &= ~CLKV_MASK; + cg_spll_spread_spectrum_2 |= CLKV(clk_v); + } + } + + sclk->sclk_value = cpu_to_be32(engine_clock); + sclk->vCG_SPLL_FUNC_CNTL = cpu_to_be32(spll_func_cntl); + sclk->vCG_SPLL_FUNC_CNTL_2 = cpu_to_be32(spll_func_cntl_2); + sclk->vCG_SPLL_FUNC_CNTL_3 = cpu_to_be32(spll_func_cntl_3); + sclk->vCG_SPLL_SPREAD_SPECTRUM = cpu_to_be32(cg_spll_spread_spectrum); + sclk->vCG_SPLL_SPREAD_SPECTRUM_2 = cpu_to_be32(cg_spll_spread_spectrum_2); + + return 0; +} + +int rv770_populate_vddc_value(struct radeon_device *rdev, u16 vddc, + RV770_SMC_VOLTAGE_VALUE *voltage) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + int i; + + if (!pi->voltage_control) { + voltage->index = 0; + voltage->value = 0; + return 0; + } + + for (i = 0; i < pi->valid_vddc_entries; i++) { + if (vddc <= pi->vddc_table[i].vddc) { + voltage->index = pi->vddc_table[i].vddc_index; + voltage->value = cpu_to_be16(vddc); + break; + } + } + + if (i == pi->valid_vddc_entries) + return -EINVAL; + + return 0; +} + +int rv770_populate_mvdd_value(struct radeon_device *rdev, u32 mclk, + RV770_SMC_VOLTAGE_VALUE *voltage) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + + if (!pi->mvdd_control) { + voltage->index = MVDD_HIGH_INDEX; + voltage->value = cpu_to_be16(MVDD_HIGH_VALUE); + return 0; + } + + if (mclk <= pi->mvdd_split_frequency) { + voltage->index = MVDD_LOW_INDEX; + voltage->value = cpu_to_be16(MVDD_LOW_VALUE); + } else { + voltage->index = MVDD_HIGH_INDEX; + voltage->value = cpu_to_be16(MVDD_HIGH_VALUE); + } + + return 0; +} + +static int rv770_convert_power_level_to_smc(struct radeon_device *rdev, + struct rv7xx_pl *pl, + RV770_SMC_HW_PERFORMANCE_LEVEL *level, + u8 watermark_level) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + int ret; + + level->gen2PCIE = pi->pcie_gen2 ? + ((pl->flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) ? 1 : 0) : 0; + level->gen2XSP = (pl->flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) ? 1 : 0; + level->backbias = (pl->flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE) ? 1 : 0; + level->displayWatermark = watermark_level; + + if (rdev->family == CHIP_RV740) + ret = rv740_populate_sclk_value(rdev, pl->sclk, + &level->sclk); + else if ((rdev->family == CHIP_RV730) || (rdev->family == CHIP_RV710)) + ret = rv730_populate_sclk_value(rdev, pl->sclk, + &level->sclk); + else + ret = rv770_populate_sclk_value(rdev, pl->sclk, + &level->sclk); + if (ret) + return ret; + + if (rdev->family == CHIP_RV740) { + if (pi->mem_gddr5) { + if (pl->mclk <= pi->mclk_strobe_mode_threshold) + level->strobeMode = + rv740_get_mclk_frequency_ratio(pl->mclk) | 0x10; + else + level->strobeMode = 0; + + if (pl->mclk > pi->mclk_edc_enable_threshold) + level->mcFlags = SMC_MC_EDC_RD_FLAG | SMC_MC_EDC_WR_FLAG; + else + level->mcFlags = 0; + } + ret = rv740_populate_mclk_value(rdev, pl->sclk, + pl->mclk, &level->mclk); + } else if ((rdev->family == CHIP_RV730) || (rdev->family == CHIP_RV710)) + ret = rv730_populate_mclk_value(rdev, pl->sclk, + pl->mclk, &level->mclk); + else + ret = rv770_populate_mclk_value(rdev, pl->sclk, + pl->mclk, &level->mclk); + if (ret) + return ret; + + ret = rv770_populate_vddc_value(rdev, pl->vddc, + &level->vddc); + if (ret) + return ret; + + ret = rv770_populate_mvdd_value(rdev, pl->mclk, &level->mvdd); + + return ret; +} + +static int rv770_convert_power_state_to_smc(struct radeon_device *rdev, + struct radeon_ps *radeon_state, + RV770_SMC_SWSTATE *smc_state) +{ + struct rv7xx_ps *state = rv770_get_ps(radeon_state); + int ret; + + if (!(radeon_state->caps & ATOM_PPLIB_DISALLOW_ON_DC)) + smc_state->flags |= PPSMC_SWSTATE_FLAG_DC; + + ret = rv770_convert_power_level_to_smc(rdev, + &state->low, + &smc_state->levels[0], + PPSMC_DISPLAY_WATERMARK_LOW); + if (ret) + return ret; + + ret = rv770_convert_power_level_to_smc(rdev, + &state->medium, + &smc_state->levels[1], + PPSMC_DISPLAY_WATERMARK_LOW); + if (ret) + return ret; + + ret = rv770_convert_power_level_to_smc(rdev, + &state->high, + &smc_state->levels[2], + PPSMC_DISPLAY_WATERMARK_HIGH); + if (ret) + return ret; + + smc_state->levels[0].arbValue = MC_CG_ARB_FREQ_F1; + smc_state->levels[1].arbValue = MC_CG_ARB_FREQ_F2; + smc_state->levels[2].arbValue = MC_CG_ARB_FREQ_F3; + + smc_state->levels[0].seqValue = rv770_get_seq_value(rdev, + &state->low); + smc_state->levels[1].seqValue = rv770_get_seq_value(rdev, + &state->medium); + smc_state->levels[2].seqValue = rv770_get_seq_value(rdev, + &state->high); + + rv770_populate_smc_sp(rdev, radeon_state, smc_state); + + return rv770_populate_smc_t(rdev, radeon_state, smc_state); + +} + +u32 rv770_calculate_memory_refresh_rate(struct radeon_device *rdev, + u32 engine_clock) +{ + u32 dram_rows; + u32 dram_refresh_rate; + u32 mc_arb_rfsh_rate; + u32 tmp; + + tmp = (RREG32(MC_ARB_RAMCFG) & NOOFROWS_MASK) >> NOOFROWS_SHIFT; + dram_rows = 1 << (tmp + 10); + tmp = RREG32(MC_SEQ_MISC0) & 3; + dram_refresh_rate = 1 << (tmp + 3); + mc_arb_rfsh_rate = ((engine_clock * 10) * dram_refresh_rate / dram_rows - 32) / 64; + + return mc_arb_rfsh_rate; +} + +static void rv770_program_memory_timing_parameters(struct radeon_device *rdev, + struct radeon_ps *radeon_state) +{ + struct rv7xx_ps *state = rv770_get_ps(radeon_state); + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + u32 sqm_ratio; + u32 arb_refresh_rate; + u32 high_clock; + + if (state->high.sclk < (state->low.sclk * 0xFF / 0x40)) + high_clock = state->high.sclk; + else + high_clock = (state->low.sclk * 0xFF / 0x40); + + radeon_atom_set_engine_dram_timings(rdev, high_clock, + state->high.mclk); + + sqm_ratio = + STATE0(64 * high_clock / pi->boot_sclk) | + STATE1(64 * high_clock / state->low.sclk) | + STATE2(64 * high_clock / state->medium.sclk) | + STATE3(64 * high_clock / state->high.sclk); + WREG32(MC_ARB_SQM_RATIO, sqm_ratio); + + arb_refresh_rate = + POWERMODE0(rv770_calculate_memory_refresh_rate(rdev, pi->boot_sclk)) | + POWERMODE1(rv770_calculate_memory_refresh_rate(rdev, state->low.sclk)) | + POWERMODE2(rv770_calculate_memory_refresh_rate(rdev, state->medium.sclk)) | + POWERMODE3(rv770_calculate_memory_refresh_rate(rdev, state->high.sclk)); + WREG32(MC_ARB_RFSH_RATE, arb_refresh_rate); +} + +void rv770_enable_backbias(struct radeon_device *rdev, + bool enable) +{ + if (enable) + WREG32_P(GENERAL_PWRMGT, BACKBIAS_PAD_EN, ~BACKBIAS_PAD_EN); + else + WREG32_P(GENERAL_PWRMGT, 0, ~(BACKBIAS_VALUE | BACKBIAS_PAD_EN)); +} + +static void rv770_enable_spread_spectrum(struct radeon_device *rdev, + bool enable) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + + if (enable) { + if (pi->sclk_ss) + WREG32_P(GENERAL_PWRMGT, DYN_SPREAD_SPECTRUM_EN, ~DYN_SPREAD_SPECTRUM_EN); + + if (pi->mclk_ss) { + if (rdev->family == CHIP_RV740) + rv740_enable_mclk_spread_spectrum(rdev, true); + } + } else { + WREG32_P(CG_SPLL_SPREAD_SPECTRUM, 0, ~SSEN); + + WREG32_P(GENERAL_PWRMGT, 0, ~DYN_SPREAD_SPECTRUM_EN); + + WREG32_P(CG_MPLL_SPREAD_SPECTRUM, 0, ~SSEN); + + if (rdev->family == CHIP_RV740) + rv740_enable_mclk_spread_spectrum(rdev, false); + } +} + +static void rv770_program_mpll_timing_parameters(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + + if ((rdev->family == CHIP_RV770) && !pi->mem_gddr5) { + WREG32(MPLL_TIME, + (MPLL_LOCK_TIME(R600_MPLLLOCKTIME_DFLT * pi->ref_div) | + MPLL_RESET_TIME(R600_MPLLRESETTIME_DFLT))); + } +} + +void rv770_setup_bsp(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + u32 xclk = radeon_get_xclk(rdev); + + r600_calculate_u_and_p(pi->asi, + xclk, + 16, + &pi->bsp, + &pi->bsu); + + r600_calculate_u_and_p(pi->pasi, + xclk, + 16, + &pi->pbsp, + &pi->pbsu); + + pi->dsp = BSP(pi->bsp) | BSU(pi->bsu); + pi->psp = BSP(pi->pbsp) | BSU(pi->pbsu); + + WREG32(CG_BSP, pi->dsp); + +} + +void rv770_program_git(struct radeon_device *rdev) +{ + WREG32_P(CG_GIT, CG_GICST(R600_GICST_DFLT), ~CG_GICST_MASK); +} + +void rv770_program_tp(struct radeon_device *rdev) +{ + int i; + enum r600_td td = R600_TD_DFLT; + + for (i = 0; i < R600_PM_NUMBER_OF_TC; i++) + WREG32(CG_FFCT_0 + (i * 4), (UTC_0(r600_utc[i]) | DTC_0(r600_dtc[i]))); + + if (td == R600_TD_AUTO) + WREG32_P(SCLK_PWRMGT_CNTL, 0, ~FIR_FORCE_TREND_SEL); + else + WREG32_P(SCLK_PWRMGT_CNTL, FIR_FORCE_TREND_SEL, ~FIR_FORCE_TREND_SEL); + if (td == R600_TD_UP) + WREG32_P(SCLK_PWRMGT_CNTL, 0, ~FIR_TREND_MODE); + if (td == R600_TD_DOWN) + WREG32_P(SCLK_PWRMGT_CNTL, FIR_TREND_MODE, ~FIR_TREND_MODE); +} + +void rv770_program_tpp(struct radeon_device *rdev) +{ + WREG32(CG_TPC, R600_TPC_DFLT); +} + +void rv770_program_sstp(struct radeon_device *rdev) +{ + WREG32(CG_SSP, (SSTU(R600_SSTU_DFLT) | SST(R600_SST_DFLT))); +} + +void rv770_program_engine_speed_parameters(struct radeon_device *rdev) +{ + WREG32_P(SPLL_CNTL_MODE, SPLL_DIV_SYNC, ~SPLL_DIV_SYNC); +} + +static void rv770_enable_display_gap(struct radeon_device *rdev) +{ + u32 tmp = RREG32(CG_DISPLAY_GAP_CNTL); + + tmp &= ~(DISP1_GAP_MCHG_MASK | DISP2_GAP_MCHG_MASK); + tmp |= (DISP1_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE) | + DISP2_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE)); + WREG32(CG_DISPLAY_GAP_CNTL, tmp); +} + +void rv770_program_vc(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + + WREG32(CG_FTV, pi->vrc); +} + +void rv770_clear_vc(struct radeon_device *rdev) +{ + WREG32(CG_FTV, 0); +} + +int rv770_upload_firmware(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + int ret; + + rv770_reset_smc(rdev); + rv770_stop_smc_clock(rdev); + + ret = rv770_load_smc_ucode(rdev, pi->sram_end); + if (ret) + return ret; + + return 0; +} + +static int rv770_populate_smc_acpi_state(struct radeon_device *rdev, + RV770_SMC_STATETABLE *table) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + + u32 mpll_ad_func_cntl = + pi->clk_regs.rv770.mpll_ad_func_cntl; + u32 mpll_ad_func_cntl_2 = + pi->clk_regs.rv770.mpll_ad_func_cntl_2; + u32 mpll_dq_func_cntl = + pi->clk_regs.rv770.mpll_dq_func_cntl; + u32 mpll_dq_func_cntl_2 = + pi->clk_regs.rv770.mpll_dq_func_cntl_2; + u32 spll_func_cntl = + pi->clk_regs.rv770.cg_spll_func_cntl; + u32 spll_func_cntl_2 = + pi->clk_regs.rv770.cg_spll_func_cntl_2; + u32 spll_func_cntl_3 = + pi->clk_regs.rv770.cg_spll_func_cntl_3; + u32 mclk_pwrmgt_cntl; + u32 dll_cntl; + + table->ACPIState = table->initialState; + + table->ACPIState.flags &= ~PPSMC_SWSTATE_FLAG_DC; + + if (pi->acpi_vddc) { + rv770_populate_vddc_value(rdev, pi->acpi_vddc, + &table->ACPIState.levels[0].vddc); + if (pi->pcie_gen2) { + if (pi->acpi_pcie_gen2) + table->ACPIState.levels[0].gen2PCIE = 1; + else + table->ACPIState.levels[0].gen2PCIE = 0; + } else + table->ACPIState.levels[0].gen2PCIE = 0; + if (pi->acpi_pcie_gen2) + table->ACPIState.levels[0].gen2XSP = 1; + else + table->ACPIState.levels[0].gen2XSP = 0; + } else { + rv770_populate_vddc_value(rdev, pi->min_vddc_in_table, + &table->ACPIState.levels[0].vddc); + table->ACPIState.levels[0].gen2PCIE = 0; + } + + + mpll_ad_func_cntl_2 |= BIAS_GEN_PDNB | RESET_EN; + + mpll_dq_func_cntl_2 |= BIAS_GEN_PDNB | RESET_EN; + + mclk_pwrmgt_cntl = (MRDCKA0_RESET | + MRDCKA1_RESET | + MRDCKB0_RESET | + MRDCKB1_RESET | + MRDCKC0_RESET | + MRDCKC1_RESET | + MRDCKD0_RESET | + MRDCKD1_RESET); + + dll_cntl = 0xff000000; + + spll_func_cntl |= SPLL_RESET | SPLL_SLEEP | SPLL_BYPASS_EN; + + spll_func_cntl_2 &= ~SCLK_MUX_SEL_MASK; + spll_func_cntl_2 |= SCLK_MUX_SEL(4); + + table->ACPIState.levels[0].mclk.mclk770.vMPLL_AD_FUNC_CNTL = cpu_to_be32(mpll_ad_func_cntl); + table->ACPIState.levels[0].mclk.mclk770.vMPLL_AD_FUNC_CNTL_2 = cpu_to_be32(mpll_ad_func_cntl_2); + table->ACPIState.levels[0].mclk.mclk770.vMPLL_DQ_FUNC_CNTL = cpu_to_be32(mpll_dq_func_cntl); + table->ACPIState.levels[0].mclk.mclk770.vMPLL_DQ_FUNC_CNTL_2 = cpu_to_be32(mpll_dq_func_cntl_2); + + table->ACPIState.levels[0].mclk.mclk770.vMCLK_PWRMGT_CNTL = cpu_to_be32(mclk_pwrmgt_cntl); + table->ACPIState.levels[0].mclk.mclk770.vDLL_CNTL = cpu_to_be32(dll_cntl); + + table->ACPIState.levels[0].mclk.mclk770.mclk_value = 0; + + table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL = cpu_to_be32(spll_func_cntl); + table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_2 = cpu_to_be32(spll_func_cntl_2); + table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_3 = cpu_to_be32(spll_func_cntl_3); + + table->ACPIState.levels[0].sclk.sclk_value = 0; + + rv770_populate_mvdd_value(rdev, 0, &table->ACPIState.levels[0].mvdd); + + table->ACPIState.levels[1] = table->ACPIState.levels[0]; + table->ACPIState.levels[2] = table->ACPIState.levels[0]; + + return 0; +} + +int rv770_populate_initial_mvdd_value(struct radeon_device *rdev, + RV770_SMC_VOLTAGE_VALUE *voltage) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + + if ((pi->s0_vid_lower_smio_cntl & pi->mvdd_mask_low) == + (pi->mvdd_low_smio[MVDD_LOW_INDEX] & pi->mvdd_mask_low) ) { + voltage->index = MVDD_LOW_INDEX; + voltage->value = cpu_to_be16(MVDD_LOW_VALUE); + } else { + voltage->index = MVDD_HIGH_INDEX; + voltage->value = cpu_to_be16(MVDD_HIGH_VALUE); + } + + return 0; +} + +static int rv770_populate_smc_initial_state(struct radeon_device *rdev, + struct radeon_ps *radeon_state, + RV770_SMC_STATETABLE *table) +{ + struct rv7xx_ps *initial_state = rv770_get_ps(radeon_state); + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + u32 a_t; + + table->initialState.levels[0].mclk.mclk770.vMPLL_AD_FUNC_CNTL = + cpu_to_be32(pi->clk_regs.rv770.mpll_ad_func_cntl); + table->initialState.levels[0].mclk.mclk770.vMPLL_AD_FUNC_CNTL_2 = + cpu_to_be32(pi->clk_regs.rv770.mpll_ad_func_cntl_2); + table->initialState.levels[0].mclk.mclk770.vMPLL_DQ_FUNC_CNTL = + cpu_to_be32(pi->clk_regs.rv770.mpll_dq_func_cntl); + table->initialState.levels[0].mclk.mclk770.vMPLL_DQ_FUNC_CNTL_2 = + cpu_to_be32(pi->clk_regs.rv770.mpll_dq_func_cntl_2); + table->initialState.levels[0].mclk.mclk770.vMCLK_PWRMGT_CNTL = + cpu_to_be32(pi->clk_regs.rv770.mclk_pwrmgt_cntl); + table->initialState.levels[0].mclk.mclk770.vDLL_CNTL = + cpu_to_be32(pi->clk_regs.rv770.dll_cntl); + + table->initialState.levels[0].mclk.mclk770.vMPLL_SS = + cpu_to_be32(pi->clk_regs.rv770.mpll_ss1); + table->initialState.levels[0].mclk.mclk770.vMPLL_SS2 = + cpu_to_be32(pi->clk_regs.rv770.mpll_ss2); + + table->initialState.levels[0].mclk.mclk770.mclk_value = + cpu_to_be32(initial_state->low.mclk); + + table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL = + cpu_to_be32(pi->clk_regs.rv770.cg_spll_func_cntl); + table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_2 = + cpu_to_be32(pi->clk_regs.rv770.cg_spll_func_cntl_2); + table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_3 = + cpu_to_be32(pi->clk_regs.rv770.cg_spll_func_cntl_3); + table->initialState.levels[0].sclk.vCG_SPLL_SPREAD_SPECTRUM = + cpu_to_be32(pi->clk_regs.rv770.cg_spll_spread_spectrum); + table->initialState.levels[0].sclk.vCG_SPLL_SPREAD_SPECTRUM_2 = + cpu_to_be32(pi->clk_regs.rv770.cg_spll_spread_spectrum_2); + + table->initialState.levels[0].sclk.sclk_value = + cpu_to_be32(initial_state->low.sclk); + + table->initialState.levels[0].arbValue = MC_CG_ARB_FREQ_F0; + + table->initialState.levels[0].seqValue = + rv770_get_seq_value(rdev, &initial_state->low); + + rv770_populate_vddc_value(rdev, + initial_state->low.vddc, + &table->initialState.levels[0].vddc); + rv770_populate_initial_mvdd_value(rdev, + &table->initialState.levels[0].mvdd); + + a_t = CG_R(0xffff) | CG_L(0); + table->initialState.levels[0].aT = cpu_to_be32(a_t); + + table->initialState.levels[0].bSP = cpu_to_be32(pi->dsp); + + if (pi->boot_in_gen2) + table->initialState.levels[0].gen2PCIE = 1; + else + table->initialState.levels[0].gen2PCIE = 0; + if (initial_state->low.flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) + table->initialState.levels[0].gen2XSP = 1; + else + table->initialState.levels[0].gen2XSP = 0; + + if (rdev->family == CHIP_RV740) { + if (pi->mem_gddr5) { + if (initial_state->low.mclk <= pi->mclk_strobe_mode_threshold) + table->initialState.levels[0].strobeMode = + rv740_get_mclk_frequency_ratio(initial_state->low.mclk) | 0x10; + else + table->initialState.levels[0].strobeMode = 0; + + if (initial_state->low.mclk >= pi->mclk_edc_enable_threshold) + table->initialState.levels[0].mcFlags = SMC_MC_EDC_RD_FLAG | SMC_MC_EDC_WR_FLAG; + else + table->initialState.levels[0].mcFlags = 0; + } + } + + table->initialState.levels[1] = table->initialState.levels[0]; + table->initialState.levels[2] = table->initialState.levels[0]; + + table->initialState.flags |= PPSMC_SWSTATE_FLAG_DC; + + return 0; +} + +static int rv770_populate_smc_vddc_table(struct radeon_device *rdev, + RV770_SMC_STATETABLE *table) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + int i; + + for (i = 0; i < pi->valid_vddc_entries; i++) { + table->highSMIO[pi->vddc_table[i].vddc_index] = + pi->vddc_table[i].high_smio; + table->lowSMIO[pi->vddc_table[i].vddc_index] = + cpu_to_be32(pi->vddc_table[i].low_smio); + } + + table->voltageMaskTable.highMask[RV770_SMC_VOLTAGEMASK_VDDC] = 0; + table->voltageMaskTable.lowMask[RV770_SMC_VOLTAGEMASK_VDDC] = + cpu_to_be32(pi->vddc_mask_low); + + for (i = 0; + ((i < pi->valid_vddc_entries) && + (pi->max_vddc_in_table > + pi->vddc_table[i].vddc)); + i++); + + table->maxVDDCIndexInPPTable = + pi->vddc_table[i].vddc_index; + + return 0; +} + +static int rv770_populate_smc_mvdd_table(struct radeon_device *rdev, + RV770_SMC_STATETABLE *table) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + + if (pi->mvdd_control) { + table->lowSMIO[MVDD_HIGH_INDEX] |= + cpu_to_be32(pi->mvdd_low_smio[MVDD_HIGH_INDEX]); + table->lowSMIO[MVDD_LOW_INDEX] |= + cpu_to_be32(pi->mvdd_low_smio[MVDD_LOW_INDEX]); + + table->voltageMaskTable.highMask[RV770_SMC_VOLTAGEMASK_MVDD] = 0; + table->voltageMaskTable.lowMask[RV770_SMC_VOLTAGEMASK_MVDD] = + cpu_to_be32(pi->mvdd_mask_low); + } + + return 0; +} + +static int rv770_init_smc_table(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct radeon_ps *radeon_boot_state = rdev->pm.dpm.boot_ps; + struct rv7xx_ps *boot_state = rv770_get_ps(radeon_boot_state); + RV770_SMC_STATETABLE *table = &pi->smc_statetable; + int ret; + + memset(table, 0, sizeof(RV770_SMC_STATETABLE)); + + pi->boot_sclk = boot_state->low.sclk; + + rv770_populate_smc_vddc_table(rdev, table); + rv770_populate_smc_mvdd_table(rdev, table); + + switch (rdev->pm.int_thermal_type) { + case THERMAL_TYPE_RV770: + case THERMAL_TYPE_ADT7473_WITH_INTERNAL: + table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_INTERNAL; + break; + case THERMAL_TYPE_NONE: + table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_NONE; + break; + case THERMAL_TYPE_EXTERNAL_GPIO: + default: + table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_EXTERNAL; + break; + } + + if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_HARDWAREDC) { + table->systemFlags |= PPSMC_SYSTEMFLAG_GPIO_DC; + + if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_DONT_WAIT_FOR_VBLANK_ON_ALERT) + table->extraFlags |= PPSMC_EXTRAFLAGS_AC2DC_DONT_WAIT_FOR_VBLANK; + + if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_GOTO_BOOT_ON_ALERT) + table->extraFlags |= PPSMC_EXTRAFLAGS_AC2DC_ACTION_GOTOINITIALSTATE; + } + + if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_STEPVDDC) + table->systemFlags |= PPSMC_SYSTEMFLAG_STEPVDDC; + + if (pi->mem_gddr5) + table->systemFlags |= PPSMC_SYSTEMFLAG_GDDR5; + + if ((rdev->family == CHIP_RV730) || (rdev->family == CHIP_RV710)) + ret = rv730_populate_smc_initial_state(rdev, radeon_boot_state, table); + else + ret = rv770_populate_smc_initial_state(rdev, radeon_boot_state, table); + if (ret) + return ret; + + if (rdev->family == CHIP_RV740) + ret = rv740_populate_smc_acpi_state(rdev, table); + else if ((rdev->family == CHIP_RV730) || (rdev->family == CHIP_RV710)) + ret = rv730_populate_smc_acpi_state(rdev, table); + else + ret = rv770_populate_smc_acpi_state(rdev, table); + if (ret) + return ret; + + table->driverState = table->initialState; + + return rv770_copy_bytes_to_smc(rdev, + pi->state_table_start, + (const u8 *)table, + sizeof(RV770_SMC_STATETABLE), + pi->sram_end); +} + +static int rv770_construct_vddc_table(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + u16 min, max, step; + u32 steps = 0; + u8 vddc_index = 0; + u32 i; + + radeon_atom_get_min_voltage(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC, &min); + radeon_atom_get_max_voltage(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC, &max); + radeon_atom_get_voltage_step(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC, &step); + + steps = (max - min) / step + 1; + + if (steps > MAX_NO_VREG_STEPS) + return -EINVAL; + + for (i = 0; i < steps; i++) { + u32 gpio_pins, gpio_mask; + + pi->vddc_table[i].vddc = (u16)(min + i * step); + radeon_atom_get_voltage_gpio_settings(rdev, + pi->vddc_table[i].vddc, + SET_VOLTAGE_TYPE_ASIC_VDDC, + &gpio_pins, &gpio_mask); + pi->vddc_table[i].low_smio = gpio_pins & gpio_mask; + pi->vddc_table[i].high_smio = 0; + pi->vddc_mask_low = gpio_mask; + if (i > 0) { + if ((pi->vddc_table[i].low_smio != + pi->vddc_table[i - 1].low_smio ) || + (pi->vddc_table[i].high_smio != + pi->vddc_table[i - 1].high_smio)) + vddc_index++; + } + pi->vddc_table[i].vddc_index = vddc_index; + } + + pi->valid_vddc_entries = (u8)steps; + + return 0; +} + +static u32 rv770_get_mclk_split_point(struct atom_memory_info *memory_info) +{ + if (memory_info->mem_type == MEM_TYPE_GDDR3) + return 30000; + + return 0; +} + +static int rv770_get_mvdd_pin_configuration(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + u32 gpio_pins, gpio_mask; + + radeon_atom_get_voltage_gpio_settings(rdev, + MVDD_HIGH_VALUE, SET_VOLTAGE_TYPE_ASIC_MVDDC, + &gpio_pins, &gpio_mask); + pi->mvdd_mask_low = gpio_mask; + pi->mvdd_low_smio[MVDD_HIGH_INDEX] = + gpio_pins & gpio_mask; + + radeon_atom_get_voltage_gpio_settings(rdev, + MVDD_LOW_VALUE, SET_VOLTAGE_TYPE_ASIC_MVDDC, + &gpio_pins, &gpio_mask); + pi->mvdd_low_smio[MVDD_LOW_INDEX] = + gpio_pins & gpio_mask; + + return 0; +} + +u8 rv770_get_memory_module_index(struct radeon_device *rdev) +{ + return (u8) ((RREG32(BIOS_SCRATCH_4) >> 16) & 0xff); +} + +static int rv770_get_mvdd_configuration(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + u8 memory_module_index; + struct atom_memory_info memory_info; + + memory_module_index = rv770_get_memory_module_index(rdev); + + if (radeon_atom_get_memory_info(rdev, memory_module_index, &memory_info)) { + pi->mvdd_control = false; + return 0; + } + + pi->mvdd_split_frequency = + rv770_get_mclk_split_point(&memory_info); + + if (pi->mvdd_split_frequency == 0) { + pi->mvdd_control = false; + return 0; + } + + return rv770_get_mvdd_pin_configuration(rdev); +} + +void rv770_enable_voltage_control(struct radeon_device *rdev, + bool enable) +{ + if (enable) + WREG32_P(GENERAL_PWRMGT, VOLT_PWRMGT_EN, ~VOLT_PWRMGT_EN); + else + WREG32_P(GENERAL_PWRMGT, 0, ~VOLT_PWRMGT_EN); +} + +static void rv770_program_display_gap(struct radeon_device *rdev) +{ + u32 tmp = RREG32(CG_DISPLAY_GAP_CNTL); + + tmp &= ~(DISP1_GAP_MCHG_MASK | DISP2_GAP_MCHG_MASK); + if (RREG32(AVIVO_D1CRTC_CONTROL) & AVIVO_CRTC_EN) { + tmp |= DISP1_GAP_MCHG(R600_PM_DISPLAY_GAP_VBLANK); + tmp |= DISP2_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE); + } else if (RREG32(AVIVO_D2CRTC_CONTROL) & AVIVO_CRTC_EN) { + tmp |= DISP1_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE); + tmp |= DISP2_GAP_MCHG(R600_PM_DISPLAY_GAP_VBLANK); + } else { + tmp |= DISP1_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE); + tmp |= DISP2_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE); + } + WREG32(CG_DISPLAY_GAP_CNTL, tmp); +} + +static void rv770_enable_dynamic_pcie_gen2(struct radeon_device *rdev, + bool enable) +{ + rv770_enable_bif_dynamic_pcie_gen2(rdev, enable); + + if (enable) + WREG32_P(GENERAL_PWRMGT, ENABLE_GEN2PCIE, ~ENABLE_GEN2PCIE); + else + WREG32_P(GENERAL_PWRMGT, 0, ~ENABLE_GEN2PCIE); +} + +static void r7xx_program_memory_timing_parameters(struct radeon_device *rdev) +{ + struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; + + if ((rdev->family == CHIP_RV730) || + (rdev->family == CHIP_RV710) || + (rdev->family == CHIP_RV740)) + rv730_program_memory_timing_parameters(rdev, radeon_new_state); + else + rv770_program_memory_timing_parameters(rdev, radeon_new_state); +} + +static int rv770_upload_sw_state(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; + u16 address = pi->state_table_start + + offsetof(RV770_SMC_STATETABLE, driverState); + RV770_SMC_SWSTATE state = { 0 }; + int ret; + + ret = rv770_convert_power_state_to_smc(rdev, radeon_new_state, &state); + if (ret) + return ret; + + return rv770_copy_bytes_to_smc(rdev, address, (const u8 *)&state, + sizeof(RV770_SMC_SWSTATE), + pi->sram_end); +} + +int rv770_halt_smc(struct radeon_device *rdev) +{ + if (rv770_send_msg_to_smc(rdev, PPSMC_MSG_Halt) != PPSMC_Result_OK) + return -EINVAL; + + if (rv770_wait_for_smc_inactive(rdev) != PPSMC_Result_OK) + return -EINVAL; + + return 0; +} + +int rv770_resume_smc(struct radeon_device *rdev) +{ + if (rv770_send_msg_to_smc(rdev, PPSMC_MSG_Resume) != PPSMC_Result_OK) + return -EINVAL; + return 0; +} + +int rv770_set_sw_state(struct radeon_device *rdev) +{ + if (rv770_send_msg_to_smc(rdev, PPSMC_MSG_SwitchToSwState) != PPSMC_Result_OK) + return -EINVAL; + return 0; +} + +int rv770_set_boot_state(struct radeon_device *rdev) +{ + if (rv770_send_msg_to_smc(rdev, PPSMC_MSG_SwitchToInitialState) != PPSMC_Result_OK) + return -EINVAL; + return 0; +} + +int rv770_restrict_performance_levels_before_switch(struct radeon_device *rdev) +{ + if (rv770_send_msg_to_smc(rdev, (PPSMC_Msg)(PPSMC_MSG_NoForcedLevel)) != PPSMC_Result_OK) + return -EINVAL; + + if (rv770_send_msg_to_smc(rdev, (PPSMC_Msg)(PPSMC_MSG_TwoLevelsDisabled)) != PPSMC_Result_OK) + return -EINVAL; + + return 0; +} + +void r7xx_start_smc(struct radeon_device *rdev) +{ + rv770_start_smc(rdev); + rv770_start_smc_clock(rdev); +} + + +void r7xx_stop_smc(struct radeon_device *rdev) +{ + rv770_reset_smc(rdev); + rv770_stop_smc_clock(rdev); +} + +static void rv770_read_clock_registers(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + + pi->clk_regs.rv770.cg_spll_func_cntl = + RREG32(CG_SPLL_FUNC_CNTL); + pi->clk_regs.rv770.cg_spll_func_cntl_2 = + RREG32(CG_SPLL_FUNC_CNTL_2); + pi->clk_regs.rv770.cg_spll_func_cntl_3 = + RREG32(CG_SPLL_FUNC_CNTL_3); + pi->clk_regs.rv770.cg_spll_spread_spectrum = + RREG32(CG_SPLL_SPREAD_SPECTRUM); + pi->clk_regs.rv770.cg_spll_spread_spectrum_2 = + RREG32(CG_SPLL_SPREAD_SPECTRUM_2); + pi->clk_regs.rv770.mpll_ad_func_cntl = + RREG32(MPLL_AD_FUNC_CNTL); + pi->clk_regs.rv770.mpll_ad_func_cntl_2 = + RREG32(MPLL_AD_FUNC_CNTL_2); + pi->clk_regs.rv770.mpll_dq_func_cntl = + RREG32(MPLL_DQ_FUNC_CNTL); + pi->clk_regs.rv770.mpll_dq_func_cntl_2 = + RREG32(MPLL_DQ_FUNC_CNTL_2); + pi->clk_regs.rv770.mclk_pwrmgt_cntl = + RREG32(MCLK_PWRMGT_CNTL); + pi->clk_regs.rv770.dll_cntl = RREG32(DLL_CNTL); +} + +static void r7xx_read_clock_registers(struct radeon_device *rdev) +{ + if (rdev->family == CHIP_RV740) + rv740_read_clock_registers(rdev); + else if ((rdev->family == CHIP_RV730) || (rdev->family == CHIP_RV710)) + rv730_read_clock_registers(rdev); + else + rv770_read_clock_registers(rdev); +} + +void rv770_read_voltage_smio_registers(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + + pi->s0_vid_lower_smio_cntl = + RREG32(S0_VID_LOWER_SMIO_CNTL); +} + +void rv770_reset_smio_status(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + u32 sw_smio_index, vid_smio_cntl; + + sw_smio_index = + (RREG32(GENERAL_PWRMGT) & SW_SMIO_INDEX_MASK) >> SW_SMIO_INDEX_SHIFT; + switch (sw_smio_index) { + case 3: + vid_smio_cntl = RREG32(S3_VID_LOWER_SMIO_CNTL); + break; + case 2: + vid_smio_cntl = RREG32(S2_VID_LOWER_SMIO_CNTL); + break; + case 1: + vid_smio_cntl = RREG32(S1_VID_LOWER_SMIO_CNTL); + break; + case 0: + return; + default: + vid_smio_cntl = pi->s0_vid_lower_smio_cntl; + break; + } + + WREG32(S0_VID_LOWER_SMIO_CNTL, vid_smio_cntl); + WREG32_P(GENERAL_PWRMGT, SW_SMIO_INDEX(0), ~SW_SMIO_INDEX_MASK); +} + +void rv770_get_memory_type(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + u32 tmp; + + tmp = RREG32(MC_SEQ_MISC0); + + if (((tmp & MC_SEQ_MISC0_GDDR5_MASK) >> MC_SEQ_MISC0_GDDR5_SHIFT) == + MC_SEQ_MISC0_GDDR5_VALUE) + pi->mem_gddr5 = true; + else + pi->mem_gddr5 = false; + +} + +void rv770_get_pcie_gen2_status(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + u32 tmp; + + tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL); + + if ((tmp & LC_OTHER_SIDE_EVER_SENT_GEN2) && + (tmp & LC_OTHER_SIDE_SUPPORTS_GEN2)) + pi->pcie_gen2 = true; + else + pi->pcie_gen2 = false; + + if (pi->pcie_gen2) { + if (tmp & LC_CURRENT_DATA_RATE) + pi->boot_in_gen2 = true; + else + pi->boot_in_gen2 = false; + } else + pi->boot_in_gen2 = false; +} + +#if 0 +static int rv770_enter_ulp_state(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + + if (pi->gfx_clock_gating) { + WREG32_P(SCLK_PWRMGT_CNTL, 0, ~DYN_GFX_CLK_OFF_EN); + WREG32_P(SCLK_PWRMGT_CNTL, GFX_CLK_FORCE_ON, ~GFX_CLK_FORCE_ON); + WREG32_P(SCLK_PWRMGT_CNTL, 0, ~GFX_CLK_FORCE_ON); + RREG32(GB_TILING_CONFIG); + } + + WREG32_P(SMC_MSG, HOST_SMC_MSG(PPSMC_MSG_SwitchToMinimumPower), + ~HOST_SMC_MSG_MASK); + + udelay(7000); + + return 0; +} + +static int rv770_exit_ulp_state(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + int i; + + WREG32_P(SMC_MSG, HOST_SMC_MSG(PPSMC_MSG_ResumeFromMinimumPower), + ~HOST_SMC_MSG_MASK); + + udelay(7000); + + for (i = 0; i < rdev->usec_timeout; i++) { + if (((RREG32(SMC_MSG) & HOST_SMC_RESP_MASK) >> HOST_SMC_RESP_SHIFT) == 1) + break; + udelay(1000); + } + + if (pi->gfx_clock_gating) + WREG32_P(SCLK_PWRMGT_CNTL, DYN_GFX_CLK_OFF_EN, ~DYN_GFX_CLK_OFF_EN); + + return 0; +} +#endif + +static void rv770_get_mclk_odt_threshold(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + u8 memory_module_index; + struct atom_memory_info memory_info; + + pi->mclk_odt_threshold = 0; + + if ((rdev->family == CHIP_RV730) || (rdev->family == CHIP_RV710)) { + memory_module_index = rv770_get_memory_module_index(rdev); + + if (radeon_atom_get_memory_info(rdev, memory_module_index, &memory_info)) + return; + + if (memory_info.mem_type == MEM_TYPE_DDR2 || + memory_info.mem_type == MEM_TYPE_DDR3) + pi->mclk_odt_threshold = 30000; + } +} + +void rv770_get_max_vddc(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + u16 vddc; + + if (radeon_atom_get_max_vddc(rdev, 0, 0, &vddc)) + pi->max_vddc = 0; + else + pi->max_vddc = vddc; +} + +void rv770_program_response_times(struct radeon_device *rdev) +{ + u32 voltage_response_time, backbias_response_time; + u32 acpi_delay_time, vbi_time_out; + u32 vddc_dly, bb_dly, acpi_dly, vbi_dly; + u32 reference_clock; + + voltage_response_time = (u32)rdev->pm.dpm.voltage_response_time; + backbias_response_time = (u32)rdev->pm.dpm.backbias_response_time; + + if (voltage_response_time == 0) + voltage_response_time = 1000; + + if (backbias_response_time == 0) + backbias_response_time = 1000; + + acpi_delay_time = 15000; + vbi_time_out = 100000; + + reference_clock = radeon_get_xclk(rdev); + + vddc_dly = (voltage_response_time * reference_clock) / 1600; + bb_dly = (backbias_response_time * reference_clock) / 1600; + acpi_dly = (acpi_delay_time * reference_clock) / 1600; + vbi_dly = (vbi_time_out * reference_clock) / 1600; + + rv770_write_smc_soft_register(rdev, + RV770_SMC_SOFT_REGISTER_delay_vreg, vddc_dly); + rv770_write_smc_soft_register(rdev, + RV770_SMC_SOFT_REGISTER_delay_bbias, bb_dly); + rv770_write_smc_soft_register(rdev, + RV770_SMC_SOFT_REGISTER_delay_acpi, acpi_dly); + rv770_write_smc_soft_register(rdev, + RV770_SMC_SOFT_REGISTER_mclk_chg_timeout, vbi_dly); +#if 0 + /* XXX look up hw revision */ + if (WEKIVA_A21) + rv770_write_smc_soft_register(rdev, + RV770_SMC_SOFT_REGISTER_baby_step_timer, + 0x10); +#endif +} + +static void rv770_program_dcodt_before_state_switch(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; + struct radeon_ps *radeon_current_state = rdev->pm.dpm.current_ps; + struct rv7xx_ps *new_state = rv770_get_ps(radeon_new_state); + struct rv7xx_ps *current_state = rv770_get_ps(radeon_current_state); + bool current_use_dc = false; + bool new_use_dc = false; + + if (pi->mclk_odt_threshold == 0) + return; + + if (current_state->high.mclk <= pi->mclk_odt_threshold) + current_use_dc = true; + + if (new_state->high.mclk <= pi->mclk_odt_threshold) + new_use_dc = true; + + if (current_use_dc == new_use_dc) + return; + + if (!current_use_dc && new_use_dc) + return; + + if ((rdev->family == CHIP_RV730) || (rdev->family == CHIP_RV710)) + rv730_program_dcodt(rdev, new_use_dc); +} + +static void rv770_program_dcodt_after_state_switch(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; + struct radeon_ps *radeon_current_state = rdev->pm.dpm.current_ps; + struct rv7xx_ps *new_state = rv770_get_ps(radeon_new_state); + struct rv7xx_ps *current_state = rv770_get_ps(radeon_current_state); + bool current_use_dc = false; + bool new_use_dc = false; + + if (pi->mclk_odt_threshold == 0) + return; + + if (current_state->high.mclk <= pi->mclk_odt_threshold) + current_use_dc = true; + + if (new_state->high.mclk <= pi->mclk_odt_threshold) + new_use_dc = true; + + if (current_use_dc == new_use_dc) + return; + + if (current_use_dc && !new_use_dc) + return; + + if ((rdev->family == CHIP_RV730) || (rdev->family == CHIP_RV710)) + rv730_program_dcodt(rdev, new_use_dc); +} + +static void rv770_retrieve_odt_values(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + + if (pi->mclk_odt_threshold == 0) + return; + + if ((rdev->family == CHIP_RV730) || (rdev->family == CHIP_RV710)) + rv730_get_odt_values(rdev); +} + +static void rv770_set_dpm_event_sources(struct radeon_device *rdev, u32 sources) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + bool want_thermal_protection; + enum radeon_dpm_event_src dpm_event_src; + + switch (sources) { + case 0: + default: + want_thermal_protection = false; + break; + case (1 << RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL): + want_thermal_protection = true; + dpm_event_src = RADEON_DPM_EVENT_SRC_DIGITAL; + break; + + case (1 << RADEON_DPM_AUTO_THROTTLE_SRC_EXTERNAL): + want_thermal_protection = true; + dpm_event_src = RADEON_DPM_EVENT_SRC_EXTERNAL; + break; + + case ((1 << RADEON_DPM_AUTO_THROTTLE_SRC_EXTERNAL) | + (1 << RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL)): + want_thermal_protection = true; + dpm_event_src = RADEON_DPM_EVENT_SRC_DIGIAL_OR_EXTERNAL; + break; + } + + if (want_thermal_protection) { + WREG32_P(CG_THERMAL_CTRL, DPM_EVENT_SRC(dpm_event_src), ~DPM_EVENT_SRC_MASK); + if (pi->thermal_protection) + WREG32_P(GENERAL_PWRMGT, 0, ~THERMAL_PROTECTION_DIS); + } else { + WREG32_P(GENERAL_PWRMGT, THERMAL_PROTECTION_DIS, ~THERMAL_PROTECTION_DIS); + } +} + +void rv770_enable_auto_throttle_source(struct radeon_device *rdev, + enum radeon_dpm_auto_throttle_src source, + bool enable) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + + if (enable) { + if (!(pi->active_auto_throttle_sources & (1 << source))) { + pi->active_auto_throttle_sources |= 1 << source; + rv770_set_dpm_event_sources(rdev, pi->active_auto_throttle_sources); + } + } else { + if (pi->active_auto_throttle_sources & (1 << source)) { + pi->active_auto_throttle_sources &= ~(1 << source); + rv770_set_dpm_event_sources(rdev, pi->active_auto_throttle_sources); + } + } +} + +static int rv770_set_thermal_temperature_range(struct radeon_device *rdev, + int min_temp, int max_temp) +{ + int low_temp = 0 * 1000; + int high_temp = 255 * 1000; + + if (low_temp < min_temp) + low_temp = min_temp; + if (high_temp > max_temp) + high_temp = max_temp; + if (high_temp < low_temp) { + DRM_ERROR("invalid thermal range: %d - %d\n", low_temp, high_temp); + return -EINVAL; + } + + WREG32_P(CG_THERMAL_INT, DIG_THERM_INTH(high_temp / 1000), ~DIG_THERM_INTH_MASK); + WREG32_P(CG_THERMAL_INT, DIG_THERM_INTL(low_temp / 1000), ~DIG_THERM_INTL_MASK); + WREG32_P(CG_THERMAL_CTRL, DIG_THERM_DPM(high_temp / 1000), ~DIG_THERM_DPM_MASK); + + rdev->pm.dpm.thermal.min_temp = low_temp; + rdev->pm.dpm.thermal.max_temp = high_temp; + + return 0; +} + +int rv770_dpm_enable(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + + if (pi->gfx_clock_gating) + rv770_restore_cgcg(rdev); + + if (rv770_dpm_enabled(rdev)) + return -EINVAL; + + if (pi->voltage_control) { + rv770_enable_voltage_control(rdev, true); + rv770_construct_vddc_table(rdev); + } + + if (pi->dcodt) + rv770_retrieve_odt_values(rdev); + + if (pi->mvdd_control) + rv770_get_mvdd_configuration(rdev); + + if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_BACKBIAS) + rv770_enable_backbias(rdev, true); + + rv770_enable_spread_spectrum(rdev, true); + + if (pi->thermal_protection) + rv770_enable_thermal_protection(rdev, true); + + rv770_program_mpll_timing_parameters(rdev); + rv770_setup_bsp(rdev); + rv770_program_git(rdev); + rv770_program_tp(rdev); + rv770_program_tpp(rdev); + rv770_program_sstp(rdev); + rv770_program_engine_speed_parameters(rdev); + rv770_enable_display_gap(rdev); + rv770_program_vc(rdev); + + if (pi->dynamic_pcie_gen2) + rv770_enable_dynamic_pcie_gen2(rdev, true); + + if (rv770_upload_firmware(rdev)) + return -EINVAL; + /* get ucode version ? */ + if (rv770_init_smc_table(rdev)) + return -EINVAL; + rv770_program_response_times(rdev); + r7xx_start_smc(rdev); + + if ((rdev->family == CHIP_RV730) || (rdev->family == CHIP_RV710)) + rv730_start_dpm(rdev); + else + rv770_start_dpm(rdev); + + if (pi->gfx_clock_gating) + rv770_gfx_clock_gating_enable(rdev, true); + + if (pi->mg_clock_gating) + rv770_mg_clock_gating_enable(rdev, true); + + if (rdev->irq.installed && + r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) { + PPSMC_Result result; + + rv770_set_thermal_temperature_range(rdev, R600_TEMP_RANGE_MIN, R600_TEMP_RANGE_MAX); + rdev->irq.dpm_thermal = true; + radeon_irq_set(rdev); + result = rv770_send_msg_to_smc(rdev, PPSMC_MSG_EnableThermalInterrupt); + + if (result != PPSMC_Result_OK) + DRM_DEBUG_KMS("Could not enable thermal interrupts.\n"); + } + + rv770_enable_auto_throttle_source(rdev, RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL, true); + + return 0; +} + +void rv770_dpm_disable(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + + if (!rv770_dpm_enabled(rdev)) + return; + + rv770_clear_vc(rdev); + + if (pi->thermal_protection) + rv770_enable_thermal_protection(rdev, false); + + rv770_enable_spread_spectrum(rdev, false); + + if (pi->dynamic_pcie_gen2) + rv770_enable_dynamic_pcie_gen2(rdev, false); + + if (rdev->irq.installed && + r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) { + rdev->irq.dpm_thermal = false; + radeon_irq_set(rdev); + } + + if (pi->gfx_clock_gating) + rv770_gfx_clock_gating_enable(rdev, false); + + if (pi->mg_clock_gating) + rv770_mg_clock_gating_enable(rdev, false); + + if ((rdev->family == CHIP_RV730) || (rdev->family == CHIP_RV710)) + rv730_stop_dpm(rdev); + else + rv770_stop_dpm(rdev); + + r7xx_stop_smc(rdev); + rv770_reset_smio_status(rdev); +} + +int rv770_dpm_set_power_state(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + + rv770_restrict_performance_levels_before_switch(rdev); + rv770_halt_smc(rdev); + rv770_upload_sw_state(rdev); + r7xx_program_memory_timing_parameters(rdev); + if (pi->dcodt) + rv770_program_dcodt_before_state_switch(rdev); + rv770_resume_smc(rdev); + rv770_set_sw_state(rdev); + if (pi->dcodt) + rv770_program_dcodt_after_state_switch(rdev); + + return 0; +} + +void rv770_dpm_reset_asic(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + + rv770_restrict_performance_levels_before_switch(rdev); + if (pi->dcodt) + rv770_program_dcodt_before_state_switch(rdev); + rv770_set_boot_state(rdev); + if (pi->dcodt) + rv770_program_dcodt_after_state_switch(rdev); +} + +void rv770_dpm_setup_asic(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + + r7xx_read_clock_registers(rdev); + rv770_read_voltage_smio_registers(rdev); + rv770_get_memory_type(rdev); + if (pi->dcodt) + rv770_get_mclk_odt_threshold(rdev); + rv770_get_pcie_gen2_status(rdev); + + rv770_enable_acpi_pm(rdev); + + if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_ASPM_L0s) + rv770_enable_l0s(rdev); + if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_ASPM_L1) + rv770_enable_l1(rdev); + if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_TURNOFFPLL_ASPML1) + rv770_enable_pll_sleep_in_l1(rdev); +} + +void rv770_dpm_display_configuration_changed(struct radeon_device *rdev) +{ + rv770_program_display_gap(rdev); +} + +union power_info { + struct _ATOM_POWERPLAY_INFO info; + struct _ATOM_POWERPLAY_INFO_V2 info_2; + struct _ATOM_POWERPLAY_INFO_V3 info_3; + struct _ATOM_PPLIB_POWERPLAYTABLE pplib; + struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2; + struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3; +}; + +union pplib_clock_info { + struct _ATOM_PPLIB_R600_CLOCK_INFO r600; + struct _ATOM_PPLIB_RS780_CLOCK_INFO rs780; + struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO evergreen; + struct _ATOM_PPLIB_SUMO_CLOCK_INFO sumo; +}; + +union pplib_power_state { + struct _ATOM_PPLIB_STATE v1; + struct _ATOM_PPLIB_STATE_V2 v2; +}; + +static void rv7xx_parse_pplib_non_clock_info(struct radeon_device *rdev, + struct radeon_ps *rps, + struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info, + u8 table_rev) +{ + rps->caps = le32_to_cpu(non_clock_info->ulCapsAndSettings); + rps->class = le16_to_cpu(non_clock_info->usClassification); + rps->class2 = le16_to_cpu(non_clock_info->usClassification2); + + if (ATOM_PPLIB_NONCLOCKINFO_VER1 < table_rev) { + rps->vclk = le32_to_cpu(non_clock_info->ulVCLK); + rps->dclk = le32_to_cpu(non_clock_info->ulDCLK); + } else if (r600_is_uvd_state(rps->class, rps->class2)) { + rps->vclk = RV770_DEFAULT_VCLK_FREQ; + rps->dclk = RV770_DEFAULT_DCLK_FREQ; + } else { + rps->vclk = 0; + rps->dclk = 0; + } + + if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT) + rdev->pm.dpm.boot_ps = rps; + if (rps->class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE) + rdev->pm.dpm.uvd_ps = rps; +} + +static void rv7xx_parse_pplib_clock_info(struct radeon_device *rdev, + struct radeon_ps *rps, int index, + union pplib_clock_info *clock_info) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct rv7xx_ps *ps = rv770_get_ps(rps); + u32 sclk, mclk; + u16 vddc; + struct rv7xx_pl *pl; + + switch (index) { + case 0: + pl = &ps->low; + break; + case 1: + pl = &ps->medium; + break; + case 2: + default: + pl = &ps->high; + break; + } + + sclk = le16_to_cpu(clock_info->r600.usEngineClockLow); + sclk |= clock_info->r600.ucEngineClockHigh << 16; + mclk = le16_to_cpu(clock_info->r600.usMemoryClockLow); + mclk |= clock_info->r600.ucMemoryClockHigh << 16; + + pl->vddc = le16_to_cpu(clock_info->r600.usVDDC); + pl->flags = le32_to_cpu(clock_info->r600.ulFlags); + + pl->mclk = mclk; + pl->sclk = sclk; + + /* patch up vddc if necessary */ + if (pl->vddc == 0xff01) { + if (radeon_atom_get_max_vddc(rdev, 0, 0, &vddc) == 0) + pl->vddc = vddc; + } + + if (rps->class & ATOM_PPLIB_CLASSIFICATION_ACPI) { + pi->acpi_vddc = pl->vddc; + if (ps->low.flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) + pi->acpi_pcie_gen2 = true; + else + pi->acpi_pcie_gen2 = false; + } + + if (pi->min_vddc_in_table > pl->vddc) + pi->min_vddc_in_table = pl->vddc; + + if (pi->max_vddc_in_table < pl->vddc) + pi->max_vddc_in_table = pl->vddc; + + /* patch up boot state */ + if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT) { + u16 vddc, vddci; + radeon_atombios_get_default_voltages(rdev, &vddc, &vddci); + pl->mclk = rdev->clock.default_mclk; + pl->sclk = rdev->clock.default_sclk; + pl->vddc = vddc; + pl->vddci = vddci; + } +} + +int rv7xx_parse_power_table(struct radeon_device *rdev) +{ + struct radeon_mode_info *mode_info = &rdev->mode_info; + struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info; + union pplib_power_state *power_state; + int i, j; + union pplib_clock_info *clock_info; + union power_info *power_info; + int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo); + u16 data_offset; + u8 frev, crev; + struct rv7xx_ps *ps; + + if (!atom_parse_data_header(mode_info->atom_context, index, NULL, + &frev, &crev, &data_offset)) + return -EINVAL; + power_info = (union power_info *)(mode_info->atom_context->bios + data_offset); + + rdev->pm.dpm.ps = kzalloc(sizeof(struct radeon_ps) * + power_info->pplib.ucNumStates, GFP_KERNEL); + if (!rdev->pm.dpm.ps) + return -ENOMEM; + rdev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps); + rdev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime); + rdev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime); + + for (i = 0; i < power_info->pplib.ucNumStates; i++) { + power_state = (union pplib_power_state *) + (mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib.usStateArrayOffset) + + i * power_info->pplib.ucStateEntrySize); + non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *) + (mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset) + + (power_state->v1.ucNonClockStateIndex * + power_info->pplib.ucNonClockSize)); + if (power_info->pplib.ucStateEntrySize - 1) { + ps = kzalloc(sizeof(struct rv7xx_ps), GFP_KERNEL); + if (ps == NULL) { + kfree(rdev->pm.dpm.ps); + return -ENOMEM; + } + rdev->pm.dpm.ps[i].ps_priv = ps; + rv7xx_parse_pplib_non_clock_info(rdev, &rdev->pm.dpm.ps[i], + non_clock_info, + power_info->pplib.ucNonClockSize); + for (j = 0; j < (power_info->pplib.ucStateEntrySize - 1); j++) { + clock_info = (union pplib_clock_info *) + (mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib.usClockInfoArrayOffset) + + (power_state->v1.ucClockStateIndices[j] * + power_info->pplib.ucClockInfoSize)); + rv7xx_parse_pplib_clock_info(rdev, + &rdev->pm.dpm.ps[i], j, + clock_info); + } + } + } + rdev->pm.dpm.num_ps = power_info->pplib.ucNumStates; + return 0; +} + +int rv770_dpm_init(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi; + int index = GetIndexIntoMasterTable(DATA, ASIC_InternalSS_Info); + uint16_t data_offset, size; + uint8_t frev, crev; + struct atom_clock_dividers dividers; + int ret; + + pi = kzalloc(sizeof(struct rv7xx_power_info), GFP_KERNEL); + if (pi == NULL) + return -ENOMEM; + rdev->pm.dpm.priv = pi; + + rv770_get_max_vddc(rdev); + + pi->acpi_vddc = 0; + pi->min_vddc_in_table = 0; + pi->max_vddc_in_table = 0; + + ret = rv7xx_parse_power_table(rdev); + if (ret) + return ret; + + if (rdev->pm.dpm.voltage_response_time == 0) + rdev->pm.dpm.voltage_response_time = R600_VOLTAGERESPONSETIME_DFLT; + if (rdev->pm.dpm.backbias_response_time == 0) + rdev->pm.dpm.backbias_response_time = R600_BACKBIASRESPONSETIME_DFLT; + + ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM, + 0, false, ÷rs); + if (ret) + pi->ref_div = dividers.ref_div + 1; + else + pi->ref_div = R600_REFERENCEDIVIDER_DFLT; + + pi->mclk_strobe_mode_threshold = 30000; + pi->mclk_edc_enable_threshold = 30000; + + pi->voltage_control = + radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC); + + pi->mvdd_control = + radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_MVDDC); + + if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size, + &frev, &crev, &data_offset)) { + pi->sclk_ss = true; + pi->mclk_ss = true; + pi->dynamic_ss = true; + } else { + pi->sclk_ss = false; + pi->mclk_ss = false; + pi->dynamic_ss = false; + } + + pi->asi = RV770_ASI_DFLT; + pi->pasi = RV770_HASI_DFLT; + pi->vrc = RV770_VRC_DFLT; + + pi->power_gating = false; + + pi->gfx_clock_gating = true; + + pi->mg_clock_gating = true; + pi->mgcgtssm = true; + + pi->dynamic_pcie_gen2 = true; + + if (pi->gfx_clock_gating && + (rdev->pm.int_thermal_type != THERMAL_TYPE_NONE)) + pi->thermal_protection = true; + else + pi->thermal_protection = false; + + pi->display_gap = true; + + if (rdev->flags & RADEON_IS_MOBILITY) + pi->dcodt = true; + else + pi->dcodt = false; + + pi->ulps = true; + + pi->mclk_stutter_mode_threshold = 0; + + pi->sram_end = SMC_RAM_END; + pi->state_table_start = RV770_SMC_TABLE_ADDRESS; + pi->soft_regs_start = RV770_SMC_SOFT_REGISTERS_START; + + return 0; +} + +void rv770_dpm_print_power_state(struct radeon_device *rdev, + struct radeon_ps *rps) +{ + struct rv7xx_ps *ps = rv770_get_ps(rps); + struct rv7xx_pl *pl; + + r600_dpm_print_class_info(rps->class, rps->class2); + r600_dpm_print_cap_info(rps->caps); + printk("\tuvd vclk: %d dclk: %d\n", rps->vclk, rps->dclk); + if (rdev->family >= CHIP_CEDAR) { + pl = &ps->low; + printk("\t\tpower level 0 sclk: %u mclk: %u vddc: %u vddci: %u\n", + pl->sclk, pl->mclk, pl->vddc, pl->vddci); + pl = &ps->medium; + printk("\t\tpower level 1 sclk: %u mclk: %u vddc: %u vddci: %u\n", + pl->sclk, pl->mclk, pl->vddc, pl->vddci); + pl = &ps->high; + printk("\t\tpower level 2 sclk: %u mclk: %u vddc: %u vddci: %u\n", + pl->sclk, pl->mclk, pl->vddc, pl->vddci); + } else { + pl = &ps->low; + printk("\t\tpower level 0 sclk: %u mclk: %u vddc: %u\n", + pl->sclk, pl->mclk, pl->vddc); + pl = &ps->medium; + printk("\t\tpower level 1 sclk: %u mclk: %u vddc: %u\n", + pl->sclk, pl->mclk, pl->vddc); + pl = &ps->high; + printk("\t\tpower level 2 sclk: %u mclk: %u vddc: %u\n", + pl->sclk, pl->mclk, pl->vddc); + } + r600_dpm_print_ps_status(rdev, rps); +} + +void rv770_dpm_fini(struct radeon_device *rdev) +{ + int i; + + for (i = 0; i < rdev->pm.dpm.num_ps; i++) { + kfree(rdev->pm.dpm.ps[i].ps_priv); + } + kfree(rdev->pm.dpm.ps); + kfree(rdev->pm.dpm.priv); +} + +u32 rv770_dpm_get_sclk(struct radeon_device *rdev, bool low) +{ + struct rv7xx_ps *requested_state = rv770_get_ps(rdev->pm.dpm.requested_ps); + + if (low) + return requested_state->low.sclk; + else + return requested_state->high.sclk; +} + +u32 rv770_dpm_get_mclk(struct radeon_device *rdev, bool low) +{ + struct rv7xx_ps *requested_state = rv770_get_ps(rdev->pm.dpm.requested_ps); + + if (low) + return requested_state->low.mclk; + else + return requested_state->high.mclk; +} diff --git a/drivers/gpu/drm/radeon/rv770_dpm.h b/drivers/gpu/drm/radeon/rv770_dpm.h new file mode 100644 index 0000000..df9aae5 --- /dev/null +++ b/drivers/gpu/drm/radeon/rv770_dpm.h @@ -0,0 +1,272 @@ +/* + * Copyright 2011 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ +#ifndef __RV770_DPM_H__ +#define __RV770_DPM_H__ + +#include "rv770_smc.h" + +struct rv770_clock_registers { + u32 cg_spll_func_cntl; + u32 cg_spll_func_cntl_2; + u32 cg_spll_func_cntl_3; + u32 cg_spll_spread_spectrum; + u32 cg_spll_spread_spectrum_2; + u32 mpll_ad_func_cntl; + u32 mpll_ad_func_cntl_2; + u32 mpll_dq_func_cntl; + u32 mpll_dq_func_cntl_2; + u32 mclk_pwrmgt_cntl; + u32 dll_cntl; + u32 mpll_ss1; + u32 mpll_ss2; +}; + +struct rv730_clock_registers { + u32 cg_spll_func_cntl; + u32 cg_spll_func_cntl_2; + u32 cg_spll_func_cntl_3; + u32 cg_spll_spread_spectrum; + u32 cg_spll_spread_spectrum_2; + u32 mclk_pwrmgt_cntl; + u32 dll_cntl; + u32 mpll_func_cntl; + u32 mpll_func_cntl2; + u32 mpll_func_cntl3; + u32 mpll_ss; + u32 mpll_ss2; +}; + +union r7xx_clock_registers { + struct rv770_clock_registers rv770; + struct rv730_clock_registers rv730; +}; + +struct vddc_table_entry { + u16 vddc; + u8 vddc_index; + u8 high_smio; + u32 low_smio; +}; + +#define MAX_NO_OF_MVDD_VALUES 2 +#define MAX_NO_VREG_STEPS 32 + +struct rv7xx_power_info { + /* flags */ + bool mem_gddr5; + bool pcie_gen2; + bool dynamic_pcie_gen2; + bool acpi_pcie_gen2; + bool boot_in_gen2; + bool voltage_control; /* vddc */ + bool mvdd_control; + bool sclk_ss; + bool mclk_ss; + bool dynamic_ss; + bool gfx_clock_gating; + bool mg_clock_gating; + bool mgcgtssm; + bool power_gating; + bool thermal_protection; + bool display_gap; + bool dcodt; + bool ulps; + /* registers */ + union r7xx_clock_registers clk_regs; + u32 s0_vid_lower_smio_cntl; + /* voltage */ + u32 vddc_mask_low; + u32 mvdd_mask_low; + u32 mvdd_split_frequency; + u32 mvdd_low_smio[MAX_NO_OF_MVDD_VALUES]; + u16 max_vddc; + u16 max_vddc_in_table; + u16 min_vddc_in_table; + struct vddc_table_entry vddc_table[MAX_NO_VREG_STEPS]; + u8 valid_vddc_entries; + /* dc odt */ + u32 mclk_odt_threshold; + u8 odt_value_0[2]; + u8 odt_value_1[2]; + /* stored values */ + u32 boot_sclk; + u16 acpi_vddc; + u32 ref_div; + u32 active_auto_throttle_sources; + u32 mclk_stutter_mode_threshold; + u32 mclk_strobe_mode_threshold; + u32 mclk_edc_enable_threshold; + u32 bsp; + u32 bsu; + u32 pbsp; + u32 pbsu; + u32 dsp; + u32 psp; + u32 asi; + u32 pasi; + u32 vrc; + u32 restricted_levels; + /* smc offsets */ + u16 state_table_start; + u16 soft_regs_start; + u16 sram_end; + /* scratch structs */ + RV770_SMC_STATETABLE smc_statetable; +}; + +struct rv7xx_pl { + u32 sclk; + u32 mclk; + u16 vddc; + u16 vddci; /* eg+ only */ + u32 flags; +}; + +struct rv7xx_ps { + struct rv7xx_pl high; + struct rv7xx_pl medium; + struct rv7xx_pl low; + bool dc_compatible; +}; + +#define RV770_RLP_DFLT 10 +#define RV770_RMP_DFLT 25 +#define RV770_LHP_DFLT 25 +#define RV770_LMP_DFLT 10 +#define RV770_VRC_DFLT 0x003f +#define RV770_ASI_DFLT 1000 +#define RV770_HASI_DFLT 200000 +#define RV770_MGCGTTLOCAL0_DFLT 0x00100000 +#define RV7XX_MGCGTTLOCAL0_DFLT 0 +#define RV770_MGCGTTLOCAL1_DFLT 0xFFFF0000 +#define RV770_MGCGCGTSSMCTRL_DFLT 0x55940000 + +#define MVDD_LOW_INDEX 0 +#define MVDD_HIGH_INDEX 1 + +#define MVDD_LOW_VALUE 0 +#define MVDD_HIGH_VALUE 0xffff + +#define RV770_DEFAULT_VCLK_FREQ 53300 /* 10 khz */ +#define RV770_DEFAULT_DCLK_FREQ 40000 /* 10 khz */ + +/* rv730/rv710 */ +int rv730_populate_sclk_value(struct radeon_device *rdev, + u32 engine_clock, + RV770_SMC_SCLK_VALUE *sclk); +int rv730_populate_mclk_value(struct radeon_device *rdev, + u32 engine_clock, u32 memory_clock, + LPRV7XX_SMC_MCLK_VALUE mclk); +void rv730_read_clock_registers(struct radeon_device *rdev); +int rv730_populate_smc_acpi_state(struct radeon_device *rdev, + RV770_SMC_STATETABLE *table); +int rv730_populate_smc_initial_state(struct radeon_device *rdev, + struct radeon_ps *radeon_initial_state, + RV770_SMC_STATETABLE *table); +void rv730_program_memory_timing_parameters(struct radeon_device *rdev, + struct radeon_ps *radeon_state); +void rv730_power_gating_enable(struct radeon_device *rdev, + bool enable); +void rv730_start_dpm(struct radeon_device *rdev); +void rv730_stop_dpm(struct radeon_device *rdev); +void rv730_program_dcodt(struct radeon_device *rdev, bool use_dcodt); +void rv730_get_odt_values(struct radeon_device *rdev); + +/* rv740 */ +int rv740_populate_sclk_value(struct radeon_device *rdev, u32 engine_clock, + RV770_SMC_SCLK_VALUE *sclk); +int rv740_populate_mclk_value(struct radeon_device *rdev, + u32 engine_clock, u32 memory_clock, + RV7XX_SMC_MCLK_VALUE *mclk); +void rv740_read_clock_registers(struct radeon_device *rdev); +int rv740_populate_smc_acpi_state(struct radeon_device *rdev, + RV770_SMC_STATETABLE *table); +void rv740_enable_mclk_spread_spectrum(struct radeon_device *rdev, + bool enable); +u8 rv740_get_mclk_frequency_ratio(u32 memory_clock); +u32 rv740_get_dll_speed(bool is_gddr5, u32 memory_clock); +u32 rv740_get_decoded_reference_divider(u32 encoded_ref); + +/* rv770 */ +u32 rv770_map_clkf_to_ibias(struct radeon_device *rdev, u32 clkf); +int rv770_populate_vddc_value(struct radeon_device *rdev, u16 vddc, + RV770_SMC_VOLTAGE_VALUE *voltage); +int rv770_populate_mvdd_value(struct radeon_device *rdev, u32 mclk, + RV770_SMC_VOLTAGE_VALUE *voltage); +u8 rv770_get_seq_value(struct radeon_device *rdev, + struct rv7xx_pl *pl); +int rv770_populate_initial_mvdd_value(struct radeon_device *rdev, + RV770_SMC_VOLTAGE_VALUE *voltage); +u32 rv770_calculate_memory_refresh_rate(struct radeon_device *rdev, + u32 engine_clock); +void rv770_program_response_times(struct radeon_device *rdev); +int rv770_populate_smc_sp(struct radeon_device *rdev, + struct radeon_ps *radeon_state, + RV770_SMC_SWSTATE *smc_state); +int rv770_populate_smc_t(struct radeon_device *rdev, + struct radeon_ps *radeon_state, + RV770_SMC_SWSTATE *smc_state); +void rv770_read_voltage_smio_registers(struct radeon_device *rdev); +void rv770_get_memory_type(struct radeon_device *rdev); +void r7xx_start_smc(struct radeon_device *rdev); +u8 rv770_get_memory_module_index(struct radeon_device *rdev); +void rv770_get_max_vddc(struct radeon_device *rdev); +void rv770_get_pcie_gen2_status(struct radeon_device *rdev); +void rv770_enable_acpi_pm(struct radeon_device *rdev); +void rv770_restore_cgcg(struct radeon_device *rdev); +bool rv770_dpm_enabled(struct radeon_device *rdev); +void rv770_enable_voltage_control(struct radeon_device *rdev, + bool enable); +void rv770_enable_backbias(struct radeon_device *rdev, + bool enable); +void rv770_enable_thermal_protection(struct radeon_device *rdev, + bool enable); +void rv770_enable_auto_throttle_source(struct radeon_device *rdev, + enum radeon_dpm_auto_throttle_src source, + bool enable); +void rv770_setup_bsp(struct radeon_device *rdev); +void rv770_program_git(struct radeon_device *rdev); +void rv770_program_tp(struct radeon_device *rdev); +void rv770_program_tpp(struct radeon_device *rdev); +void rv770_program_sstp(struct radeon_device *rdev); +void rv770_program_engine_speed_parameters(struct radeon_device *rdev); +void rv770_program_vc(struct radeon_device *rdev); +void rv770_clear_vc(struct radeon_device *rdev); +int rv770_upload_firmware(struct radeon_device *rdev); +void rv770_stop_dpm(struct radeon_device *rdev); +void r7xx_stop_smc(struct radeon_device *rdev); +void rv770_reset_smio_status(struct radeon_device *rdev); +int rv770_restrict_performance_levels_before_switch(struct radeon_device *rdev); +int rv770_halt_smc(struct radeon_device *rdev); +int rv770_resume_smc(struct radeon_device *rdev); +int rv770_set_sw_state(struct radeon_device *rdev); +int rv770_set_boot_state(struct radeon_device *rdev); +int rv7xx_parse_power_table(struct radeon_device *rdev); + +/* smc */ +int rv770_read_smc_soft_register(struct radeon_device *rdev, + u16 reg_offset, u32 *value); +int rv770_write_smc_soft_register(struct radeon_device *rdev, + u16 reg_offset, u32 value); + +#endif diff --git a/drivers/gpu/drm/radeon/rv770_smc.c b/drivers/gpu/drm/radeon/rv770_smc.c new file mode 100644 index 0000000..8e07153 --- /dev/null +++ b/drivers/gpu/drm/radeon/rv770_smc.c @@ -0,0 +1,404 @@ +/* + * Copyright 2011 Advanced Micro Devices, 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: Alex Deucher + */ + +#include <linux/firmware.h> +#include "drmP.h" +#include "radeon.h" +#include "rv770d.h" +#include "rv770_dpm.h" +#include "rv770_smc.h" +#include "atom.h" +#include "radeon_ucode.h" + +#define FIRST_SMC_INT_VECT_REG 0xFFD8 +#define FIRST_INT_VECT_S19 0xFFC0 + +static const u8 rv770_smc_int_vectors[] = +{ + 0x08, 0x10, 0x08, 0x10, + 0x08, 0x10, 0x08, 0x10, + 0x08, 0x10, 0x08, 0x10, + 0x08, 0x10, 0x08, 0x10, + 0x08, 0x10, 0x08, 0x10, + 0x08, 0x10, 0x08, 0x10, + 0x08, 0x10, 0x08, 0x10, + 0x08, 0x10, 0x08, 0x10, + 0x08, 0x10, 0x08, 0x10, + 0x08, 0x10, 0x08, 0x10, + 0x08, 0x10, 0x08, 0x10, + 0x08, 0x10, 0x08, 0x10, + 0x08, 0x10, 0x0C, 0xD7, + 0x08, 0x2B, 0x08, 0x10, + 0x03, 0x51, 0x03, 0x51, + 0x03, 0x51, 0x03, 0x51 +}; + +static const u8 rv730_smc_int_vectors[] = +{ + 0x08, 0x15, 0x08, 0x15, + 0x08, 0x15, 0x08, 0x15, + 0x08, 0x15, 0x08, 0x15, + 0x08, 0x15, 0x08, 0x15, + 0x08, 0x15, 0x08, 0x15, + 0x08, 0x15, 0x08, 0x15, + 0x08, 0x15, 0x08, 0x15, + 0x08, 0x15, 0x08, 0x15, + 0x08, 0x15, 0x08, 0x15, + 0x08, 0x15, 0x08, 0x15, + 0x08, 0x15, 0x08, 0x15, + 0x08, 0x15, 0x08, 0x15, + 0x08, 0x15, 0x0C, 0xBB, + 0x08, 0x30, 0x08, 0x15, + 0x03, 0x56, 0x03, 0x56, + 0x03, 0x56, 0x03, 0x56 +}; + +static const u8 rv710_smc_int_vectors[] = +{ + 0x08, 0x04, 0x08, 0x04, + 0x08, 0x04, 0x08, 0x04, + 0x08, 0x04, 0x08, 0x04, + 0x08, 0x04, 0x08, 0x04, + 0x08, 0x04, 0x08, 0x04, + 0x08, 0x04, 0x08, 0x04, + 0x08, 0x04, 0x08, 0x04, + 0x08, 0x04, 0x08, 0x04, + 0x08, 0x04, 0x08, 0x04, + 0x08, 0x04, 0x08, 0x04, + 0x08, 0x04, 0x08, 0x04, + 0x08, 0x04, 0x08, 0x04, + 0x08, 0x04, 0x0C, 0xCB, + 0x08, 0x1F, 0x08, 0x04, + 0x03, 0x51, 0x03, 0x51, + 0x03, 0x51, 0x03, 0x51 +}; + +static const u8 rv740_smc_int_vectors[] = +{ + 0x08, 0x10, 0x08, 0x10, + 0x08, 0x10, 0x08, 0x10, + 0x08, 0x10, 0x08, 0x10, + 0x08, 0x10, 0x08, 0x10, + 0x08, 0x10, 0x08, 0x10, + 0x08, 0x10, 0x08, 0x10, + 0x08, 0x10, 0x08, 0x10, + 0x08, 0x10, 0x08, 0x10, + 0x08, 0x10, 0x08, 0x10, + 0x08, 0x10, 0x08, 0x10, + 0x08, 0x10, 0x08, 0x10, + 0x08, 0x10, 0x08, 0x10, + 0x08, 0x10, 0x0C, 0xD7, + 0x08, 0x2B, 0x08, 0x10, + 0x03, 0x51, 0x03, 0x51, + 0x03, 0x51, 0x03, 0x51 +}; + +int rv770_set_smc_sram_address(struct radeon_device *rdev, + u16 smc_address, u16 limit) +{ + u32 addr; + + if (smc_address & 3) + return -EINVAL; + if ((smc_address + 3) > limit) + return -EINVAL; + + addr = smc_address; + addr |= SMC_SRAM_AUTO_INC_DIS; + + WREG32(SMC_SRAM_ADDR, addr); + + return 0; +} + +int rv770_copy_bytes_to_smc(struct radeon_device *rdev, + u16 smc_start_address, const u8 *src, + u16 byte_count, u16 limit) +{ + u32 data, original_data, extra_shift; + u16 addr; + int ret; + + if (smc_start_address & 3) + return -EINVAL; + if ((smc_start_address + byte_count) > limit) + return -EINVAL; + + addr = smc_start_address; + + while (byte_count >= 4) { + /* SMC address space is BE */ + data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3]; + + ret = rv770_set_smc_sram_address(rdev, addr, limit); + if (ret) + return ret; + + WREG32(SMC_SRAM_DATA, data); + + src += 4; + byte_count -= 4; + addr += 4; + } + + /* RMW for final bytes */ + if (byte_count > 0) { + data = 0; + + ret = rv770_set_smc_sram_address(rdev, addr, limit); + if (ret) + return ret; + + original_data = RREG32(SMC_SRAM_DATA); + + extra_shift = 8 * (4 - byte_count); + + while (byte_count > 0) { + /* SMC address space is BE */ + data = (data << 8) + *src++; + byte_count--; + } + + data <<= extra_shift; + + data |= (original_data & ~((~0UL) << extra_shift)); + + ret = rv770_set_smc_sram_address(rdev, addr, limit); + if (ret) + return ret; + + WREG32(SMC_SRAM_DATA, data); + } + + return 0; +} + +static int rv770_program_interrupt_vectors(struct radeon_device *rdev, + u32 smc_first_vector, const u8 *src, + u32 byte_count) +{ + u32 tmp, i; + + if (byte_count % 4) + return -EINVAL; + + if (smc_first_vector < FIRST_SMC_INT_VECT_REG) { + tmp = FIRST_SMC_INT_VECT_REG - smc_first_vector; + + if (tmp > byte_count) + return 0; + + byte_count -= tmp; + src += tmp; + smc_first_vector = FIRST_SMC_INT_VECT_REG; + } + + for (i = 0; i < byte_count; i += 4) { + /* SMC address space is BE */ + tmp = (src[i] << 24) | (src[i + 1] << 16) | (src[i + 2] << 8) | src[i + 3]; + + WREG32(SMC_ISR_FFD8_FFDB + i, tmp); + } + + return 0; +} + +void rv770_start_smc(struct radeon_device *rdev) +{ + WREG32_P(SMC_IO, SMC_RST_N, ~SMC_RST_N); +} + +void rv770_reset_smc(struct radeon_device *rdev) +{ + WREG32_P(SMC_IO, 0, ~SMC_RST_N); +} + +void rv770_stop_smc_clock(struct radeon_device *rdev) +{ + WREG32_P(SMC_IO, 0, ~SMC_CLK_EN); +} + +void rv770_start_smc_clock(struct radeon_device *rdev) +{ + WREG32_P(SMC_IO, SMC_CLK_EN, ~SMC_CLK_EN); +} + +bool rv770_is_smc_running(struct radeon_device *rdev) +{ + u32 tmp; + + tmp = RREG32(SMC_IO); + + if ((tmp & SMC_RST_N) && (tmp & SMC_CLK_EN)) + return true; + else + return false; +} + +PPSMC_Result rv770_send_msg_to_smc(struct radeon_device *rdev, PPSMC_Msg msg) +{ + u32 tmp; + int i; + PPSMC_Result result; + + if (!rv770_is_smc_running(rdev)) + return PPSMC_Result_Failed; + + WREG32_P(SMC_MSG, HOST_SMC_MSG(msg), ~HOST_SMC_MSG_MASK); + + for (i = 0; i < rdev->usec_timeout; i++) { + tmp = RREG32(SMC_MSG) & HOST_SMC_RESP_MASK; + tmp >>= HOST_SMC_RESP_SHIFT; + if (tmp != 0) + break; + udelay(1); + } + + tmp = RREG32(SMC_MSG) & HOST_SMC_RESP_MASK; + tmp >>= HOST_SMC_RESP_SHIFT; + + result = (PPSMC_Result)tmp; + return result; +} + +PPSMC_Result rv770_wait_for_smc_inactive(struct radeon_device *rdev) +{ + int i; + PPSMC_Result result = PPSMC_Result_OK; + + if (!rv770_is_smc_running(rdev)) + return result; + + for (i = 0; i < rdev->usec_timeout; i++) { + if (RREG32(SMC_IO) & SMC_STOP_MODE) + break; + udelay(1); + } + + return result; +} + +static void rv770_clear_smc_sram(struct radeon_device *rdev, u16 limit) +{ + u16 i; + + for (i = 0; i < limit; i += 4) { + rv770_set_smc_sram_address(rdev, i, limit); + WREG32(SMC_SRAM_DATA, 0); + } +} + +int rv770_load_smc_ucode(struct radeon_device *rdev, + u16 limit) +{ + int ret; + const u8 *int_vect; + u16 int_vect_start_address; + u16 int_vect_size; + const u8 *ucode_data; + u16 ucode_start_address; + u16 ucode_size; + + if (!rdev->smc_fw) + return -EINVAL; + + rv770_clear_smc_sram(rdev, limit); + + switch (rdev->family) { + case CHIP_RV770: + ucode_start_address = RV770_SMC_UCODE_START; + ucode_size = RV770_SMC_UCODE_SIZE; + int_vect = (const u8 *)&rv770_smc_int_vectors; + int_vect_start_address = RV770_SMC_INT_VECTOR_START; + int_vect_size = RV770_SMC_INT_VECTOR_SIZE; + break; + case CHIP_RV730: + ucode_start_address = RV730_SMC_UCODE_START; + ucode_size = RV730_SMC_UCODE_SIZE; + int_vect = (const u8 *)&rv730_smc_int_vectors; + int_vect_start_address = RV730_SMC_INT_VECTOR_START; + int_vect_size = RV730_SMC_INT_VECTOR_SIZE; + break; + case CHIP_RV710: + ucode_start_address = RV710_SMC_UCODE_START; + ucode_size = RV710_SMC_UCODE_SIZE; + int_vect = (const u8 *)&rv710_smc_int_vectors; + int_vect_start_address = RV710_SMC_INT_VECTOR_START; + int_vect_size = RV710_SMC_INT_VECTOR_SIZE; + break; + case CHIP_RV740: + ucode_start_address = RV740_SMC_UCODE_START; + ucode_size = RV740_SMC_UCODE_SIZE; + int_vect = (const u8 *)&rv740_smc_int_vectors; + int_vect_start_address = RV740_SMC_INT_VECTOR_START; + int_vect_size = RV740_SMC_INT_VECTOR_SIZE; + break; + default: + DRM_ERROR("unknown asic in smc ucode loader\n"); + BUG(); + } + + /* load the ucode */ + ucode_data = (const u8 *)rdev->smc_fw->data; + ret = rv770_copy_bytes_to_smc(rdev, ucode_start_address, + ucode_data, ucode_size, limit); + if (ret) + return ret; + + /* set up the int vectors */ + ret = rv770_program_interrupt_vectors(rdev, int_vect_start_address, + int_vect, int_vect_size); + if (ret) + return ret; + + return 0; +} + +int rv770_read_smc_sram_dword(struct radeon_device *rdev, + u16 smc_address, u32 *value, u16 limit) +{ + int ret; + + ret = rv770_set_smc_sram_address(rdev, smc_address, limit); + if (ret) + return ret; + + *value = RREG32(SMC_SRAM_DATA); + + return 0; +} + +int rv770_write_smc_sram_dword(struct radeon_device *rdev, + u16 smc_address, u32 value, u16 limit) +{ + int ret; + + ret = rv770_set_smc_sram_address(rdev, smc_address, limit); + if (ret) + return ret; + + WREG32(SMC_SRAM_DATA, value); + + return 0; +} diff --git a/drivers/gpu/drm/radeon/rv770_smc.h b/drivers/gpu/drm/radeon/rv770_smc.h new file mode 100644 index 0000000..bdb652c --- /dev/null +++ b/drivers/gpu/drm/radeon/rv770_smc.h @@ -0,0 +1,208 @@ +/* + * Copyright 2011 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ +#ifndef __RV770_SMC_H__ +#define __RV770_SMC_H__ + +#include "ppsmc.h" + +#pragma pack(push, 1) + +#define RV770_SMC_TABLE_ADDRESS 0xB000 + +#define RV770_SMC_PERFORMANCE_LEVELS_PER_SWSTATE 3 + +struct RV770_SMC_SCLK_VALUE +{ + uint32_t vCG_SPLL_FUNC_CNTL; + uint32_t vCG_SPLL_FUNC_CNTL_2; + uint32_t vCG_SPLL_FUNC_CNTL_3; + uint32_t vCG_SPLL_SPREAD_SPECTRUM; + uint32_t vCG_SPLL_SPREAD_SPECTRUM_2; + uint32_t sclk_value; +}; + +typedef struct RV770_SMC_SCLK_VALUE RV770_SMC_SCLK_VALUE; + +struct RV770_SMC_MCLK_VALUE +{ + uint32_t vMPLL_AD_FUNC_CNTL; + uint32_t vMPLL_AD_FUNC_CNTL_2; + uint32_t vMPLL_DQ_FUNC_CNTL; + uint32_t vMPLL_DQ_FUNC_CNTL_2; + uint32_t vMCLK_PWRMGT_CNTL; + uint32_t vDLL_CNTL; + uint32_t vMPLL_SS; + uint32_t vMPLL_SS2; + uint32_t mclk_value; +}; + +typedef struct RV770_SMC_MCLK_VALUE RV770_SMC_MCLK_VALUE; + + +struct RV730_SMC_MCLK_VALUE +{ + uint32_t vMCLK_PWRMGT_CNTL; + uint32_t vDLL_CNTL; + uint32_t vMPLL_FUNC_CNTL; + uint32_t vMPLL_FUNC_CNTL2; + uint32_t vMPLL_FUNC_CNTL3; + uint32_t vMPLL_SS; + uint32_t vMPLL_SS2; + uint32_t mclk_value; +}; + +typedef struct RV730_SMC_MCLK_VALUE RV730_SMC_MCLK_VALUE; + +struct RV770_SMC_VOLTAGE_VALUE +{ + uint16_t value; + uint8_t index; + uint8_t padding; +}; + +typedef struct RV770_SMC_VOLTAGE_VALUE RV770_SMC_VOLTAGE_VALUE; + +union RV7XX_SMC_MCLK_VALUE +{ + RV770_SMC_MCLK_VALUE mclk770; + RV730_SMC_MCLK_VALUE mclk730; +}; + +typedef union RV7XX_SMC_MCLK_VALUE RV7XX_SMC_MCLK_VALUE, *LPRV7XX_SMC_MCLK_VALUE; + +struct RV770_SMC_HW_PERFORMANCE_LEVEL +{ + uint8_t arbValue; + union{ + uint8_t seqValue; + uint8_t ACIndex; + }; + uint8_t displayWatermark; + uint8_t gen2PCIE; + uint8_t gen2XSP; + uint8_t backbias; + uint8_t strobeMode; + uint8_t mcFlags; + uint32_t aT; + uint32_t bSP; + RV770_SMC_SCLK_VALUE sclk; + RV7XX_SMC_MCLK_VALUE mclk; + RV770_SMC_VOLTAGE_VALUE vddc; + RV770_SMC_VOLTAGE_VALUE mvdd; + RV770_SMC_VOLTAGE_VALUE vddci; + uint8_t reserved1; + uint8_t reserved2; + uint8_t stateFlags; + uint8_t padding; +}; + +#define SMC_STROBE_RATIO 0x0F +#define SMC_STROBE_ENABLE 0x10 + +#define SMC_MC_EDC_RD_FLAG 0x01 +#define SMC_MC_EDC_WR_FLAG 0x02 +#define SMC_MC_RTT_ENABLE 0x04 +#define SMC_MC_STUTTER_EN 0x08 + +typedef struct RV770_SMC_HW_PERFORMANCE_LEVEL RV770_SMC_HW_PERFORMANCE_LEVEL; + +struct RV770_SMC_SWSTATE +{ + uint8_t flags; + uint8_t padding1; + uint8_t padding2; + uint8_t padding3; + RV770_SMC_HW_PERFORMANCE_LEVEL levels[RV770_SMC_PERFORMANCE_LEVELS_PER_SWSTATE]; +}; + +typedef struct RV770_SMC_SWSTATE RV770_SMC_SWSTATE; + +#define RV770_SMC_VOLTAGEMASK_VDDC 0 +#define RV770_SMC_VOLTAGEMASK_MVDD 1 +#define RV770_SMC_VOLTAGEMASK_VDDCI 2 +#define RV770_SMC_VOLTAGEMASK_MAX 4 + +struct RV770_SMC_VOLTAGEMASKTABLE +{ + uint8_t highMask[RV770_SMC_VOLTAGEMASK_MAX]; + uint32_t lowMask[RV770_SMC_VOLTAGEMASK_MAX]; +}; + +typedef struct RV770_SMC_VOLTAGEMASKTABLE RV770_SMC_VOLTAGEMASKTABLE; + +#define MAX_NO_VREG_STEPS 32 + +struct RV770_SMC_STATETABLE +{ + uint8_t thermalProtectType; + uint8_t systemFlags; + uint8_t maxVDDCIndexInPPTable; + uint8_t extraFlags; + uint8_t highSMIO[MAX_NO_VREG_STEPS]; + uint32_t lowSMIO[MAX_NO_VREG_STEPS]; + RV770_SMC_VOLTAGEMASKTABLE voltageMaskTable; + RV770_SMC_SWSTATE initialState; + RV770_SMC_SWSTATE ACPIState; + RV770_SMC_SWSTATE driverState; + RV770_SMC_SWSTATE ULVState; +}; + +typedef struct RV770_SMC_STATETABLE RV770_SMC_STATETABLE; + +#define PPSMC_STATEFLAG_AUTO_PULSE_SKIP 0x01 + +#pragma pack(pop) + +#define RV770_SMC_SOFT_REGISTERS_START 0x104 + +#define RV770_SMC_SOFT_REGISTER_mclk_chg_timeout 0x0 +#define RV770_SMC_SOFT_REGISTER_baby_step_timer 0x8 +#define RV770_SMC_SOFT_REGISTER_delay_bbias 0xC +#define RV770_SMC_SOFT_REGISTER_delay_vreg 0x10 +#define RV770_SMC_SOFT_REGISTER_delay_acpi 0x2C +#define RV770_SMC_SOFT_REGISTER_seq_index 0x64 +#define RV770_SMC_SOFT_REGISTER_mvdd_chg_time 0x68 +#define RV770_SMC_SOFT_REGISTER_mclk_switch_lim 0x78 +#define RV770_SMC_SOFT_REGISTER_mc_block_delay 0x90 +#define RV770_SMC_SOFT_REGISTER_is_asic_lombok 0xA0 + +int rv770_set_smc_sram_address(struct radeon_device *rdev, + u16 smc_address, u16 limit); +int rv770_copy_bytes_to_smc(struct radeon_device *rdev, + u16 smc_start_address, const u8 *src, + u16 byte_count, u16 limit); +void rv770_start_smc(struct radeon_device *rdev); +void rv770_reset_smc(struct radeon_device *rdev); +void rv770_stop_smc_clock(struct radeon_device *rdev); +void rv770_start_smc_clock(struct radeon_device *rdev); +bool rv770_is_smc_running(struct radeon_device *rdev); +PPSMC_Result rv770_send_msg_to_smc(struct radeon_device *rdev, PPSMC_Msg msg); +PPSMC_Result rv770_wait_for_smc_inactive(struct radeon_device *rdev); +int rv770_read_smc_sram_dword(struct radeon_device *rdev, + u16 smc_address, u32 *value, u16 limit); +int rv770_write_smc_sram_dword(struct radeon_device *rdev, + u16 smc_address, u32 value, u16 limit); +int rv770_load_smc_ucode(struct radeon_device *rdev, + u16 limit); + +#endif diff --git a/drivers/gpu/drm/radeon/rv770d.h b/drivers/gpu/drm/radeon/rv770d.h index 85b1626..784eeaf 100644 --- a/drivers/gpu/drm/radeon/rv770d.h +++ b/drivers/gpu/drm/radeon/rv770d.h @@ -62,6 +62,242 @@ # define UPLL_FB_DIV(x) ((x) << 0) # define UPLL_FB_DIV_MASK 0x01FFFFFF
+/* pm registers */ +#define SMC_SRAM_ADDR 0x200 +#define SMC_SRAM_AUTO_INC_DIS (1 << 16) +#define SMC_SRAM_DATA 0x204 +#define SMC_IO 0x208 +#define SMC_RST_N (1 << 0) +#define SMC_STOP_MODE (1 << 2) +#define SMC_CLK_EN (1 << 11) +#define SMC_MSG 0x20c +#define HOST_SMC_MSG(x) ((x) << 0) +#define HOST_SMC_MSG_MASK (0xff << 0) +#define HOST_SMC_MSG_SHIFT 0 +#define HOST_SMC_RESP(x) ((x) << 8) +#define HOST_SMC_RESP_MASK (0xff << 8) +#define HOST_SMC_RESP_SHIFT 8 +#define SMC_HOST_MSG(x) ((x) << 16) +#define SMC_HOST_MSG_MASK (0xff << 16) +#define SMC_HOST_MSG_SHIFT 16 +#define SMC_HOST_RESP(x) ((x) << 24) +#define SMC_HOST_RESP_MASK (0xff << 24) +#define SMC_HOST_RESP_SHIFT 24 + +#define SMC_ISR_FFD8_FFDB 0x218 + +#define CG_SPLL_FUNC_CNTL 0x600 +#define SPLL_RESET (1 << 0) +#define SPLL_SLEEP (1 << 1) +#define SPLL_DIVEN (1 << 2) +#define SPLL_BYPASS_EN (1 << 3) +#define SPLL_REF_DIV(x) ((x) << 4) +#define SPLL_REF_DIV_MASK (0x3f << 4) +#define SPLL_HILEN(x) ((x) << 12) +#define SPLL_HILEN_MASK (0xf << 12) +#define SPLL_LOLEN(x) ((x) << 16) +#define SPLL_LOLEN_MASK (0xf << 16) +#define CG_SPLL_FUNC_CNTL_2 0x604 +#define SCLK_MUX_SEL(x) ((x) << 0) +#define SCLK_MUX_SEL_MASK (0x1ff << 0) +#define CG_SPLL_FUNC_CNTL_3 0x608 +#define SPLL_FB_DIV(x) ((x) << 0) +#define SPLL_FB_DIV_MASK (0x3ffffff << 0) +#define SPLL_DITHEN (1 << 28) + +#define SPLL_CNTL_MODE 0x610 +#define SPLL_DIV_SYNC (1 << 5) + +#define MPLL_AD_FUNC_CNTL 0x624 +#define CLKF(x) ((x) << 0) +#define CLKF_MASK (0x7f << 0) +#define CLKR(x) ((x) << 7) +#define CLKR_MASK (0x1f << 7) +#define CLKFRAC(x) ((x) << 12) +#define CLKFRAC_MASK (0x1f << 12) +#define YCLK_POST_DIV(x) ((x) << 17) +#define YCLK_POST_DIV_MASK (3 << 17) +#define IBIAS(x) ((x) << 20) +#define IBIAS_MASK (0x3ff << 20) +#define RESET (1 << 30) +#define PDNB (1 << 31) +#define MPLL_AD_FUNC_CNTL_2 0x628 +#define BYPASS (1 << 19) +#define BIAS_GEN_PDNB (1 << 24) +#define RESET_EN (1 << 25) +#define VCO_MODE (1 << 29) +#define MPLL_DQ_FUNC_CNTL 0x62c +#define MPLL_DQ_FUNC_CNTL_2 0x630 + +#define GENERAL_PWRMGT 0x63c +# define GLOBAL_PWRMGT_EN (1 << 0) +# define STATIC_PM_EN (1 << 1) +# define THERMAL_PROTECTION_DIS (1 << 2) +# define THERMAL_PROTECTION_TYPE (1 << 3) +# define ENABLE_GEN2PCIE (1 << 4) +# define ENABLE_GEN2XSP (1 << 5) +# define SW_SMIO_INDEX(x) ((x) << 6) +# define SW_SMIO_INDEX_MASK (3 << 6) +# define SW_SMIO_INDEX_SHIFT 6 +# define LOW_VOLT_D2_ACPI (1 << 8) +# define LOW_VOLT_D3_ACPI (1 << 9) +# define VOLT_PWRMGT_EN (1 << 10) +# define BACKBIAS_PAD_EN (1 << 18) +# define BACKBIAS_VALUE (1 << 19) +# define DYN_SPREAD_SPECTRUM_EN (1 << 23) +# define AC_DC_SW (1 << 24) + +#define CG_TPC 0x640 +#define SCLK_PWRMGT_CNTL 0x644 +# define SCLK_PWRMGT_OFF (1 << 0) +# define SCLK_LOW_D1 (1 << 1) +# define FIR_RESET (1 << 4) +# define FIR_FORCE_TREND_SEL (1 << 5) +# define FIR_TREND_MODE (1 << 6) +# define DYN_GFX_CLK_OFF_EN (1 << 7) +# define GFX_CLK_FORCE_ON (1 << 8) +# define GFX_CLK_REQUEST_OFF (1 << 9) +# define GFX_CLK_FORCE_OFF (1 << 10) +# define GFX_CLK_OFF_ACPI_D1 (1 << 11) +# define GFX_CLK_OFF_ACPI_D2 (1 << 12) +# define GFX_CLK_OFF_ACPI_D3 (1 << 13) +#define MCLK_PWRMGT_CNTL 0x648 +# define DLL_SPEED(x) ((x) << 0) +# define DLL_SPEED_MASK (0x1f << 0) +# define MPLL_PWRMGT_OFF (1 << 5) +# define DLL_READY (1 << 6) +# define MC_INT_CNTL (1 << 7) +# define MRDCKA0_SLEEP (1 << 8) +# define MRDCKA1_SLEEP (1 << 9) +# define MRDCKB0_SLEEP (1 << 10) +# define MRDCKB1_SLEEP (1 << 11) +# define MRDCKC0_SLEEP (1 << 12) +# define MRDCKC1_SLEEP (1 << 13) +# define MRDCKD0_SLEEP (1 << 14) +# define MRDCKD1_SLEEP (1 << 15) +# define MRDCKA0_RESET (1 << 16) +# define MRDCKA1_RESET (1 << 17) +# define MRDCKB0_RESET (1 << 18) +# define MRDCKB1_RESET (1 << 19) +# define MRDCKC0_RESET (1 << 20) +# define MRDCKC1_RESET (1 << 21) +# define MRDCKD0_RESET (1 << 22) +# define MRDCKD1_RESET (1 << 23) +# define DLL_READY_READ (1 << 24) +# define USE_DISPLAY_GAP (1 << 25) +# define USE_DISPLAY_URGENT_NORMAL (1 << 26) +# define MPLL_TURNOFF_D2 (1 << 28) +#define DLL_CNTL 0x64c +# define MRDCKA0_BYPASS (1 << 24) +# define MRDCKA1_BYPASS (1 << 25) +# define MRDCKB0_BYPASS (1 << 26) +# define MRDCKB1_BYPASS (1 << 27) +# define MRDCKC0_BYPASS (1 << 28) +# define MRDCKC1_BYPASS (1 << 29) +# define MRDCKD0_BYPASS (1 << 30) +# define MRDCKD1_BYPASS (1 << 31) + +#define MPLL_TIME 0x654 +# define MPLL_LOCK_TIME(x) ((x) << 0) +# define MPLL_LOCK_TIME_MASK (0xffff << 0) +# define MPLL_RESET_TIME(x) ((x) << 16) +# define MPLL_RESET_TIME_MASK (0xffff << 16) + +#define CG_CLKPIN_CNTL 0x660 +# define MUX_TCLK_TO_XCLK (1 << 8) +# define XTALIN_DIVIDE (1 << 9) + +#define S0_VID_LOWER_SMIO_CNTL 0x678 +#define S1_VID_LOWER_SMIO_CNTL 0x67c +#define S2_VID_LOWER_SMIO_CNTL 0x680 +#define S3_VID_LOWER_SMIO_CNTL 0x684 + +#define CG_FTV 0x690 +#define CG_FFCT_0 0x694 +# define UTC_0(x) ((x) << 0) +# define UTC_0_MASK (0x3ff << 0) +# define DTC_0(x) ((x) << 10) +# define DTC_0_MASK (0x3ff << 10) + +#define CG_BSP 0x6d0 +# define BSP(x) ((x) << 0) +# define BSP_MASK (0xffff << 0) +# define BSU(x) ((x) << 16) +# define BSU_MASK (0xf << 16) +#define CG_AT 0x6d4 +# define CG_R(x) ((x) << 0) +# define CG_R_MASK (0xffff << 0) +# define CG_L(x) ((x) << 16) +# define CG_L_MASK (0xffff << 16) +#define CG_GIT 0x6d8 +# define CG_GICST(x) ((x) << 0) +# define CG_GICST_MASK (0xffff << 0) +# define CG_GIPOT(x) ((x) << 16) +# define CG_GIPOT_MASK (0xffff << 16) + +#define CG_SSP 0x6e8 +# define SST(x) ((x) << 0) +# define SST_MASK (0xffff << 0) +# define SSTU(x) ((x) << 16) +# define SSTU_MASK (0xf << 16) + +#define CG_DISPLAY_GAP_CNTL 0x714 +# define DISP1_GAP(x) ((x) << 0) +# define DISP1_GAP_MASK (3 << 0) +# define DISP2_GAP(x) ((x) << 2) +# define DISP2_GAP_MASK (3 << 2) +# define VBI_TIMER_COUNT(x) ((x) << 4) +# define VBI_TIMER_COUNT_MASK (0x3fff << 4) +# define VBI_TIMER_UNIT(x) ((x) << 20) +# define VBI_TIMER_UNIT_MASK (7 << 20) +# define DISP1_GAP_MCHG(x) ((x) << 24) +# define DISP1_GAP_MCHG_MASK (3 << 24) +# define DISP2_GAP_MCHG(x) ((x) << 26) +# define DISP2_GAP_MCHG_MASK (3 << 26) + +#define CG_SPLL_SPREAD_SPECTRUM 0x790 +#define SSEN (1 << 0) +#define CLKS(x) ((x) << 4) +#define CLKS_MASK (0xfff << 4) +#define CG_SPLL_SPREAD_SPECTRUM_2 0x794 +#define CLKV(x) ((x) << 0) +#define CLKV_MASK (0x3ffffff << 0) +#define CG_MPLL_SPREAD_SPECTRUM 0x798 +#define CG_UPLL_SPREAD_SPECTRUM 0x79c +# define SSEN_MASK 0x00000001 + +#define CG_CGTT_LOCAL_0 0x7d0 +#define CG_CGTT_LOCAL_1 0x7d4 + +#define BIOS_SCRATCH_4 0x1734 + +#define MC_SEQ_MISC0 0x2a00 +#define MC_SEQ_MISC0_GDDR5_SHIFT 28 +#define MC_SEQ_MISC0_GDDR5_MASK 0xf0000000 +#define MC_SEQ_MISC0_GDDR5_VALUE 5 + +#define MC_ARB_SQM_RATIO 0x2770 +#define STATE0(x) ((x) << 0) +#define STATE0_MASK (0xff << 0) +#define STATE1(x) ((x) << 8) +#define STATE1_MASK (0xff << 8) +#define STATE2(x) ((x) << 16) +#define STATE2_MASK (0xff << 16) +#define STATE3(x) ((x) << 24) +#define STATE3_MASK (0xff << 24) + +#define MC_ARB_RFSH_RATE 0x27b0 +#define POWERMODE0(x) ((x) << 0) +#define POWERMODE0_MASK (0xff << 0) +#define POWERMODE1(x) ((x) << 8) +#define POWERMODE1_MASK (0xff << 8) +#define POWERMODE2(x) ((x) << 16) +#define POWERMODE2_MASK (0xff << 16) +#define POWERMODE3(x) ((x) << 24) +#define POWERMODE3_MASK (0xff << 24) + +#define CGTS_SM_CTRL_REG 0x9150 + /* Registers */ #define CB_COLOR0_BASE 0x28040 #define CB_COLOR1_BASE 0x28044 @@ -86,8 +322,8 @@ #define CONFIG_MEMSIZE 0x5428
#define CP_ME_CNTL 0x86D8 -#define CP_ME_HALT (1<<28) -#define CP_PFP_HALT (1<<26) +#define CP_ME_HALT (1 << 28) +#define CP_PFP_HALT (1 << 26) #define CP_ME_RAM_DATA 0xC160 #define CP_ME_RAM_RADDR 0xC158 #define CP_ME_RAM_WADDR 0xC15C @@ -157,9 +393,22 @@ #define GUI_ACTIVE (1<<31) #define GRBM_STATUS2 0x8014
-#define CG_CLKPIN_CNTL 0x660 -# define MUX_TCLK_TO_XCLK (1 << 8) -# define XTALIN_DIVIDE (1 << 9) +#define CG_THERMAL_CTRL 0x72C +#define DPM_EVENT_SRC(x) ((x) << 0) +#define DPM_EVENT_SRC_MASK (7 << 0) +#define DIG_THERM_DPM(x) ((x) << 14) +#define DIG_THERM_DPM_MASK 0x003FC000 +#define DIG_THERM_DPM_SHIFT 14 + +#define CG_THERMAL_INT 0x734 +#define DIG_THERM_INTH(x) ((x) << 8) +#define DIG_THERM_INTH_MASK 0x0000FF00 +#define DIG_THERM_INTH_SHIFT 8 +#define DIG_THERM_INTL(x) ((x) << 16) +#define DIG_THERM_INTL_MASK 0x00FF0000 +#define DIG_THERM_INTL_SHIFT 16 +#define THERM_INT_MASK_HIGH (1 << 24) +#define THERM_INT_MASK_LOW (1 << 25)
#define CG_MULT_THERMAL_STATUS 0x740 #define ASIC_T(x) ((x) << 16) @@ -662,7 +911,22 @@ #define D1GRPH_SECONDARY_SURFACE_ADDRESS_HIGH 0x691c #define D2GRPH_SECONDARY_SURFACE_ADDRESS_HIGH 0x611c
-/* PCIE link stuff */ +/* PCIE indirect regs */ +#define PCIE_P_CNTL 0x40 +# define P_PLL_PWRDN_IN_L1L23 (1 << 3) +# define P_PLL_BUF_PDNB (1 << 4) +# define P_PLL_PDNB (1 << 9) +# define P_ALLOW_PRX_FRONTEND_SHUTOFF (1 << 12) +/* PCIE PORT regs */ +#define PCIE_LC_CNTL 0xa0 +# define LC_L0S_INACTIVITY(x) ((x) << 8) +# define LC_L0S_INACTIVITY_MASK (0xf << 8) +# define LC_L0S_INACTIVITY_SHIFT 8 +# define LC_L1_INACTIVITY(x) ((x) << 12) +# define LC_L1_INACTIVITY_MASK (0xf << 12) +# define LC_L1_INACTIVITY_SHIFT 12 +# define LC_PMI_TO_L1_DIS (1 << 16) +# define LC_ASPM_TO_L1_DIS (1 << 24) #define PCIE_LC_TRAINING_CNTL 0xa1 /* PCIE_P */ #define PCIE_LC_LINK_WIDTH_CNTL 0xa2 /* PCIE_P */ # define LC_LINK_WIDTH_SHIFT 0 @@ -690,6 +954,9 @@ # define LC_SPEED_CHANGE_ATTEMPTS_ALLOWED_MASK (0x3 << 8) # define LC_SPEED_CHANGE_ATTEMPTS_ALLOWED_SHIFT 3 # define LC_CURRENT_DATA_RATE (1 << 11) +# define LC_HW_VOLTAGE_IF_CONTROL(x) ((x) << 12) +# define LC_HW_VOLTAGE_IF_CONTROL_MASK (3 << 12) +# define LC_HW_VOLTAGE_IF_CONTROL_SHIFT 12 # define LC_VOLTAGE_TIMER_SEL_MASK (0xf << 14) # define LC_CLR_FAILED_SPD_CHANGE_CNT (1 << 21) # define LC_OTHER_SIDE_EVER_SENT_GEN2 (1 << 23)
From: Alex Deucher alexander.deucher@amd.com
This adds dpm support for evergreen asics. This includes: - clockgating - dynamic engine clock scaling - dynamic memory clock scaling - dynamic voltage scaling - dynamic pcie gen1/gen2 switching (requires additional acpi support)
Set radeon.dpm=1 to enable.
v2: reduce stack usage, rename ulv struct
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/Makefile | 2 +- drivers/gpu/drm/radeon/cypress_dpm.c | 2103 ++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/cypress_dpm.h | 134 ++ drivers/gpu/drm/radeon/evergreen.c | 22 + drivers/gpu/drm/radeon/evergreen_smc.h | 67 + drivers/gpu/drm/radeon/evergreend.h | 305 +++++ drivers/gpu/drm/radeon/r600.c | 14 +- drivers/gpu/drm/radeon/radeon.h | 13 + drivers/gpu/drm/radeon/radeon_acpi.c | 23 + drivers/gpu/drm/radeon/radeon_asic.c | 12 + drivers/gpu/drm/radeon/radeon_asic.h | 7 + drivers/gpu/drm/radeon/radeon_pm.c | 5 + drivers/gpu/drm/radeon/radeon_ucode.h | 20 + drivers/gpu/drm/radeon/rv770_dpm.c | 45 +- drivers/gpu/drm/radeon/rv770_dpm.h | 4 + drivers/gpu/drm/radeon/rv770_smc.c | 109 ++ 16 files changed, 2875 insertions(+), 10 deletions(-) create mode 100644 drivers/gpu/drm/radeon/cypress_dpm.c create mode 100644 drivers/gpu/drm/radeon/cypress_dpm.h create mode 100644 drivers/gpu/drm/radeon/evergreen_smc.h
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index c97753d..7092c96 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile @@ -78,7 +78,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \ atombios_encoders.o radeon_semaphore.o radeon_sa.o atombios_i2c.o si.o \ si_blit_shaders.o radeon_prime.o radeon_uvd.o cik.o cik_blit_shaders.o \ r600_dpm.o rs780_dpm.o rv6xx_dpm.o rv770_dpm.o rv730_dpm.o rv740_dpm.o \ - rv770_smc.o + rv770_smc.o cypress_dpm.o
radeon-$(CONFIG_COMPAT) += radeon_ioc32.o radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o diff --git a/drivers/gpu/drm/radeon/cypress_dpm.c b/drivers/gpu/drm/radeon/cypress_dpm.c new file mode 100644 index 0000000..2345c81 --- /dev/null +++ b/drivers/gpu/drm/radeon/cypress_dpm.c @@ -0,0 +1,2103 @@ +/* + * Copyright 2011 Advanced Micro Devices, 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: Alex Deucher + */ + +#include "drmP.h" +#include "radeon.h" +#include "evergreend.h" +#include "r600_dpm.h" +#include "cypress_dpm.h" +#include "atom.h" + +#define SMC_RAM_END 0x8000 + +#define MC_CG_ARB_FREQ_F0 0x0a +#define MC_CG_ARB_FREQ_F1 0x0b +#define MC_CG_ARB_FREQ_F2 0x0c +#define MC_CG_ARB_FREQ_F3 0x0d + +#define MC_CG_SEQ_DRAMCONF_S0 0x05 +#define MC_CG_SEQ_DRAMCONF_S1 0x06 +#define MC_CG_SEQ_YCLK_SUSPEND 0x04 +#define MC_CG_SEQ_YCLK_RESUME 0x0a + +struct rv7xx_ps *rv770_get_ps(struct radeon_ps *rps); +struct rv7xx_power_info *rv770_get_pi(struct radeon_device *rdev); +struct evergreen_power_info *evergreen_get_pi(struct radeon_device *rdev); + +static u8 cypress_get_mclk_frequency_ratio(struct radeon_device *rdev, + u32 memory_clock, bool strobe_mode); + +static void cypress_enable_bif_dynamic_pcie_gen2(struct radeon_device *rdev, + bool enable) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + u32 tmp, bif; + + tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL); + if (enable) { + if ((tmp & LC_OTHER_SIDE_EVER_SENT_GEN2) && + (tmp & LC_OTHER_SIDE_SUPPORTS_GEN2)) { + if (!pi->boot_in_gen2) { + bif = RREG32(CG_BIF_REQ_AND_RSP) & ~CG_CLIENT_REQ_MASK; + bif |= CG_CLIENT_REQ(0xd); + WREG32(CG_BIF_REQ_AND_RSP, bif); + + tmp &= ~LC_HW_VOLTAGE_IF_CONTROL_MASK; + tmp |= LC_HW_VOLTAGE_IF_CONTROL(1); + tmp |= LC_GEN2_EN_STRAP; + + tmp |= LC_CLR_FAILED_SPD_CHANGE_CNT; + WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp); + udelay(10); + tmp &= ~LC_CLR_FAILED_SPD_CHANGE_CNT; + WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp); + } + } + } else { + if (!pi->boot_in_gen2) { + tmp &= ~LC_HW_VOLTAGE_IF_CONTROL_MASK; + tmp &= ~LC_GEN2_EN_STRAP; + } + if ((tmp & LC_OTHER_SIDE_EVER_SENT_GEN2) || + (tmp & LC_OTHER_SIDE_SUPPORTS_GEN2)) + WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp); + } +} + +static void cypress_enable_dynamic_pcie_gen2(struct radeon_device *rdev, + bool enable) +{ + cypress_enable_bif_dynamic_pcie_gen2(rdev, enable); + + if (enable) + WREG32_P(GENERAL_PWRMGT, ENABLE_GEN2PCIE, ~ENABLE_GEN2PCIE); + else + WREG32_P(GENERAL_PWRMGT, 0, ~ENABLE_GEN2PCIE); +} + +#if 0 +static int cypress_enter_ulp_state(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + + if (pi->gfx_clock_gating) { + WREG32_P(SCLK_PWRMGT_CNTL, 0, ~DYN_GFX_CLK_OFF_EN); + WREG32_P(SCLK_PWRMGT_CNTL, GFX_CLK_FORCE_ON, ~GFX_CLK_FORCE_ON); + WREG32_P(SCLK_PWRMGT_CNTL, 0, ~GFX_CLK_FORCE_ON); + + RREG32(GB_ADDR_CONFIG); + } + + WREG32_P(SMC_MSG, HOST_SMC_MSG(PPSMC_MSG_SwitchToMinimumPower), + ~HOST_SMC_MSG_MASK); + + udelay(7000); + + return 0; +} +#endif + +static void cypress_gfx_clock_gating_enable(struct radeon_device *rdev, + bool enable) +{ + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + + if (enable) { + if (eg_pi->light_sleep) { + WREG32(GRBM_GFX_INDEX, 0xC0000000); + + WREG32_CG(CG_CGLS_TILE_0, 0xFFFFFFFF); + WREG32_CG(CG_CGLS_TILE_1, 0xFFFFFFFF); + WREG32_CG(CG_CGLS_TILE_2, 0xFFFFFFFF); + WREG32_CG(CG_CGLS_TILE_3, 0xFFFFFFFF); + WREG32_CG(CG_CGLS_TILE_4, 0xFFFFFFFF); + WREG32_CG(CG_CGLS_TILE_5, 0xFFFFFFFF); + WREG32_CG(CG_CGLS_TILE_6, 0xFFFFFFFF); + WREG32_CG(CG_CGLS_TILE_7, 0xFFFFFFFF); + WREG32_CG(CG_CGLS_TILE_8, 0xFFFFFFFF); + WREG32_CG(CG_CGLS_TILE_9, 0xFFFFFFFF); + WREG32_CG(CG_CGLS_TILE_10, 0xFFFFFFFF); + WREG32_CG(CG_CGLS_TILE_11, 0xFFFFFFFF); + + WREG32_P(SCLK_PWRMGT_CNTL, DYN_LIGHT_SLEEP_EN, ~DYN_LIGHT_SLEEP_EN); + } + WREG32_P(SCLK_PWRMGT_CNTL, DYN_GFX_CLK_OFF_EN, ~DYN_GFX_CLK_OFF_EN); + } else { + WREG32_P(SCLK_PWRMGT_CNTL, 0, ~DYN_GFX_CLK_OFF_EN); + WREG32_P(SCLK_PWRMGT_CNTL, GFX_CLK_FORCE_ON, ~GFX_CLK_FORCE_ON); + WREG32_P(SCLK_PWRMGT_CNTL, 0, ~GFX_CLK_FORCE_ON); + RREG32(GB_ADDR_CONFIG); + + if (eg_pi->light_sleep) { + WREG32_P(SCLK_PWRMGT_CNTL, 0, ~DYN_LIGHT_SLEEP_EN); + + WREG32(GRBM_GFX_INDEX, 0xC0000000); + + WREG32_CG(CG_CGLS_TILE_0, 0); + WREG32_CG(CG_CGLS_TILE_1, 0); + WREG32_CG(CG_CGLS_TILE_2, 0); + WREG32_CG(CG_CGLS_TILE_3, 0); + WREG32_CG(CG_CGLS_TILE_4, 0); + WREG32_CG(CG_CGLS_TILE_5, 0); + WREG32_CG(CG_CGLS_TILE_6, 0); + WREG32_CG(CG_CGLS_TILE_7, 0); + WREG32_CG(CG_CGLS_TILE_8, 0); + WREG32_CG(CG_CGLS_TILE_9, 0); + WREG32_CG(CG_CGLS_TILE_10, 0); + WREG32_CG(CG_CGLS_TILE_11, 0); + } + } +} + +static void cypress_mg_clock_gating_enable(struct radeon_device *rdev, + bool enable) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + + if (enable) { + u32 cgts_sm_ctrl_reg; + + if (rdev->family == CHIP_CEDAR) + cgts_sm_ctrl_reg = CEDAR_MGCGCGTSSMCTRL_DFLT; + else if (rdev->family == CHIP_REDWOOD) + cgts_sm_ctrl_reg = REDWOOD_MGCGCGTSSMCTRL_DFLT; + else + cgts_sm_ctrl_reg = CYPRESS_MGCGCGTSSMCTRL_DFLT; + + WREG32(GRBM_GFX_INDEX, 0xC0000000); + + WREG32_CG(CG_CGTT_LOCAL_0, CYPRESS_MGCGTTLOCAL0_DFLT); + WREG32_CG(CG_CGTT_LOCAL_1, CYPRESS_MGCGTTLOCAL1_DFLT & 0xFFFFCFFF); + WREG32_CG(CG_CGTT_LOCAL_2, CYPRESS_MGCGTTLOCAL2_DFLT); + WREG32_CG(CG_CGTT_LOCAL_3, CYPRESS_MGCGTTLOCAL3_DFLT); + + if (pi->mgcgtssm) + WREG32(CGTS_SM_CTRL_REG, cgts_sm_ctrl_reg); + + if (eg_pi->mcls) { + WREG32_P(MC_CITF_MISC_RD_CG, MEM_LS_ENABLE, ~MEM_LS_ENABLE); + WREG32_P(MC_CITF_MISC_WR_CG, MEM_LS_ENABLE, ~MEM_LS_ENABLE); + WREG32_P(MC_CITF_MISC_VM_CG, MEM_LS_ENABLE, ~MEM_LS_ENABLE); + WREG32_P(MC_HUB_MISC_HUB_CG, MEM_LS_ENABLE, ~MEM_LS_ENABLE); + WREG32_P(MC_HUB_MISC_VM_CG, MEM_LS_ENABLE, ~MEM_LS_ENABLE); + WREG32_P(MC_HUB_MISC_SIP_CG, MEM_LS_ENABLE, ~MEM_LS_ENABLE); + WREG32_P(MC_XPB_CLK_GAT, MEM_LS_ENABLE, ~MEM_LS_ENABLE); + WREG32_P(VM_L2_CG, MEM_LS_ENABLE, ~MEM_LS_ENABLE); + } + } else { + WREG32(GRBM_GFX_INDEX, 0xC0000000); + + WREG32_CG(CG_CGTT_LOCAL_0, 0xFFFFFFFF); + WREG32_CG(CG_CGTT_LOCAL_1, 0xFFFFFFFF); + WREG32_CG(CG_CGTT_LOCAL_2, 0xFFFFFFFF); + WREG32_CG(CG_CGTT_LOCAL_3, 0xFFFFFFFF); + + if (pi->mgcgtssm) + WREG32(CGTS_SM_CTRL_REG, 0x81f44bc0); + } +} + +void cypress_enable_spread_spectrum(struct radeon_device *rdev, + bool enable) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + + if (enable) { + if (pi->sclk_ss) + WREG32_P(GENERAL_PWRMGT, DYN_SPREAD_SPECTRUM_EN, ~DYN_SPREAD_SPECTRUM_EN); + + if (pi->mclk_ss) + WREG32_P(MPLL_CNTL_MODE, SS_SSEN, ~SS_SSEN); + } else { + WREG32_P(CG_SPLL_SPREAD_SPECTRUM, 0, ~SSEN); + WREG32_P(GENERAL_PWRMGT, 0, ~DYN_SPREAD_SPECTRUM_EN); + WREG32_P(MPLL_CNTL_MODE, 0, ~SS_SSEN); + WREG32_P(MPLL_CNTL_MODE, 0, ~SS_DSMODE_EN); + } +} + +void cypress_start_dpm(struct radeon_device *rdev) +{ + WREG32_P(GENERAL_PWRMGT, GLOBAL_PWRMGT_EN, ~GLOBAL_PWRMGT_EN); +} + +void cypress_enable_sclk_control(struct radeon_device *rdev, + bool enable) +{ + if (enable) + WREG32_P(SCLK_PWRMGT_CNTL, 0, ~SCLK_PWRMGT_OFF); + else + WREG32_P(SCLK_PWRMGT_CNTL, SCLK_PWRMGT_OFF, ~SCLK_PWRMGT_OFF); +} + +void cypress_enable_mclk_control(struct radeon_device *rdev, + bool enable) +{ + if (enable) + WREG32_P(MCLK_PWRMGT_CNTL, 0, ~MPLL_PWRMGT_OFF); + else + WREG32_P(MCLK_PWRMGT_CNTL, MPLL_PWRMGT_OFF, ~MPLL_PWRMGT_OFF); +} + +int cypress_notify_smc_display_change(struct radeon_device *rdev, + bool has_display) +{ + PPSMC_Msg msg = has_display ? + (PPSMC_Msg)PPSMC_MSG_HasDisplay : (PPSMC_Msg)PPSMC_MSG_NoDisplay; + + if (rv770_send_msg_to_smc(rdev, msg) != PPSMC_Result_OK) + return -EINVAL; + + return 0; +} + +void cypress_program_response_times(struct radeon_device *rdev) +{ + u32 reference_clock; + u32 mclk_switch_limit; + + reference_clock = radeon_get_xclk(rdev); + mclk_switch_limit = (460 * reference_clock) / 100; + + rv770_write_smc_soft_register(rdev, + RV770_SMC_SOFT_REGISTER_mclk_switch_lim, + mclk_switch_limit); + + rv770_write_smc_soft_register(rdev, + RV770_SMC_SOFT_REGISTER_mvdd_chg_time, 1); + + rv770_write_smc_soft_register(rdev, + RV770_SMC_SOFT_REGISTER_mc_block_delay, 0xAA); + + rv770_program_response_times(rdev); + + if (ASIC_IS_LOMBOK(rdev)) + rv770_write_smc_soft_register(rdev, + RV770_SMC_SOFT_REGISTER_is_asic_lombok, 1); + +} + +static int cypress_pcie_performance_request(struct radeon_device *rdev, + u8 perf_req, bool advertise) +{ + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + u32 tmp; + + udelay(10); + tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL); + if ((perf_req == PCIE_PERF_REQ_PECI_GEN1) && (tmp & LC_CURRENT_DATA_RATE)) + return 0; + +#if defined(CONFIG_ACPI) + if ((perf_req == PCIE_PERF_REQ_PECI_GEN1) || + (perf_req == PCIE_PERF_REQ_PECI_GEN2)) { + eg_pi->pcie_performance_request_registered = true; + return radeon_acpi_pcie_performance_request(rdev, perf_req, advertise); + } else if ((perf_req == PCIE_PERF_REQ_REMOVE_REGISTRY) && + eg_pi->pcie_performance_request_registered) { + eg_pi->pcie_performance_request_registered = false; + return radeon_acpi_pcie_performance_request(rdev, perf_req, advertise); + } +#endif + + return 0; +} + +void cypress_advertise_gen2_capability(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + u32 tmp; + +#if defined(CONFIG_ACPI) + radeon_acpi_pcie_notify_device_ready(rdev); +#endif + + tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL); + + if ((tmp & LC_OTHER_SIDE_EVER_SENT_GEN2) && + (tmp & LC_OTHER_SIDE_SUPPORTS_GEN2)) + pi->pcie_gen2 = true; + else + pi->pcie_gen2 = false; + + if (!pi->pcie_gen2) + cypress_pcie_performance_request(rdev, PCIE_PERF_REQ_PECI_GEN2, true); + +} + +static u32 cypress_get_maximum_link_speed(struct radeon_ps *radeon_state) +{ + struct rv7xx_ps *state = rv770_get_ps(radeon_state); + + if (state->high.flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) + return 1; + return 0; +} + +void cypress_notify_link_speed_change_after_state_change(struct radeon_device *rdev) +{ + struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; + struct radeon_ps *radeon_current_state = rdev->pm.dpm.current_ps; + u32 pcie_link_speed_target = cypress_get_maximum_link_speed(radeon_new_state); + u32 pcie_link_speed_current = cypress_get_maximum_link_speed(radeon_current_state); + u8 request; + + if (pcie_link_speed_target < pcie_link_speed_current) { + if (pcie_link_speed_target == 0) + request = PCIE_PERF_REQ_PECI_GEN1; + else if (pcie_link_speed_target == 1) + request = PCIE_PERF_REQ_PECI_GEN2; + else + request = PCIE_PERF_REQ_PECI_GEN3; + + cypress_pcie_performance_request(rdev, request, false); + } +} + +void cypress_notify_link_speed_change_before_state_change(struct radeon_device *rdev) +{ + struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; + struct radeon_ps *radeon_current_state = rdev->pm.dpm.current_ps; + u32 pcie_link_speed_target = cypress_get_maximum_link_speed(radeon_new_state); + u32 pcie_link_speed_current = cypress_get_maximum_link_speed(radeon_current_state); + u8 request; + + if (pcie_link_speed_target > pcie_link_speed_current) { + if (pcie_link_speed_target == 0) + request = PCIE_PERF_REQ_PECI_GEN1; + else if (pcie_link_speed_target == 1) + request = PCIE_PERF_REQ_PECI_GEN2; + else + request = PCIE_PERF_REQ_PECI_GEN3; + + cypress_pcie_performance_request(rdev, request, false); + } +} + +static int cypress_populate_voltage_value(struct radeon_device *rdev, + struct atom_voltage_table *table, + u16 value, RV770_SMC_VOLTAGE_VALUE *voltage) +{ + unsigned int i; + + for (i = 0; i < table->count; i++) { + if (value <= table->entries[i].value) { + voltage->index = (u8)i; + voltage->value = cpu_to_be16(table->entries[i].value); + break; + } + } + + if (i == table->count) + return -EINVAL; + + return 0; +} + +static u8 cypress_get_strobe_mode_settings(struct radeon_device *rdev, u32 mclk) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + u8 result = 0; + bool strobe_mode = false; + + if (pi->mem_gddr5) { + if (mclk <= pi->mclk_strobe_mode_threshold) + strobe_mode = true; + result = cypress_get_mclk_frequency_ratio(rdev, mclk, strobe_mode); + + if (strobe_mode) + result |= SMC_STROBE_ENABLE; + } + + return result; +} + +static u32 cypress_map_clkf_to_ibias(struct radeon_device *rdev, u32 clkf) +{ + u32 ref_clk = rdev->clock.mpll.reference_freq; + u32 vco = clkf * ref_clk; + + /* 100 Mhz ref clk */ + if (ref_clk == 10000) { + if (vco > 500000) + return 0xC6; + if (vco > 400000) + return 0x9D; + if (vco > 330000) + return 0x6C; + if (vco > 250000) + return 0x2B; + if (vco > 160000) + return 0x5B; + if (vco > 120000) + return 0x0A; + return 0x4B; + } + + /* 27 Mhz ref clk */ + if (vco > 250000) + return 0x8B; + if (vco > 200000) + return 0xCC; + if (vco > 150000) + return 0x9B; + return 0x6B; +} + +static int cypress_populate_mclk_value(struct radeon_device *rdev, + u32 engine_clock, u32 memory_clock, + RV7XX_SMC_MCLK_VALUE *mclk, + bool strobe_mode, bool dll_state_on) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + + u32 mpll_ad_func_cntl = + pi->clk_regs.rv770.mpll_ad_func_cntl; + u32 mpll_ad_func_cntl_2 = + pi->clk_regs.rv770.mpll_ad_func_cntl_2; + u32 mpll_dq_func_cntl = + pi->clk_regs.rv770.mpll_dq_func_cntl; + u32 mpll_dq_func_cntl_2 = + pi->clk_regs.rv770.mpll_dq_func_cntl_2; + u32 mclk_pwrmgt_cntl = + pi->clk_regs.rv770.mclk_pwrmgt_cntl; + u32 dll_cntl = + pi->clk_regs.rv770.dll_cntl; + u32 mpll_ss1 = pi->clk_regs.rv770.mpll_ss1; + u32 mpll_ss2 = pi->clk_regs.rv770.mpll_ss2; + struct atom_clock_dividers dividers; + u32 ibias; + u32 dll_speed; + int ret; + u32 mc_seq_misc7; + + ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_MEMORY_PLL_PARAM, + memory_clock, strobe_mode, ÷rs); + if (ret) + return ret; + + if (!strobe_mode) { + mc_seq_misc7 = RREG32(MC_SEQ_MISC7); + + if(mc_seq_misc7 & 0x8000000) + dividers.post_div = 1; + } + + ibias = cypress_map_clkf_to_ibias(rdev, dividers.whole_fb_div); + + mpll_ad_func_cntl &= ~(CLKR_MASK | + YCLK_POST_DIV_MASK | + CLKF_MASK | + CLKFRAC_MASK | + IBIAS_MASK); + mpll_ad_func_cntl |= CLKR(dividers.ref_div); + mpll_ad_func_cntl |= YCLK_POST_DIV(dividers.post_div); + mpll_ad_func_cntl |= CLKF(dividers.whole_fb_div); + mpll_ad_func_cntl |= CLKFRAC(dividers.frac_fb_div); + mpll_ad_func_cntl |= IBIAS(ibias); + + if (dividers.vco_mode) + mpll_ad_func_cntl_2 |= VCO_MODE; + else + mpll_ad_func_cntl_2 &= ~VCO_MODE; + + if (pi->mem_gddr5) { + mpll_dq_func_cntl &= ~(CLKR_MASK | + YCLK_POST_DIV_MASK | + CLKF_MASK | + CLKFRAC_MASK | + IBIAS_MASK); + mpll_dq_func_cntl |= CLKR(dividers.ref_div); + mpll_dq_func_cntl |= YCLK_POST_DIV(dividers.post_div); + mpll_dq_func_cntl |= CLKF(dividers.whole_fb_div); + mpll_dq_func_cntl |= CLKFRAC(dividers.frac_fb_div); + mpll_dq_func_cntl |= IBIAS(ibias); + + if (strobe_mode) + mpll_dq_func_cntl &= ~PDNB; + else + mpll_dq_func_cntl |= PDNB; + + if (dividers.vco_mode) + mpll_dq_func_cntl_2 |= VCO_MODE; + else + mpll_dq_func_cntl_2 &= ~VCO_MODE; + } + + if (pi->mclk_ss) { + struct radeon_atom_ss ss; + u32 vco_freq = memory_clock * dividers.post_div; + + if (radeon_atombios_get_asic_ss_info(rdev, &ss, + ASIC_INTERNAL_MEMORY_SS, vco_freq)) { + u32 reference_clock = rdev->clock.mpll.reference_freq; + u32 decoded_ref = rv740_get_decoded_reference_divider(dividers.ref_div); + u32 clk_s = reference_clock * 5 / (decoded_ref * ss.rate); + u32 clk_v = ss.percentage * + (0x4000 * dividers.whole_fb_div + 0x800 * dividers.frac_fb_div) / (clk_s * 625); + + mpll_ss1 &= ~CLKV_MASK; + mpll_ss1 |= CLKV(clk_v); + + mpll_ss2 &= ~CLKS_MASK; + mpll_ss2 |= CLKS(clk_s); + } + } + + dll_speed = rv740_get_dll_speed(pi->mem_gddr5, + memory_clock); + + mclk_pwrmgt_cntl &= ~DLL_SPEED_MASK; + mclk_pwrmgt_cntl |= DLL_SPEED(dll_speed); + if (dll_state_on) + mclk_pwrmgt_cntl |= (MRDCKA0_PDNB | + MRDCKA1_PDNB | + MRDCKB0_PDNB | + MRDCKB1_PDNB | + MRDCKC0_PDNB | + MRDCKC1_PDNB | + MRDCKD0_PDNB | + MRDCKD1_PDNB); + else + mclk_pwrmgt_cntl &= ~(MRDCKA0_PDNB | + MRDCKA1_PDNB | + MRDCKB0_PDNB | + MRDCKB1_PDNB | + MRDCKC0_PDNB | + MRDCKC1_PDNB | + MRDCKD0_PDNB | + MRDCKD1_PDNB); + + mclk->mclk770.mclk_value = cpu_to_be32(memory_clock); + mclk->mclk770.vMPLL_AD_FUNC_CNTL = cpu_to_be32(mpll_ad_func_cntl); + mclk->mclk770.vMPLL_AD_FUNC_CNTL_2 = cpu_to_be32(mpll_ad_func_cntl_2); + mclk->mclk770.vMPLL_DQ_FUNC_CNTL = cpu_to_be32(mpll_dq_func_cntl); + mclk->mclk770.vMPLL_DQ_FUNC_CNTL_2 = cpu_to_be32(mpll_dq_func_cntl_2); + mclk->mclk770.vMCLK_PWRMGT_CNTL = cpu_to_be32(mclk_pwrmgt_cntl); + mclk->mclk770.vDLL_CNTL = cpu_to_be32(dll_cntl); + mclk->mclk770.vMPLL_SS = cpu_to_be32(mpll_ss1); + mclk->mclk770.vMPLL_SS2 = cpu_to_be32(mpll_ss2); + + return 0; +} + +static u8 cypress_get_mclk_frequency_ratio(struct radeon_device *rdev, + u32 memory_clock, bool strobe_mode) +{ + u8 mc_para_index; + + if (rdev->family >= CHIP_BARTS) { + if (strobe_mode) { + if (memory_clock < 10000) + mc_para_index = 0x00; + else if (memory_clock > 47500) + mc_para_index = 0x0f; + else + mc_para_index = (u8)((memory_clock - 10000) / 2500); + } else { + if (memory_clock < 65000) + mc_para_index = 0x00; + else if (memory_clock > 135000) + mc_para_index = 0x0f; + else + mc_para_index = (u8)((memory_clock - 60000) / 5000); + } + } else { + if (strobe_mode) { + if (memory_clock < 10000) + mc_para_index = 0x00; + else if (memory_clock > 47500) + mc_para_index = 0x0f; + else + mc_para_index = (u8)((memory_clock - 10000) / 2500); + } else { + if (memory_clock < 40000) + mc_para_index = 0x00; + else if (memory_clock > 115000) + mc_para_index = 0x0f; + else + mc_para_index = (u8)((memory_clock - 40000) / 5000); + } + } + return mc_para_index; +} + +static int cypress_populate_mvdd_value(struct radeon_device *rdev, + u32 mclk, + RV770_SMC_VOLTAGE_VALUE *voltage) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + + if (!pi->mvdd_control) { + voltage->index = eg_pi->mvdd_high_index; + voltage->value = cpu_to_be16(MVDD_HIGH_VALUE); + return 0; + } + + if (mclk <= pi->mvdd_split_frequency) { + voltage->index = eg_pi->mvdd_low_index; + voltage->value = cpu_to_be16(MVDD_LOW_VALUE); + } else { + voltage->index = eg_pi->mvdd_high_index; + voltage->value = cpu_to_be16(MVDD_HIGH_VALUE); + } + + return 0; +} + +int cypress_convert_power_level_to_smc(struct radeon_device *rdev, + struct rv7xx_pl *pl, + RV770_SMC_HW_PERFORMANCE_LEVEL *level, + u8 watermark_level) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + int ret; + bool dll_state_on; + + level->gen2PCIE = pi->pcie_gen2 ? + ((pl->flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) ? 1 : 0) : 0; + level->gen2XSP = (pl->flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) ? 1 : 0; + level->backbias = (pl->flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE) ? 1 : 0; + level->displayWatermark = watermark_level; + + ret = rv740_populate_sclk_value(rdev, pl->sclk, &level->sclk); + if (ret) + return ret; + + level->mcFlags = 0; + if (pi->mclk_stutter_mode_threshold && + (pl->mclk <= pi->mclk_stutter_mode_threshold)) { + level->mcFlags |= SMC_MC_STUTTER_EN; + if (eg_pi->sclk_deep_sleep) + level->stateFlags |= PPSMC_STATEFLAG_AUTO_PULSE_SKIP; + else + level->stateFlags &= ~PPSMC_STATEFLAG_AUTO_PULSE_SKIP; + } + + if (pi->mem_gddr5) { + if (pl->mclk > pi->mclk_edc_enable_threshold) + level->mcFlags |= SMC_MC_EDC_RD_FLAG; + + if (pl->mclk > eg_pi->mclk_edc_wr_enable_threshold) + level->mcFlags |= SMC_MC_EDC_WR_FLAG; + + level->strobeMode = cypress_get_strobe_mode_settings(rdev, pl->mclk); + + if (level->strobeMode & SMC_STROBE_ENABLE) { + if (cypress_get_mclk_frequency_ratio(rdev, pl->mclk, true) >= + ((RREG32(MC_SEQ_MISC7) >> 16) & 0xf)) + dll_state_on = ((RREG32(MC_SEQ_MISC5) >> 1) & 0x1) ? true : false; + else + dll_state_on = ((RREG32(MC_SEQ_MISC6) >> 1) & 0x1) ? true : false; + } else + dll_state_on = eg_pi->dll_default_on; + + ret = cypress_populate_mclk_value(rdev, + pl->sclk, + pl->mclk, + &level->mclk, + (level->strobeMode & SMC_STROBE_ENABLE) != 0, + dll_state_on); + } else { + ret = cypress_populate_mclk_value(rdev, + pl->sclk, + pl->mclk, + &level->mclk, + true, + true); + } + if (ret) + return ret; + + ret = cypress_populate_voltage_value(rdev, + &eg_pi->vddc_voltage_table, + pl->vddc, + &level->vddc); + if (ret) + return ret; + + if (eg_pi->vddci_control) { + ret = cypress_populate_voltage_value(rdev, + &eg_pi->vddci_voltage_table, + pl->vddci, + &level->vddci); + if (ret) + return ret; + } + + ret = cypress_populate_mvdd_value(rdev, pl->mclk, &level->mvdd); + + return ret; +} + +static int cypress_convert_power_state_to_smc(struct radeon_device *rdev, + struct radeon_ps *radeon_state, + RV770_SMC_SWSTATE *smc_state) +{ + struct rv7xx_ps *state = rv770_get_ps(radeon_state); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + int ret; + + if (!(radeon_state->caps & ATOM_PPLIB_DISALLOW_ON_DC)) + smc_state->flags |= PPSMC_SWSTATE_FLAG_DC; + + ret = cypress_convert_power_level_to_smc(rdev, + &state->low, + &smc_state->levels[0], + PPSMC_DISPLAY_WATERMARK_LOW); + if (ret) + return ret; + + ret = cypress_convert_power_level_to_smc(rdev, + &state->medium, + &smc_state->levels[1], + PPSMC_DISPLAY_WATERMARK_LOW); + if (ret) + return ret; + + ret = cypress_convert_power_level_to_smc(rdev, + &state->high, + &smc_state->levels[2], + PPSMC_DISPLAY_WATERMARK_HIGH); + if (ret) + return ret; + + smc_state->levels[0].arbValue = MC_CG_ARB_FREQ_F1; + smc_state->levels[1].arbValue = MC_CG_ARB_FREQ_F2; + smc_state->levels[2].arbValue = MC_CG_ARB_FREQ_F3; + + if (eg_pi->dynamic_ac_timing) { + smc_state->levels[0].ACIndex = 2; + smc_state->levels[1].ACIndex = 3; + smc_state->levels[2].ACIndex = 4; + } else { + smc_state->levels[0].ACIndex = 0; + smc_state->levels[1].ACIndex = 0; + smc_state->levels[2].ACIndex = 0; + } + + rv770_populate_smc_sp(rdev, radeon_state, smc_state); + + return rv770_populate_smc_t(rdev, radeon_state, smc_state); +} + +static void cypress_convert_mc_registers(struct evergreen_mc_reg_entry *entry, + SMC_Evergreen_MCRegisterSet *data, + u32 num_entries, u32 valid_flag) +{ + u32 i, j; + + for (i = 0, j = 0; j < num_entries; j++) { + if (valid_flag & (1 << j)) { + data->value[i] = cpu_to_be32(entry->mc_data[j]); + i++; + } + } +} + +static void cypress_convert_mc_reg_table_entry_to_smc(struct radeon_device *rdev, + struct rv7xx_pl *pl, + SMC_Evergreen_MCRegisterSet *mc_reg_table_data) +{ + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + u32 i = 0; + + for (i = 0; i < eg_pi->mc_reg_table.num_entries; i++) { + if (pl->mclk <= + eg_pi->mc_reg_table.mc_reg_table_entry[i].mclk_max) + break; + } + + if ((i == eg_pi->mc_reg_table.num_entries) && (i > 0)) + --i; + + cypress_convert_mc_registers(&eg_pi->mc_reg_table.mc_reg_table_entry[i], + mc_reg_table_data, + eg_pi->mc_reg_table.last, + eg_pi->mc_reg_table.valid_flag); +} + +static void cypress_convert_mc_reg_table_to_smc(struct radeon_device *rdev, + struct radeon_ps *radeon_state, + SMC_Evergreen_MCRegisters *mc_reg_table) +{ + struct rv7xx_ps *state = rv770_get_ps(radeon_state); + + cypress_convert_mc_reg_table_entry_to_smc(rdev, + &state->low, + &mc_reg_table->data[2]); + cypress_convert_mc_reg_table_entry_to_smc(rdev, + &state->medium, + &mc_reg_table->data[3]); + cypress_convert_mc_reg_table_entry_to_smc(rdev, + &state->high, + &mc_reg_table->data[4]); +} + +int cypress_upload_sw_state(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; + u16 address = pi->state_table_start + + offsetof(RV770_SMC_STATETABLE, driverState); + RV770_SMC_SWSTATE state = { 0 }; + int ret; + + ret = cypress_convert_power_state_to_smc(rdev, radeon_new_state, &state); + if (ret) + return ret; + + return rv770_copy_bytes_to_smc(rdev, address, (u8 *)&state, + sizeof(RV770_SMC_SWSTATE), + pi->sram_end); +} + +int cypress_upload_mc_reg_table(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; + SMC_Evergreen_MCRegisters mc_reg_table = { 0 }; + u16 address; + + cypress_convert_mc_reg_table_to_smc(rdev, radeon_new_state, &mc_reg_table); + + address = eg_pi->mc_reg_table_start + + (u16)offsetof(SMC_Evergreen_MCRegisters, data[2]); + + return rv770_copy_bytes_to_smc(rdev, address, + (u8 *)&mc_reg_table.data[2], + sizeof(SMC_Evergreen_MCRegisterSet) * 3, + pi->sram_end); +} + +u32 cypress_calculate_burst_time(struct radeon_device *rdev, + u32 engine_clock, u32 memory_clock) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + u32 multiplier = pi->mem_gddr5 ? 1 : 2; + u32 result = (4 * multiplier * engine_clock) / (memory_clock / 2); + u32 burst_time; + + if (result <= 4) + burst_time = 0; + else if (result < 8) + burst_time = result - 4; + else { + burst_time = result / 2 ; + if (burst_time > 18) + burst_time = 18; + } + + return burst_time; +} + +void cypress_program_memory_timing_parameters(struct radeon_device *rdev) +{ + struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; + struct rv7xx_ps *new_state = rv770_get_ps(radeon_new_state); + u32 mc_arb_burst_time = RREG32(MC_ARB_BURST_TIME); + + mc_arb_burst_time &= ~(STATE1_MASK | STATE2_MASK | STATE3_MASK); + + mc_arb_burst_time |= STATE1(cypress_calculate_burst_time(rdev, + new_state->low.sclk, + new_state->low.mclk)); + mc_arb_burst_time |= STATE2(cypress_calculate_burst_time(rdev, + new_state->medium.sclk, + new_state->medium.mclk)); + mc_arb_burst_time |= STATE3(cypress_calculate_burst_time(rdev, + new_state->high.sclk, + new_state->high.mclk)); + + rv730_program_memory_timing_parameters(rdev, radeon_new_state); + + WREG32(MC_ARB_BURST_TIME, mc_arb_burst_time); +} + +static void cypress_populate_mc_reg_addresses(struct radeon_device *rdev, + SMC_Evergreen_MCRegisters *mc_reg_table) +{ + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + u32 i, j; + + for (i = 0, j = 0; j < eg_pi->mc_reg_table.last; j++) { + if (eg_pi->mc_reg_table.valid_flag & (1 << j)) { + mc_reg_table->address[i].s0 = + cpu_to_be16(eg_pi->mc_reg_table.mc_reg_address[j].s0); + mc_reg_table->address[i].s1 = + cpu_to_be16(eg_pi->mc_reg_table.mc_reg_address[j].s1); + i++; + } + } + + mc_reg_table->last = (u8)i; +} + +static void cypress_set_mc_reg_address_table(struct radeon_device *rdev) +{ + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + u32 i = 0; + + eg_pi->mc_reg_table.mc_reg_address[i].s0 = MC_SEQ_RAS_TIMING_LP >> 2; + eg_pi->mc_reg_table.mc_reg_address[i].s1 = MC_SEQ_RAS_TIMING >> 2; + i++; + + eg_pi->mc_reg_table.mc_reg_address[i].s0 = MC_SEQ_CAS_TIMING_LP >> 2; + eg_pi->mc_reg_table.mc_reg_address[i].s1 = MC_SEQ_CAS_TIMING >> 2; + i++; + + eg_pi->mc_reg_table.mc_reg_address[i].s0 = MC_SEQ_MISC_TIMING_LP >> 2; + eg_pi->mc_reg_table.mc_reg_address[i].s1 = MC_SEQ_MISC_TIMING >> 2; + i++; + + eg_pi->mc_reg_table.mc_reg_address[i].s0 = MC_SEQ_MISC_TIMING2_LP >> 2; + eg_pi->mc_reg_table.mc_reg_address[i].s1 = MC_SEQ_MISC_TIMING2 >> 2; + i++; + + eg_pi->mc_reg_table.mc_reg_address[i].s0 = MC_SEQ_RD_CTL_D0_LP >> 2; + eg_pi->mc_reg_table.mc_reg_address[i].s1 = MC_SEQ_RD_CTL_D0 >> 2; + i++; + + eg_pi->mc_reg_table.mc_reg_address[i].s0 = MC_SEQ_RD_CTL_D1_LP >> 2; + eg_pi->mc_reg_table.mc_reg_address[i].s1 = MC_SEQ_RD_CTL_D1 >> 2; + i++; + + eg_pi->mc_reg_table.mc_reg_address[i].s0 = MC_SEQ_WR_CTL_D0_LP >> 2; + eg_pi->mc_reg_table.mc_reg_address[i].s1 = MC_SEQ_WR_CTL_D0 >> 2; + i++; + + eg_pi->mc_reg_table.mc_reg_address[i].s0 = MC_SEQ_WR_CTL_D1_LP >> 2; + eg_pi->mc_reg_table.mc_reg_address[i].s1 = MC_SEQ_WR_CTL_D1 >> 2; + i++; + + eg_pi->mc_reg_table.mc_reg_address[i].s0 = MC_SEQ_PMG_CMD_EMRS_LP >> 2; + eg_pi->mc_reg_table.mc_reg_address[i].s1 = MC_PMG_CMD_EMRS >> 2; + i++; + + eg_pi->mc_reg_table.mc_reg_address[i].s0 = MC_SEQ_PMG_CMD_MRS_LP >> 2; + eg_pi->mc_reg_table.mc_reg_address[i].s1 = MC_PMG_CMD_MRS >> 2; + i++; + + eg_pi->mc_reg_table.mc_reg_address[i].s0 = MC_SEQ_PMG_CMD_MRS1_LP >> 2; + eg_pi->mc_reg_table.mc_reg_address[i].s1 = MC_PMG_CMD_MRS1 >> 2; + i++; + + eg_pi->mc_reg_table.mc_reg_address[i].s0 = MC_SEQ_MISC1 >> 2; + eg_pi->mc_reg_table.mc_reg_address[i].s1 = MC_SEQ_MISC1 >> 2; + i++; + + eg_pi->mc_reg_table.mc_reg_address[i].s0 = MC_SEQ_RESERVE_M >> 2; + eg_pi->mc_reg_table.mc_reg_address[i].s1 = MC_SEQ_RESERVE_M >> 2; + i++; + + eg_pi->mc_reg_table.mc_reg_address[i].s0 = MC_SEQ_MISC3 >> 2; + eg_pi->mc_reg_table.mc_reg_address[i].s1 = MC_SEQ_MISC3 >> 2; + i++; + + eg_pi->mc_reg_table.last = (u8)i; +} + +static void cypress_retrieve_ac_timing_for_one_entry(struct radeon_device *rdev, + struct evergreen_mc_reg_entry *entry) +{ + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + u32 i; + + for (i = 0; i < eg_pi->mc_reg_table.last; i++) + entry->mc_data[i] = + RREG32(eg_pi->mc_reg_table.mc_reg_address[i].s1 << 2); + +} + +static void cypress_retrieve_ac_timing_for_all_ranges(struct radeon_device *rdev, + struct atom_memory_clock_range_table *range_table) +{ + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + u32 i, j; + + for (i = 0; i < range_table->num_entries; i++) { + eg_pi->mc_reg_table.mc_reg_table_entry[i].mclk_max = + range_table->mclk[i]; + radeon_atom_set_ac_timing(rdev, range_table->mclk[i]); + cypress_retrieve_ac_timing_for_one_entry(rdev, + &eg_pi->mc_reg_table.mc_reg_table_entry[i]); + } + + eg_pi->mc_reg_table.num_entries = range_table->num_entries; + eg_pi->mc_reg_table.valid_flag = 0; + + for (i = 0; i < eg_pi->mc_reg_table.last; i++) { + for (j = 1; j < range_table->num_entries; j++) { + if (eg_pi->mc_reg_table.mc_reg_table_entry[j-1].mc_data[i] != + eg_pi->mc_reg_table.mc_reg_table_entry[j].mc_data[i]) { + eg_pi->mc_reg_table.valid_flag |= (1 << i); + break; + } + } + } +} + +static int cypress_initialize_mc_reg_table(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + u8 module_index = rv770_get_memory_module_index(rdev); + struct atom_memory_clock_range_table range_table = { 0 }; + int ret; + + ret = radeon_atom_get_mclk_range_table(rdev, + pi->mem_gddr5, + module_index, &range_table); + if (ret) + return ret; + + cypress_retrieve_ac_timing_for_all_ranges(rdev, &range_table); + + return 0; +} + +static void cypress_wait_for_mc_sequencer(struct radeon_device *rdev, u8 value) +{ + u32 i, j; + u32 channels = 2; + + if ((rdev->family == CHIP_CYPRESS) || + (rdev->family == CHIP_HEMLOCK)) + channels = 4; + else if (rdev->family == CHIP_CEDAR) + channels = 1; + + for (i = 0; i < channels; i++) { + if ((rdev->family == CHIP_CYPRESS) || + (rdev->family == CHIP_HEMLOCK)) { + WREG32_P(MC_CONFIG_MCD, MC_RD_ENABLE_MCD(i), ~MC_RD_ENABLE_MCD_MASK); + WREG32_P(MC_CG_CONFIG_MCD, MC_RD_ENABLE_MCD(i), ~MC_RD_ENABLE_MCD_MASK); + } else { + WREG32_P(MC_CONFIG, MC_RD_ENABLE(i), ~MC_RD_ENABLE_MASK); + WREG32_P(MC_CG_CONFIG, MC_RD_ENABLE(i), ~MC_RD_ENABLE_MASK); + } + for (j = 0; j < rdev->usec_timeout; j++) { + if (((RREG32(MC_SEQ_CG) & CG_SEQ_RESP_MASK) >> CG_SEQ_RESP_SHIFT) == value) + break; + udelay(1); + } + } +} + +static void cypress_force_mc_use_s1(struct radeon_device *rdev) +{ + struct radeon_ps *radeon_boot_state = rdev->pm.dpm.boot_ps; + struct rv7xx_ps *boot_state = rv770_get_ps(radeon_boot_state); + u32 strobe_mode; + u32 mc_seq_cg; + int i; + + if (RREG32(MC_SEQ_STATUS_M) & PMG_PWRSTATE) + return; + + radeon_atom_set_ac_timing(rdev, boot_state->low.mclk); + radeon_mc_wait_for_idle(rdev); + + if ((rdev->family == CHIP_CYPRESS) || + (rdev->family == CHIP_HEMLOCK)) { + WREG32(MC_CONFIG_MCD, 0xf); + WREG32(MC_CG_CONFIG_MCD, 0xf); + } else { + WREG32(MC_CONFIG, 0xf); + WREG32(MC_CG_CONFIG, 0xf); + } + + for (i = 0; i < rdev->num_crtc; i++) + radeon_wait_for_vblank(rdev, i); + + WREG32(MC_SEQ_CG, MC_CG_SEQ_YCLK_SUSPEND); + cypress_wait_for_mc_sequencer(rdev, MC_CG_SEQ_YCLK_SUSPEND); + + strobe_mode = cypress_get_strobe_mode_settings(rdev, + boot_state->low.mclk); + + mc_seq_cg = CG_SEQ_REQ(MC_CG_SEQ_DRAMCONF_S1); + mc_seq_cg |= SEQ_CG_RESP(strobe_mode); + WREG32(MC_SEQ_CG, mc_seq_cg); + + for (i = 0; i < rdev->usec_timeout; i++) { + if (RREG32(MC_SEQ_STATUS_M) & PMG_PWRSTATE) + break; + udelay(1); + } + + mc_seq_cg &= ~CG_SEQ_REQ_MASK; + mc_seq_cg |= CG_SEQ_REQ(MC_CG_SEQ_YCLK_RESUME); + WREG32(MC_SEQ_CG, mc_seq_cg); + + cypress_wait_for_mc_sequencer(rdev, MC_CG_SEQ_YCLK_RESUME); +} + +static void cypress_copy_ac_timing_from_s1_to_s0(struct radeon_device *rdev) +{ + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + u32 value; + u32 i; + + for (i = 0; i < eg_pi->mc_reg_table.last; i++) { + value = RREG32(eg_pi->mc_reg_table.mc_reg_address[i].s1 << 2); + WREG32(eg_pi->mc_reg_table.mc_reg_address[i].s0 << 2, value); + } +} + +static void cypress_force_mc_use_s0(struct radeon_device *rdev) +{ + struct radeon_ps *radeon_boot_state = rdev->pm.dpm.boot_ps; + struct rv7xx_ps *boot_state = rv770_get_ps(radeon_boot_state); + u32 strobe_mode; + u32 mc_seq_cg; + int i; + + cypress_copy_ac_timing_from_s1_to_s0(rdev); + radeon_mc_wait_for_idle(rdev); + + if ((rdev->family == CHIP_CYPRESS) || + (rdev->family == CHIP_HEMLOCK)) { + WREG32(MC_CONFIG_MCD, 0xf); + WREG32(MC_CG_CONFIG_MCD, 0xf); + } else { + WREG32(MC_CONFIG, 0xf); + WREG32(MC_CG_CONFIG, 0xf); + } + + for (i = 0; i < rdev->num_crtc; i++) + radeon_wait_for_vblank(rdev, i); + + WREG32(MC_SEQ_CG, MC_CG_SEQ_YCLK_SUSPEND); + cypress_wait_for_mc_sequencer(rdev, MC_CG_SEQ_YCLK_SUSPEND); + + strobe_mode = cypress_get_strobe_mode_settings(rdev, + boot_state->low.mclk); + + mc_seq_cg = CG_SEQ_REQ(MC_CG_SEQ_DRAMCONF_S0); + mc_seq_cg |= SEQ_CG_RESP(strobe_mode); + WREG32(MC_SEQ_CG, mc_seq_cg); + + for (i = 0; i < rdev->usec_timeout; i++) { + if (!(RREG32(MC_SEQ_STATUS_M) & PMG_PWRSTATE)) + break; + udelay(1); + } + + mc_seq_cg &= ~CG_SEQ_REQ_MASK; + mc_seq_cg |= CG_SEQ_REQ(MC_CG_SEQ_YCLK_RESUME); + WREG32(MC_SEQ_CG, mc_seq_cg); + + cypress_wait_for_mc_sequencer(rdev, MC_CG_SEQ_YCLK_RESUME); +} + +static int cypress_populate_initial_mvdd_value(struct radeon_device *rdev, + RV770_SMC_VOLTAGE_VALUE *voltage) +{ + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + + voltage->index = eg_pi->mvdd_high_index; + voltage->value = cpu_to_be16(MVDD_HIGH_VALUE); + + return 0; +} + +int cypress_populate_smc_initial_state(struct radeon_device *rdev, + struct radeon_ps *radeon_initial_state, + RV770_SMC_STATETABLE *table) +{ + struct rv7xx_ps *initial_state = rv770_get_ps(radeon_initial_state); + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + u32 a_t; + + table->initialState.levels[0].mclk.mclk770.vMPLL_AD_FUNC_CNTL = + cpu_to_be32(pi->clk_regs.rv770.mpll_ad_func_cntl); + table->initialState.levels[0].mclk.mclk770.vMPLL_AD_FUNC_CNTL_2 = + cpu_to_be32(pi->clk_regs.rv770.mpll_ad_func_cntl_2); + table->initialState.levels[0].mclk.mclk770.vMPLL_DQ_FUNC_CNTL = + cpu_to_be32(pi->clk_regs.rv770.mpll_dq_func_cntl); + table->initialState.levels[0].mclk.mclk770.vMPLL_DQ_FUNC_CNTL_2 = + cpu_to_be32(pi->clk_regs.rv770.mpll_dq_func_cntl_2); + table->initialState.levels[0].mclk.mclk770.vMCLK_PWRMGT_CNTL = + cpu_to_be32(pi->clk_regs.rv770.mclk_pwrmgt_cntl); + table->initialState.levels[0].mclk.mclk770.vDLL_CNTL = + cpu_to_be32(pi->clk_regs.rv770.dll_cntl); + + table->initialState.levels[0].mclk.mclk770.vMPLL_SS = + cpu_to_be32(pi->clk_regs.rv770.mpll_ss1); + table->initialState.levels[0].mclk.mclk770.vMPLL_SS2 = + cpu_to_be32(pi->clk_regs.rv770.mpll_ss2); + + table->initialState.levels[0].mclk.mclk770.mclk_value = + cpu_to_be32(initial_state->low.mclk); + + table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL = + cpu_to_be32(pi->clk_regs.rv770.cg_spll_func_cntl); + table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_2 = + cpu_to_be32(pi->clk_regs.rv770.cg_spll_func_cntl_2); + table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_3 = + cpu_to_be32(pi->clk_regs.rv770.cg_spll_func_cntl_3); + table->initialState.levels[0].sclk.vCG_SPLL_SPREAD_SPECTRUM = + cpu_to_be32(pi->clk_regs.rv770.cg_spll_spread_spectrum); + table->initialState.levels[0].sclk.vCG_SPLL_SPREAD_SPECTRUM_2 = + cpu_to_be32(pi->clk_regs.rv770.cg_spll_spread_spectrum_2); + + table->initialState.levels[0].sclk.sclk_value = + cpu_to_be32(initial_state->low.sclk); + + table->initialState.levels[0].arbValue = MC_CG_ARB_FREQ_F0; + + table->initialState.levels[0].ACIndex = 0; + + cypress_populate_voltage_value(rdev, + &eg_pi->vddc_voltage_table, + initial_state->low.vddc, + &table->initialState.levels[0].vddc); + + if (eg_pi->vddci_control) + cypress_populate_voltage_value(rdev, + &eg_pi->vddci_voltage_table, + initial_state->low.vddci, + &table->initialState.levels[0].vddci); + + cypress_populate_initial_mvdd_value(rdev, + &table->initialState.levels[0].mvdd); + + a_t = CG_R(0xffff) | CG_L(0); + table->initialState.levels[0].aT = cpu_to_be32(a_t); + + table->initialState.levels[0].bSP = cpu_to_be32(pi->dsp); + + + if (pi->boot_in_gen2) + table->initialState.levels[0].gen2PCIE = 1; + else + table->initialState.levels[0].gen2PCIE = 0; + if (initial_state->low.flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) + table->initialState.levels[0].gen2XSP = 1; + else + table->initialState.levels[0].gen2XSP = 0; + + if (pi->mem_gddr5) { + table->initialState.levels[0].strobeMode = + cypress_get_strobe_mode_settings(rdev, + initial_state->low.mclk); + + if (initial_state->low.mclk > pi->mclk_edc_enable_threshold) + table->initialState.levels[0].mcFlags = SMC_MC_EDC_RD_FLAG | SMC_MC_EDC_WR_FLAG; + else + table->initialState.levels[0].mcFlags = 0; + } + + table->initialState.levels[1] = table->initialState.levels[0]; + table->initialState.levels[2] = table->initialState.levels[0]; + + table->initialState.flags |= PPSMC_SWSTATE_FLAG_DC; + + return 0; +} + +int cypress_populate_smc_acpi_state(struct radeon_device *rdev, + RV770_SMC_STATETABLE *table) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + u32 mpll_ad_func_cntl = + pi->clk_regs.rv770.mpll_ad_func_cntl; + u32 mpll_ad_func_cntl_2 = + pi->clk_regs.rv770.mpll_ad_func_cntl_2; + u32 mpll_dq_func_cntl = + pi->clk_regs.rv770.mpll_dq_func_cntl; + u32 mpll_dq_func_cntl_2 = + pi->clk_regs.rv770.mpll_dq_func_cntl_2; + u32 spll_func_cntl = + pi->clk_regs.rv770.cg_spll_func_cntl; + u32 spll_func_cntl_2 = + pi->clk_regs.rv770.cg_spll_func_cntl_2; + u32 spll_func_cntl_3 = + pi->clk_regs.rv770.cg_spll_func_cntl_3; + u32 mclk_pwrmgt_cntl = + pi->clk_regs.rv770.mclk_pwrmgt_cntl; + u32 dll_cntl = + pi->clk_regs.rv770.dll_cntl; + + table->ACPIState = table->initialState; + + table->ACPIState.flags &= ~PPSMC_SWSTATE_FLAG_DC; + + if (pi->acpi_vddc) { + cypress_populate_voltage_value(rdev, + &eg_pi->vddc_voltage_table, + pi->acpi_vddc, + &table->ACPIState.levels[0].vddc); + if (pi->pcie_gen2) { + if (pi->acpi_pcie_gen2) + table->ACPIState.levels[0].gen2PCIE = 1; + else + table->ACPIState.levels[0].gen2PCIE = 0; + } else + table->ACPIState.levels[0].gen2PCIE = 0; + if (pi->acpi_pcie_gen2) + table->ACPIState.levels[0].gen2XSP = 1; + else + table->ACPIState.levels[0].gen2XSP = 0; + } else { + cypress_populate_voltage_value(rdev, + &eg_pi->vddc_voltage_table, + pi->min_vddc_in_table, + &table->ACPIState.levels[0].vddc); + table->ACPIState.levels[0].gen2PCIE = 0; + } + + if (eg_pi->acpi_vddci) { + if (eg_pi->vddci_control) { + cypress_populate_voltage_value(rdev, + &eg_pi->vddci_voltage_table, + eg_pi->acpi_vddci, + &table->ACPIState.levels[0].vddci); + } + } + + mpll_ad_func_cntl &= ~PDNB; + + mpll_ad_func_cntl_2 |= BIAS_GEN_PDNB | RESET_EN; + + if (pi->mem_gddr5) + mpll_dq_func_cntl &= ~PDNB; + mpll_dq_func_cntl_2 |= BIAS_GEN_PDNB | RESET_EN | BYPASS; + + mclk_pwrmgt_cntl |= (MRDCKA0_RESET | + MRDCKA1_RESET | + MRDCKB0_RESET | + MRDCKB1_RESET | + MRDCKC0_RESET | + MRDCKC1_RESET | + MRDCKD0_RESET | + MRDCKD1_RESET); + + mclk_pwrmgt_cntl &= ~(MRDCKA0_PDNB | + MRDCKA1_PDNB | + MRDCKB0_PDNB | + MRDCKB1_PDNB | + MRDCKC0_PDNB | + MRDCKC1_PDNB | + MRDCKD0_PDNB | + MRDCKD1_PDNB); + + dll_cntl |= (MRDCKA0_BYPASS | + MRDCKA1_BYPASS | + MRDCKB0_BYPASS | + MRDCKB1_BYPASS | + MRDCKC0_BYPASS | + MRDCKC1_BYPASS | + MRDCKD0_BYPASS | + MRDCKD1_BYPASS); + + /* evergreen only */ + if (rdev->family <= CHIP_HEMLOCK) + spll_func_cntl |= SPLL_RESET | SPLL_SLEEP | SPLL_BYPASS_EN; + + spll_func_cntl_2 &= ~SCLK_MUX_SEL_MASK; + spll_func_cntl_2 |= SCLK_MUX_SEL(4); + + table->ACPIState.levels[0].mclk.mclk770.vMPLL_AD_FUNC_CNTL = + cpu_to_be32(mpll_ad_func_cntl); + table->ACPIState.levels[0].mclk.mclk770.vMPLL_AD_FUNC_CNTL_2 = + cpu_to_be32(mpll_ad_func_cntl_2); + table->ACPIState.levels[0].mclk.mclk770.vMPLL_DQ_FUNC_CNTL = + cpu_to_be32(mpll_dq_func_cntl); + table->ACPIState.levels[0].mclk.mclk770.vMPLL_DQ_FUNC_CNTL_2 = + cpu_to_be32(mpll_dq_func_cntl_2); + table->ACPIState.levels[0].mclk.mclk770.vMCLK_PWRMGT_CNTL = + cpu_to_be32(mclk_pwrmgt_cntl); + table->ACPIState.levels[0].mclk.mclk770.vDLL_CNTL = cpu_to_be32(dll_cntl); + + table->ACPIState.levels[0].mclk.mclk770.mclk_value = 0; + + table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL = + cpu_to_be32(spll_func_cntl); + table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_2 = + cpu_to_be32(spll_func_cntl_2); + table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_3 = + cpu_to_be32(spll_func_cntl_3); + + table->ACPIState.levels[0].sclk.sclk_value = 0; + + cypress_populate_mvdd_value(rdev, 0, &table->ACPIState.levels[0].mvdd); + + if (eg_pi->dynamic_ac_timing) + table->ACPIState.levels[0].ACIndex = 1; + + table->ACPIState.levels[1] = table->ACPIState.levels[0]; + table->ACPIState.levels[2] = table->ACPIState.levels[0]; + + return 0; +} + +static void cypress_trim_voltage_table_to_fit_state_table(struct radeon_device *rdev, + struct atom_voltage_table *voltage_table) +{ + unsigned int i, diff; + + if (voltage_table->count <= MAX_NO_VREG_STEPS) + return; + + diff = voltage_table->count - MAX_NO_VREG_STEPS; + + for (i= 0; i < MAX_NO_VREG_STEPS; i++) + voltage_table->entries[i] = voltage_table->entries[i + diff]; + + voltage_table->count = MAX_NO_VREG_STEPS; +} + +int cypress_construct_voltage_tables(struct radeon_device *rdev) +{ + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + int ret; + + ret = radeon_atom_get_voltage_table(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC, + &eg_pi->vddc_voltage_table); + if (ret) + return ret; + + if (eg_pi->vddc_voltage_table.count > MAX_NO_VREG_STEPS) + cypress_trim_voltage_table_to_fit_state_table(rdev, + &eg_pi->vddc_voltage_table); + + if (eg_pi->vddci_control) { + ret = radeon_atom_get_voltage_table(rdev, SET_VOLTAGE_TYPE_ASIC_VDDCI, + &eg_pi->vddci_voltage_table); + if (ret) + return ret; + + if (eg_pi->vddci_voltage_table.count > MAX_NO_VREG_STEPS) + cypress_trim_voltage_table_to_fit_state_table(rdev, + &eg_pi->vddci_voltage_table); + } + + return 0; +} + +static void cypress_populate_smc_voltage_table(struct radeon_device *rdev, + struct atom_voltage_table *voltage_table, + RV770_SMC_STATETABLE *table) +{ + unsigned int i; + + for (i = 0; i < voltage_table->count; i++) { + table->highSMIO[i] = 0; + table->lowSMIO[i] |= cpu_to_be32(voltage_table->entries[i].smio_low); + } +} + +int cypress_populate_smc_voltage_tables(struct radeon_device *rdev, + RV770_SMC_STATETABLE *table) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + unsigned char i; + + if (eg_pi->vddc_voltage_table.count) { + cypress_populate_smc_voltage_table(rdev, + &eg_pi->vddc_voltage_table, + table); + + table->voltageMaskTable.highMask[RV770_SMC_VOLTAGEMASK_VDDC] = 0; + table->voltageMaskTable.lowMask[RV770_SMC_VOLTAGEMASK_VDDC] = + cpu_to_be32(eg_pi->vddc_voltage_table.mask_low); + + for (i = 0; i < eg_pi->vddc_voltage_table.count; i++) { + if (pi->max_vddc_in_table <= + eg_pi->vddc_voltage_table.entries[i].value) { + table->maxVDDCIndexInPPTable = i; + break; + } + } + } + + if (eg_pi->vddci_voltage_table.count) { + cypress_populate_smc_voltage_table(rdev, + &eg_pi->vddci_voltage_table, + table); + + table->voltageMaskTable.highMask[RV770_SMC_VOLTAGEMASK_VDDCI] = 0; + table->voltageMaskTable.lowMask[RV770_SMC_VOLTAGEMASK_VDDCI] = + cpu_to_be32(eg_pi->vddc_voltage_table.mask_low); + } + + return 0; +} + +static u32 cypress_get_mclk_split_point(struct atom_memory_info *memory_info) +{ + if ((memory_info->mem_type == MEM_TYPE_GDDR3) || + (memory_info->mem_type == MEM_TYPE_DDR3)) + return 30000; + + return 0; +} + +int cypress_get_mvdd_configuration(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + u8 module_index; + struct atom_memory_info memory_info; + u32 tmp = RREG32(GENERAL_PWRMGT); + + if (!(tmp & BACKBIAS_PAD_EN)) { + eg_pi->mvdd_high_index = 0; + eg_pi->mvdd_low_index = 1; + pi->mvdd_control = false; + return 0; + } + + if (tmp & BACKBIAS_VALUE) + eg_pi->mvdd_high_index = 1; + else + eg_pi->mvdd_high_index = 0; + + eg_pi->mvdd_low_index = + (eg_pi->mvdd_high_index == 0) ? 1 : 0; + + module_index = rv770_get_memory_module_index(rdev); + + if (radeon_atom_get_memory_info(rdev, module_index, &memory_info)) { + pi->mvdd_control = false; + return 0; + } + + pi->mvdd_split_frequency = + cypress_get_mclk_split_point(&memory_info); + + if (pi->mvdd_split_frequency == 0) { + pi->mvdd_control = false; + return 0; + } + + return 0; +} + +static int cypress_init_smc_table(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct radeon_ps *radeon_boot_state = rdev->pm.dpm.boot_ps; + RV770_SMC_STATETABLE *table = &pi->smc_statetable; + int ret; + + memset(table, 0, sizeof(RV770_SMC_STATETABLE)); + + cypress_populate_smc_voltage_tables(rdev, table); + + switch (rdev->pm.int_thermal_type) { + case THERMAL_TYPE_EVERGREEN: + case THERMAL_TYPE_EMC2103_WITH_INTERNAL: + table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_INTERNAL; + break; + case THERMAL_TYPE_NONE: + table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_NONE; + break; + default: + table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_EXTERNAL; + break; + } + + if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_HARDWAREDC) + table->systemFlags |= PPSMC_SYSTEMFLAG_GPIO_DC; + + if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_REGULATOR_HOT) + table->systemFlags |= PPSMC_SYSTEMFLAG_REGULATOR_HOT; + + if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_STEPVDDC) + table->systemFlags |= PPSMC_SYSTEMFLAG_STEPVDDC; + + if (pi->mem_gddr5) + table->systemFlags |= PPSMC_SYSTEMFLAG_GDDR5; + + ret = cypress_populate_smc_initial_state(rdev, radeon_boot_state, table); + if (ret) + return ret; + + ret = cypress_populate_smc_acpi_state(rdev, table); + if (ret) + return ret; + + table->driverState = table->initialState; + + return rv770_copy_bytes_to_smc(rdev, + pi->state_table_start, + (u8 *)table, sizeof(RV770_SMC_STATETABLE), + pi->sram_end); +} + +int cypress_populate_mc_reg_table(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct radeon_ps *radeon_boot_state = rdev->pm.dpm.boot_ps; + struct rv7xx_ps *boot_state = rv770_get_ps(radeon_boot_state); + SMC_Evergreen_MCRegisters mc_reg_table = { 0 }; + + rv770_write_smc_soft_register(rdev, + RV770_SMC_SOFT_REGISTER_seq_index, 1); + + cypress_populate_mc_reg_addresses(rdev, &mc_reg_table); + + cypress_convert_mc_reg_table_entry_to_smc(rdev, + &boot_state->low, + &mc_reg_table.data[0]); + + cypress_convert_mc_registers(&eg_pi->mc_reg_table.mc_reg_table_entry[0], + &mc_reg_table.data[1], eg_pi->mc_reg_table.last, + eg_pi->mc_reg_table.valid_flag); + + cypress_convert_mc_reg_table_to_smc(rdev, radeon_boot_state, &mc_reg_table); + + return rv770_copy_bytes_to_smc(rdev, eg_pi->mc_reg_table_start, + (u8 *)&mc_reg_table, sizeof(SMC_Evergreen_MCRegisters), + pi->sram_end); +} + +int cypress_get_table_locations(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + u32 tmp; + int ret; + + ret = rv770_read_smc_sram_dword(rdev, + EVERGREEN_SMC_FIRMWARE_HEADER_LOCATION + + EVERGREEN_SMC_FIRMWARE_HEADER_stateTable, + &tmp, pi->sram_end); + if (ret) + return ret; + + pi->state_table_start = (u16)tmp; + + ret = rv770_read_smc_sram_dword(rdev, + EVERGREEN_SMC_FIRMWARE_HEADER_LOCATION + + EVERGREEN_SMC_FIRMWARE_HEADER_softRegisters, + &tmp, pi->sram_end); + if (ret) + return ret; + + pi->soft_regs_start = (u16)tmp; + + ret = rv770_read_smc_sram_dword(rdev, + EVERGREEN_SMC_FIRMWARE_HEADER_LOCATION + + EVERGREEN_SMC_FIRMWARE_HEADER_mcRegisterTable, + &tmp, pi->sram_end); + if (ret) + return ret; + + eg_pi->mc_reg_table_start = (u16)tmp; + + return 0; +} + +void cypress_enable_display_gap(struct radeon_device *rdev) +{ + u32 tmp = RREG32(CG_DISPLAY_GAP_CNTL); + + tmp &= ~(DISP1_GAP_MASK | DISP2_GAP_MASK); + tmp |= (DISP1_GAP(R600_PM_DISPLAY_GAP_IGNORE) | + DISP2_GAP(R600_PM_DISPLAY_GAP_IGNORE)); + + tmp &= ~(DISP1_GAP_MCHG_MASK | DISP2_GAP_MCHG_MASK); + tmp |= (DISP1_GAP_MCHG(R600_PM_DISPLAY_GAP_VBLANK) | + DISP2_GAP_MCHG(R600_PM_DISPLAY_GAP_IGNORE)); + WREG32(CG_DISPLAY_GAP_CNTL, tmp); +} + +static void cypress_program_display_gap(struct radeon_device *rdev) +{ + u32 tmp, pipe; + int i; + + tmp = RREG32(CG_DISPLAY_GAP_CNTL) & ~(DISP1_GAP_MASK | DISP2_GAP_MASK); + if (rdev->pm.dpm.new_active_crtc_count > 0) + tmp |= DISP1_GAP(R600_PM_DISPLAY_GAP_VBLANK_OR_WM); + else + tmp |= DISP1_GAP(R600_PM_DISPLAY_GAP_IGNORE); + + if (rdev->pm.dpm.new_active_crtc_count > 1) + tmp |= DISP2_GAP(R600_PM_DISPLAY_GAP_VBLANK_OR_WM); + else + tmp |= DISP2_GAP(R600_PM_DISPLAY_GAP_IGNORE); + + WREG32(CG_DISPLAY_GAP_CNTL, tmp); + + tmp = RREG32(DCCG_DISP_SLOW_SELECT_REG); + pipe = (tmp & DCCG_DISP1_SLOW_SELECT_MASK) >> DCCG_DISP1_SLOW_SELECT_SHIFT; + + if ((rdev->pm.dpm.new_active_crtc_count > 0) && + (!(rdev->pm.dpm.new_active_crtcs & (1 << pipe)))) { + /* find the first active crtc */ + for (i = 0; i < rdev->num_crtc; i++) { + if (rdev->pm.dpm.new_active_crtcs & (1 << i)) + break; + } + if (i == rdev->num_crtc) + pipe = 0; + else + pipe = i; + + tmp &= ~DCCG_DISP1_SLOW_SELECT_MASK; + tmp |= DCCG_DISP1_SLOW_SELECT(pipe); + WREG32(DCCG_DISP_SLOW_SELECT_REG, tmp); + } + + cypress_notify_smc_display_change(rdev, rdev->pm.dpm.new_active_crtc_count > 0); +} + +void cypress_dpm_setup_asic(struct radeon_device *rdev) +{ + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + + rv740_read_clock_registers(rdev); + rv770_read_voltage_smio_registers(rdev); + rv770_get_max_vddc(rdev); + rv770_get_memory_type(rdev); + + if (eg_pi->pcie_performance_request) + eg_pi->pcie_performance_request_registered = false; + + if (eg_pi->pcie_performance_request) + cypress_advertise_gen2_capability(rdev); + + rv770_get_pcie_gen2_status(rdev); + + rv770_enable_acpi_pm(rdev); +} + +int cypress_dpm_enable(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + + if (pi->gfx_clock_gating) + rv770_restore_cgcg(rdev); + + if (rv770_dpm_enabled(rdev)) + return -EINVAL; + + if (pi->voltage_control) { + rv770_enable_voltage_control(rdev, true); + cypress_construct_voltage_tables(rdev); + } + + if (pi->mvdd_control) + cypress_get_mvdd_configuration(rdev); + + if (eg_pi->dynamic_ac_timing) { + cypress_set_mc_reg_address_table(rdev); + cypress_force_mc_use_s0(rdev); + cypress_initialize_mc_reg_table(rdev); + cypress_force_mc_use_s1(rdev); + } + + if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_BACKBIAS) + rv770_enable_backbias(rdev, true); + + if (pi->dynamic_ss) + cypress_enable_spread_spectrum(rdev, true); + + if (pi->thermal_protection) + rv770_enable_thermal_protection(rdev, true); + + rv770_setup_bsp(rdev); + rv770_program_git(rdev); + rv770_program_tp(rdev); + rv770_program_tpp(rdev); + rv770_program_sstp(rdev); + rv770_program_engine_speed_parameters(rdev); + cypress_enable_display_gap(rdev); + rv770_program_vc(rdev); + + if (pi->dynamic_pcie_gen2) + cypress_enable_dynamic_pcie_gen2(rdev, true); + + if (rv770_upload_firmware(rdev)) + return -EINVAL; + + cypress_get_table_locations(rdev); + + if (cypress_init_smc_table(rdev)) + return -EINVAL; + + if (eg_pi->dynamic_ac_timing) + cypress_populate_mc_reg_table(rdev); + + cypress_program_response_times(rdev); + + r7xx_start_smc(rdev); + + cypress_notify_smc_display_change(rdev, false); + + cypress_enable_sclk_control(rdev, true); + + if (eg_pi->memory_transition) + cypress_enable_mclk_control(rdev, true); + + cypress_start_dpm(rdev); + + if (pi->gfx_clock_gating) + cypress_gfx_clock_gating_enable(rdev, true); + + if (pi->mg_clock_gating) + cypress_mg_clock_gating_enable(rdev, true); + + if (rdev->irq.installed && + r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) { + PPSMC_Result result; + + rv770_set_thermal_temperature_range(rdev, R600_TEMP_RANGE_MIN, R600_TEMP_RANGE_MAX); + rdev->irq.dpm_thermal = true; + radeon_irq_set(rdev); + result = rv770_send_msg_to_smc(rdev, PPSMC_MSG_EnableThermalInterrupt); + + if (result != PPSMC_Result_OK) + DRM_DEBUG_KMS("Could not enable thermal interrupts.\n"); + } + + rv770_enable_auto_throttle_source(rdev, RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL, true); + + return 0; +} + +void cypress_dpm_disable(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + + if (!rv770_dpm_enabled(rdev)) + return; + + rv770_clear_vc(rdev); + + if (pi->thermal_protection) + rv770_enable_thermal_protection(rdev, false); + + if (pi->dynamic_pcie_gen2) + cypress_enable_dynamic_pcie_gen2(rdev, false); + + if (rdev->irq.installed && + r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) { + rdev->irq.dpm_thermal = false; + radeon_irq_set(rdev); + } + + if (pi->gfx_clock_gating) + cypress_gfx_clock_gating_enable(rdev, false); + + if (pi->mg_clock_gating) + cypress_mg_clock_gating_enable(rdev, false); + + rv770_stop_dpm(rdev); + r7xx_stop_smc(rdev); + + cypress_enable_spread_spectrum(rdev, false); + + if (eg_pi->dynamic_ac_timing) + cypress_force_mc_use_s1(rdev); + + rv770_reset_smio_status(rdev); +} + +int cypress_dpm_set_power_state(struct radeon_device *rdev) +{ + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + + rv770_restrict_performance_levels_before_switch(rdev); + + if (eg_pi->pcie_performance_request) + cypress_notify_link_speed_change_before_state_change(rdev); + + rv770_halt_smc(rdev); + cypress_upload_sw_state(rdev); + + if (eg_pi->dynamic_ac_timing) + cypress_upload_mc_reg_table(rdev); + + cypress_program_memory_timing_parameters(rdev); + + rv770_resume_smc(rdev); + rv770_set_sw_state(rdev); + + if (eg_pi->pcie_performance_request) + cypress_notify_link_speed_change_after_state_change(rdev); + + return 0; +} + +void cypress_dpm_reset_asic(struct radeon_device *rdev) +{ + rv770_restrict_performance_levels_before_switch(rdev); + rv770_set_boot_state(rdev); +} + +void cypress_dpm_display_configuration_changed(struct radeon_device *rdev) +{ + cypress_program_display_gap(rdev); +} + +int cypress_dpm_init(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi; + struct evergreen_power_info *eg_pi; + int index = GetIndexIntoMasterTable(DATA, ASIC_InternalSS_Info); + uint16_t data_offset, size; + uint8_t frev, crev; + struct atom_clock_dividers dividers; + int ret; + + eg_pi = kzalloc(sizeof(struct evergreen_power_info), GFP_KERNEL); + if (eg_pi == NULL) + return -ENOMEM; + rdev->pm.dpm.priv = eg_pi; + pi = &eg_pi->rv7xx; + + rv770_get_max_vddc(rdev); + + eg_pi->ulv.supported = false; + pi->acpi_vddc = 0; + eg_pi->acpi_vddci = 0; + pi->min_vddc_in_table = 0; + pi->max_vddc_in_table = 0; + + ret = rv7xx_parse_power_table(rdev); + if (ret) + return ret; + + if (rdev->pm.dpm.voltage_response_time == 0) + rdev->pm.dpm.voltage_response_time = R600_VOLTAGERESPONSETIME_DFLT; + if (rdev->pm.dpm.backbias_response_time == 0) + rdev->pm.dpm.backbias_response_time = R600_BACKBIASRESPONSETIME_DFLT; + + ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM, + 0, false, ÷rs); + if (ret) + pi->ref_div = dividers.ref_div + 1; + else + pi->ref_div = R600_REFERENCEDIVIDER_DFLT; + + pi->mclk_strobe_mode_threshold = 40000; + pi->mclk_edc_enable_threshold = 40000; + eg_pi->mclk_edc_wr_enable_threshold = 40000; + + pi->voltage_control = + radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC); + + pi->mvdd_control = + radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_MVDDC); + + eg_pi->vddci_control = + radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDCI); + + if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size, + &frev, &crev, &data_offset)) { + pi->sclk_ss = true; + pi->mclk_ss = true; + pi->dynamic_ss = true; + } else { + pi->sclk_ss = false; + pi->mclk_ss = false; + pi->dynamic_ss = true; + } + + pi->asi = RV770_ASI_DFLT; + pi->pasi = CYPRESS_HASI_DFLT; + pi->vrc = CYPRESS_VRC_DFLT; + + pi->power_gating = false; + + if ((rdev->family == CHIP_CYPRESS) || + (rdev->family == CHIP_HEMLOCK)) + pi->gfx_clock_gating = false; + else + pi->gfx_clock_gating = true; + + pi->mg_clock_gating = true; + pi->mgcgtssm = true; + eg_pi->ls_clock_gating = false; + eg_pi->sclk_deep_sleep = false; + + pi->dynamic_pcie_gen2 = true; + + if (pi->gfx_clock_gating && + (rdev->pm.int_thermal_type != THERMAL_TYPE_NONE)) + pi->thermal_protection = true; + else + pi->thermal_protection = false; + + pi->display_gap = true; + + if (rdev->flags & RADEON_IS_MOBILITY) + pi->dcodt = true; + else + pi->dcodt = false; + + pi->ulps = true; + + eg_pi->dynamic_ac_timing = true; + eg_pi->abm = true; + eg_pi->mcls = true; + eg_pi->light_sleep = true; + eg_pi->memory_transition = true; +#if defined(CONFIG_ACPI) + eg_pi->pcie_performance_request = + radeon_acpi_is_pcie_performance_request_supported(rdev); +#else + eg_pi->pcie_performance_request = false; +#endif + + if ((rdev->family == CHIP_CYPRESS) || + (rdev->family == CHIP_HEMLOCK) || + (rdev->family == CHIP_JUNIPER)) + eg_pi->dll_default_on = true; + else + eg_pi->dll_default_on = false; + + eg_pi->sclk_deep_sleep = false; + pi->mclk_stutter_mode_threshold = 0; + + pi->sram_end = SMC_RAM_END; + + return 0; +} + +void cypress_dpm_fini(struct radeon_device *rdev) +{ + int i; + + for (i = 0; i < rdev->pm.dpm.num_ps; i++) { + kfree(rdev->pm.dpm.ps[i].ps_priv); + } + kfree(rdev->pm.dpm.ps); + kfree(rdev->pm.dpm.priv); +} diff --git a/drivers/gpu/drm/radeon/cypress_dpm.h b/drivers/gpu/drm/radeon/cypress_dpm.h new file mode 100644 index 0000000..6cc3d3f --- /dev/null +++ b/drivers/gpu/drm/radeon/cypress_dpm.h @@ -0,0 +1,134 @@ +/* + * Copyright 2011 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ +#ifndef __CYPRESS_DPM_H__ +#define __CYPRESS_DPM_H__ + +#include "rv770_dpm.h" +#include "evergreen_smc.h" + +struct evergreen_mc_reg_entry { + u32 mclk_max; + u32 mc_data[SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE]; +}; + +struct evergreen_mc_reg_table { + u8 last; + u8 num_entries; + u16 valid_flag; + struct evergreen_mc_reg_entry mc_reg_table_entry[MAX_AC_TIMING_ENTRIES]; + SMC_Evergreen_MCRegisterAddress mc_reg_address[SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE]; +}; + +struct evergreen_ulv_param { + bool supported; + struct rv7xx_pl *pl; +}; + +struct evergreen_arb_registers { + u32 mc_arb_dram_timing; + u32 mc_arb_dram_timing2; + u32 mc_arb_rfsh_rate; + u32 mc_arb_burst_time; +}; + +struct evergreen_power_info { + /* must be first! */ + struct rv7xx_power_info rv7xx; + /* flags */ + bool vddci_control; + bool dynamic_ac_timing; + bool abm; + bool mcls; + bool light_sleep; + bool memory_transition; + bool pcie_performance_request; + bool pcie_performance_request_registered; + bool sclk_deep_sleep; + bool dll_default_on; + bool ls_clock_gating; + /* stored values */ + u16 acpi_vddci; + u8 mvdd_high_index; + u8 mvdd_low_index; + u32 mclk_edc_wr_enable_threshold; + struct evergreen_mc_reg_table mc_reg_table; + struct atom_voltage_table vddc_voltage_table; + struct atom_voltage_table vddci_voltage_table; + struct evergreen_arb_registers bootup_arb_registers; + struct evergreen_ulv_param ulv; + /* smc offsets */ + u16 mc_reg_table_start; +}; + +#define CYPRESS_HASI_DFLT 400000 +#define CYPRESS_MGCGTTLOCAL0_DFLT 0x00000000 +#define CYPRESS_MGCGTTLOCAL1_DFLT 0x00000000 +#define CYPRESS_MGCGTTLOCAL2_DFLT 0x00000000 +#define CYPRESS_MGCGTTLOCAL3_DFLT 0x00000000 +#define CYPRESS_MGCGCGTSSMCTRL_DFLT 0x81944bc0 +#define REDWOOD_MGCGCGTSSMCTRL_DFLT 0x6e944040 +#define CEDAR_MGCGCGTSSMCTRL_DFLT 0x46944040 +#define CYPRESS_VRC_DFLT 0xC00033 + +#define PCIE_PERF_REQ_REMOVE_REGISTRY 0 +#define PCIE_PERF_REQ_FORCE_LOWPOWER 1 +#define PCIE_PERF_REQ_PECI_GEN1 2 +#define PCIE_PERF_REQ_PECI_GEN2 3 +#define PCIE_PERF_REQ_PECI_GEN3 4 + +int cypress_convert_power_level_to_smc(struct radeon_device *rdev, + struct rv7xx_pl *pl, + RV770_SMC_HW_PERFORMANCE_LEVEL *level, + u8 watermark_level); +int cypress_populate_smc_acpi_state(struct radeon_device *rdev, + RV770_SMC_STATETABLE *table); +int cypress_populate_smc_voltage_tables(struct radeon_device *rdev, + RV770_SMC_STATETABLE *table); +int cypress_populate_smc_initial_state(struct radeon_device *rdev, + struct radeon_ps *radeon_initial_state, + RV770_SMC_STATETABLE *table); +u32 cypress_calculate_burst_time(struct radeon_device *rdev, + u32 engine_clock, u32 memory_clock); +void cypress_notify_link_speed_change_before_state_change(struct radeon_device *rdev); +int cypress_upload_sw_state(struct radeon_device *rdev); +int cypress_upload_mc_reg_table(struct radeon_device *rdev); +void cypress_program_memory_timing_parameters(struct radeon_device *rdev); +void cypress_notify_link_speed_change_after_state_change(struct radeon_device *rdev); +int cypress_construct_voltage_tables(struct radeon_device *rdev); +int cypress_get_mvdd_configuration(struct radeon_device *rdev); +void cypress_enable_spread_spectrum(struct radeon_device *rdev, + bool enable); +void cypress_enable_display_gap(struct radeon_device *rdev); +int cypress_get_table_locations(struct radeon_device *rdev); +int cypress_populate_mc_reg_table(struct radeon_device *rdev); +void cypress_program_response_times(struct radeon_device *rdev); +int cypress_notify_smc_display_change(struct radeon_device *rdev, + bool has_display); +void cypress_enable_sclk_control(struct radeon_device *rdev, + bool enable); +void cypress_enable_mclk_control(struct radeon_device *rdev, + bool enable); +void cypress_start_dpm(struct radeon_device *rdev); +void cypress_advertise_gen2_capability(struct radeon_device *rdev); + +#endif diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 63a1e6e..c91b1e5 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -4167,6 +4167,7 @@ int evergreen_irq_set(struct radeon_device *rdev) u32 grph1 = 0, grph2 = 0, grph3 = 0, grph4 = 0, grph5 = 0, grph6 = 0; u32 afmt1 = 0, afmt2 = 0, afmt3 = 0, afmt4 = 0, afmt5 = 0, afmt6 = 0; u32 dma_cntl, dma_cntl1 = 0; + u32 thermal_int = 0;
if (!rdev->irq.installed) { WARN(1, "Can't enable IRQ/MSI because no handler is installed\n"); @@ -4186,6 +4187,8 @@ int evergreen_irq_set(struct radeon_device *rdev) hpd4 = RREG32(DC_HPD4_INT_CONTROL) & ~DC_HPDx_INT_EN; hpd5 = RREG32(DC_HPD5_INT_CONTROL) & ~DC_HPDx_INT_EN; hpd6 = RREG32(DC_HPD6_INT_CONTROL) & ~DC_HPDx_INT_EN; + thermal_int = RREG32(CG_THERMAL_INT) & + ~(THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW);
afmt1 = RREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET) & ~AFMT_AZ_FORMAT_WTRIG_MASK; afmt2 = RREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET) & ~AFMT_AZ_FORMAT_WTRIG_MASK; @@ -4231,6 +4234,11 @@ int evergreen_irq_set(struct radeon_device *rdev) } }
+ if (rdev->irq.dpm_thermal) { + DRM_DEBUG("dpm thermal\n"); + thermal_int |= THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW; + } + if (rdev->irq.crtc_vblank_int[0] || atomic_read(&rdev->irq.pflip[0])) { DRM_DEBUG("evergreen_irq_set: vblank 0\n"); @@ -4352,6 +4360,7 @@ int evergreen_irq_set(struct radeon_device *rdev) WREG32(DC_HPD4_INT_CONTROL, hpd4); WREG32(DC_HPD5_INT_CONTROL, hpd5); WREG32(DC_HPD6_INT_CONTROL, hpd6); + WREG32(CG_THERMAL_INT, thermal_int);
WREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, afmt1); WREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, afmt2); @@ -4543,6 +4552,7 @@ int evergreen_irq_process(struct radeon_device *rdev) u32 ring_index; bool queue_hotplug = false; bool queue_hdmi = false; + bool queue_thermal = false;
if (!rdev->ih.enabled || rdev->shutdown) return IRQ_NONE; @@ -4864,6 +4874,16 @@ restart_ih: DRM_DEBUG("IH: DMA trap\n"); radeon_fence_process(rdev, R600_RING_TYPE_DMA_INDEX); break; + case 230: /* thermal low to high */ + DRM_DEBUG("IH: thermal low to high\n"); + rdev->pm.dpm.thermal.high_to_low = false; + queue_thermal = true; + break; + case 231: /* thermal high to low */ + DRM_DEBUG("IH: thermal high to low\n"); + rdev->pm.dpm.thermal.high_to_low = true; + queue_thermal = true; + break; case 233: /* GUI IDLE */ DRM_DEBUG("IH: GUI idle\n"); break; @@ -4886,6 +4906,8 @@ restart_ih: schedule_work(&rdev->hotplug_work); if (queue_hdmi) schedule_work(&rdev->audio_work); + if (queue_thermal) + schedule_work(&rdev->pm.dpm.thermal.work); rdev->ih.rptr = rptr; WREG32(IH_RB_RPTR, rdev->ih.rptr); atomic_set(&rdev->ih.lock, 0); diff --git a/drivers/gpu/drm/radeon/evergreen_smc.h b/drivers/gpu/drm/radeon/evergreen_smc.h new file mode 100644 index 0000000..76ada8c --- /dev/null +++ b/drivers/gpu/drm/radeon/evergreen_smc.h @@ -0,0 +1,67 @@ +/* + * Copyright 2011 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ +#ifndef __EVERGREEN_SMC_H__ +#define __EVERGREEN_SMC_H__ + +#include "rv770_smc.h" + +#pragma pack(push, 1) + +#define SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE 16 + +struct SMC_Evergreen_MCRegisterAddress +{ + uint16_t s0; + uint16_t s1; +}; + +typedef struct SMC_Evergreen_MCRegisterAddress SMC_Evergreen_MCRegisterAddress; + + +struct SMC_Evergreen_MCRegisterSet +{ + uint32_t value[SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE]; +}; + +typedef struct SMC_Evergreen_MCRegisterSet SMC_Evergreen_MCRegisterSet; + +struct SMC_Evergreen_MCRegisters +{ + uint8_t last; + uint8_t reserved[3]; + SMC_Evergreen_MCRegisterAddress address[SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE]; + SMC_Evergreen_MCRegisterSet data[5]; +}; + +typedef struct SMC_Evergreen_MCRegisters SMC_Evergreen_MCRegisters; + +#define EVERGREEN_SMC_FIRMWARE_HEADER_LOCATION 0x100 + +#define EVERGREEN_SMC_FIRMWARE_HEADER_softRegisters 0x0 +#define EVERGREEN_SMC_FIRMWARE_HEADER_stateTable 0xC +#define EVERGREEN_SMC_FIRMWARE_HEADER_mcRegisterTable 0x20 + + +#pragma pack(pop) + +#endif diff --git a/drivers/gpu/drm/radeon/evergreend.h b/drivers/gpu/drm/radeon/evergreend.h index 8603b7c..93b91a3 100644 --- a/drivers/gpu/drm/radeon/evergreend.h +++ b/drivers/gpu/drm/radeon/evergreend.h @@ -48,6 +48,293 @@ #define SUMO_GB_ADDR_CONFIG_GOLDEN 0x02010002 #define SUMO2_GB_ADDR_CONFIG_GOLDEN 0x02010002
+/* pm registers */ +#define SMC_MSG 0x20c +#define HOST_SMC_MSG(x) ((x) << 0) +#define HOST_SMC_MSG_MASK (0xff << 0) +#define HOST_SMC_MSG_SHIFT 0 +#define HOST_SMC_RESP(x) ((x) << 8) +#define HOST_SMC_RESP_MASK (0xff << 8) +#define HOST_SMC_RESP_SHIFT 8 +#define SMC_HOST_MSG(x) ((x) << 16) +#define SMC_HOST_MSG_MASK (0xff << 16) +#define SMC_HOST_MSG_SHIFT 16 +#define SMC_HOST_RESP(x) ((x) << 24) +#define SMC_HOST_RESP_MASK (0xff << 24) +#define SMC_HOST_RESP_SHIFT 24 + +#define DCCG_DISP_SLOW_SELECT_REG 0x4fc +#define DCCG_DISP1_SLOW_SELECT(x) ((x) << 0) +#define DCCG_DISP1_SLOW_SELECT_MASK (7 << 0) +#define DCCG_DISP1_SLOW_SELECT_SHIFT 0 +#define DCCG_DISP2_SLOW_SELECT(x) ((x) << 4) +#define DCCG_DISP2_SLOW_SELECT_MASK (7 << 4) +#define DCCG_DISP2_SLOW_SELECT_SHIFT 4 + +#define CG_SPLL_FUNC_CNTL 0x600 +#define SPLL_RESET (1 << 0) +#define SPLL_SLEEP (1 << 1) +#define SPLL_BYPASS_EN (1 << 3) +#define SPLL_REF_DIV(x) ((x) << 4) +#define SPLL_REF_DIV_MASK (0x3f << 4) +#define SPLL_PDIV_A(x) ((x) << 20) +#define SPLL_PDIV_A_MASK (0x7f << 20) +#define CG_SPLL_FUNC_CNTL_2 0x604 +#define SCLK_MUX_SEL(x) ((x) << 0) +#define SCLK_MUX_SEL_MASK (0x1ff << 0) +#define CG_SPLL_FUNC_CNTL_3 0x608 +#define SPLL_FB_DIV(x) ((x) << 0) +#define SPLL_FB_DIV_MASK (0x3ffffff << 0) +#define SPLL_DITHEN (1 << 28) + +#define MPLL_CNTL_MODE 0x61c +# define SS_SSEN (1 << 24) +# define SS_DSMODE_EN (1 << 25) + +#define MPLL_AD_FUNC_CNTL 0x624 +#define CLKF(x) ((x) << 0) +#define CLKF_MASK (0x7f << 0) +#define CLKR(x) ((x) << 7) +#define CLKR_MASK (0x1f << 7) +#define CLKFRAC(x) ((x) << 12) +#define CLKFRAC_MASK (0x1f << 12) +#define YCLK_POST_DIV(x) ((x) << 17) +#define YCLK_POST_DIV_MASK (3 << 17) +#define IBIAS(x) ((x) << 20) +#define IBIAS_MASK (0x3ff << 20) +#define RESET (1 << 30) +#define PDNB (1 << 31) +#define MPLL_AD_FUNC_CNTL_2 0x628 +#define BYPASS (1 << 19) +#define BIAS_GEN_PDNB (1 << 24) +#define RESET_EN (1 << 25) +#define VCO_MODE (1 << 29) +#define MPLL_DQ_FUNC_CNTL 0x62c +#define MPLL_DQ_FUNC_CNTL_2 0x630 + +#define GENERAL_PWRMGT 0x63c +# define GLOBAL_PWRMGT_EN (1 << 0) +# define STATIC_PM_EN (1 << 1) +# define THERMAL_PROTECTION_DIS (1 << 2) +# define THERMAL_PROTECTION_TYPE (1 << 3) +# define ENABLE_GEN2PCIE (1 << 4) +# define ENABLE_GEN2XSP (1 << 5) +# define SW_SMIO_INDEX(x) ((x) << 6) +# define SW_SMIO_INDEX_MASK (3 << 6) +# define SW_SMIO_INDEX_SHIFT 6 +# define LOW_VOLT_D2_ACPI (1 << 8) +# define LOW_VOLT_D3_ACPI (1 << 9) +# define VOLT_PWRMGT_EN (1 << 10) +# define BACKBIAS_PAD_EN (1 << 18) +# define BACKBIAS_VALUE (1 << 19) +# define DYN_SPREAD_SPECTRUM_EN (1 << 23) +# define AC_DC_SW (1 << 24) + +#define SCLK_PWRMGT_CNTL 0x644 +# define SCLK_PWRMGT_OFF (1 << 0) +# define SCLK_LOW_D1 (1 << 1) +# define FIR_RESET (1 << 4) +# define FIR_FORCE_TREND_SEL (1 << 5) +# define FIR_TREND_MODE (1 << 6) +# define DYN_GFX_CLK_OFF_EN (1 << 7) +# define GFX_CLK_FORCE_ON (1 << 8) +# define GFX_CLK_REQUEST_OFF (1 << 9) +# define GFX_CLK_FORCE_OFF (1 << 10) +# define GFX_CLK_OFF_ACPI_D1 (1 << 11) +# define GFX_CLK_OFF_ACPI_D2 (1 << 12) +# define GFX_CLK_OFF_ACPI_D3 (1 << 13) +# define DYN_LIGHT_SLEEP_EN (1 << 14) +#define MCLK_PWRMGT_CNTL 0x648 +# define DLL_SPEED(x) ((x) << 0) +# define DLL_SPEED_MASK (0x1f << 0) +# define MPLL_PWRMGT_OFF (1 << 5) +# define DLL_READY (1 << 6) +# define MC_INT_CNTL (1 << 7) +# define MRDCKA0_PDNB (1 << 8) +# define MRDCKA1_PDNB (1 << 9) +# define MRDCKB0_PDNB (1 << 10) +# define MRDCKB1_PDNB (1 << 11) +# define MRDCKC0_PDNB (1 << 12) +# define MRDCKC1_PDNB (1 << 13) +# define MRDCKD0_PDNB (1 << 14) +# define MRDCKD1_PDNB (1 << 15) +# define MRDCKA0_RESET (1 << 16) +# define MRDCKA1_RESET (1 << 17) +# define MRDCKB0_RESET (1 << 18) +# define MRDCKB1_RESET (1 << 19) +# define MRDCKC0_RESET (1 << 20) +# define MRDCKC1_RESET (1 << 21) +# define MRDCKD0_RESET (1 << 22) +# define MRDCKD1_RESET (1 << 23) +# define DLL_READY_READ (1 << 24) +# define USE_DISPLAY_GAP (1 << 25) +# define USE_DISPLAY_URGENT_NORMAL (1 << 26) +# define MPLL_TURNOFF_D2 (1 << 28) +#define DLL_CNTL 0x64c +# define MRDCKA0_BYPASS (1 << 24) +# define MRDCKA1_BYPASS (1 << 25) +# define MRDCKB0_BYPASS (1 << 26) +# define MRDCKB1_BYPASS (1 << 27) +# define MRDCKC0_BYPASS (1 << 28) +# define MRDCKC1_BYPASS (1 << 29) +# define MRDCKD0_BYPASS (1 << 30) +# define MRDCKD1_BYPASS (1 << 31) + +#define CG_AT 0x6d4 +# define CG_R(x) ((x) << 0) +# define CG_R_MASK (0xffff << 0) +# define CG_L(x) ((x) << 16) +# define CG_L_MASK (0xffff << 16) + +#define CG_DISPLAY_GAP_CNTL 0x714 +# define DISP1_GAP(x) ((x) << 0) +# define DISP1_GAP_MASK (3 << 0) +# define DISP2_GAP(x) ((x) << 2) +# define DISP2_GAP_MASK (3 << 2) +# define VBI_TIMER_COUNT(x) ((x) << 4) +# define VBI_TIMER_COUNT_MASK (0x3fff << 4) +# define VBI_TIMER_UNIT(x) ((x) << 20) +# define VBI_TIMER_UNIT_MASK (7 << 20) +# define DISP1_GAP_MCHG(x) ((x) << 24) +# define DISP1_GAP_MCHG_MASK (3 << 24) +# define DISP2_GAP_MCHG(x) ((x) << 26) +# define DISP2_GAP_MCHG_MASK (3 << 26) + +#define CG_BIF_REQ_AND_RSP 0x7f4 +#define CG_CLIENT_REQ(x) ((x) << 0) +#define CG_CLIENT_REQ_MASK (0xff << 0) +#define CG_CLIENT_REQ_SHIFT 0 +#define CG_CLIENT_RESP(x) ((x) << 8) +#define CG_CLIENT_RESP_MASK (0xff << 8) +#define CG_CLIENT_RESP_SHIFT 8 +#define CLIENT_CG_REQ(x) ((x) << 16) +#define CLIENT_CG_REQ_MASK (0xff << 16) +#define CLIENT_CG_REQ_SHIFT 16 +#define CLIENT_CG_RESP(x) ((x) << 24) +#define CLIENT_CG_RESP_MASK (0xff << 24) +#define CLIENT_CG_RESP_SHIFT 24 + +#define CG_SPLL_SPREAD_SPECTRUM 0x790 +#define SSEN (1 << 0) +#define CG_SPLL_SPREAD_SPECTRUM_2 0x794 + +#define MPLL_SS1 0x85c +#define CLKV(x) ((x) << 0) +#define CLKV_MASK (0x3ffffff << 0) +#define MPLL_SS2 0x860 +#define CLKS(x) ((x) << 0) +#define CLKS_MASK (0xfff << 0) + +#define CG_IND_ADDR 0x8f8 +#define CG_IND_DATA 0x8fc +/* CGIND regs */ +#define CG_CGTT_LOCAL_0 0x00 +#define CG_CGTT_LOCAL_1 0x01 +#define CG_CGTT_LOCAL_2 0x02 +#define CG_CGTT_LOCAL_3 0x03 +#define CG_CGLS_TILE_0 0x20 +#define CG_CGLS_TILE_1 0x21 +#define CG_CGLS_TILE_2 0x22 +#define CG_CGLS_TILE_3 0x23 +#define CG_CGLS_TILE_4 0x24 +#define CG_CGLS_TILE_5 0x25 +#define CG_CGLS_TILE_6 0x26 +#define CG_CGLS_TILE_7 0x27 +#define CG_CGLS_TILE_8 0x28 +#define CG_CGLS_TILE_9 0x29 +#define CG_CGLS_TILE_10 0x2a +#define CG_CGLS_TILE_11 0x2b + +#define VM_L2_CG 0x15c0 + +#define MC_CONFIG 0x2000 + +#define MC_CONFIG_MCD 0x20a0 +#define MC_CG_CONFIG_MCD 0x20a4 +#define MC_RD_ENABLE_MCD(x) ((x) << 8) +#define MC_RD_ENABLE_MCD_MASK (7 << 8) + +#define MC_HUB_MISC_HUB_CG 0x20b8 +#define MC_HUB_MISC_VM_CG 0x20bc +#define MC_HUB_MISC_SIP_CG 0x20c0 + +#define MC_XPB_CLK_GAT 0x2478 + +#define MC_CG_CONFIG 0x25bc +#define MC_RD_ENABLE(x) ((x) << 4) +#define MC_RD_ENABLE_MASK (3 << 4) + +#define MC_CITF_MISC_RD_CG 0x2648 +#define MC_CITF_MISC_WR_CG 0x264c +#define MC_CITF_MISC_VM_CG 0x2650 +# define MEM_LS_ENABLE (1 << 19) + +#define MC_ARB_BURST_TIME 0x2808 +#define STATE0(x) ((x) << 0) +#define STATE0_MASK (0x1f << 0) +#define STATE1(x) ((x) << 5) +#define STATE1_MASK (0x1f << 5) +#define STATE2(x) ((x) << 10) +#define STATE2_MASK (0x1f << 10) +#define STATE3(x) ((x) << 15) +#define STATE3_MASK (0x1f << 15) + +#define MC_SEQ_RAS_TIMING 0x28a0 +#define MC_SEQ_CAS_TIMING 0x28a4 +#define MC_SEQ_MISC_TIMING 0x28a8 +#define MC_SEQ_MISC_TIMING2 0x28ac + +#define MC_SEQ_RD_CTL_D0 0x28b4 +#define MC_SEQ_RD_CTL_D1 0x28b8 +#define MC_SEQ_WR_CTL_D0 0x28bc +#define MC_SEQ_WR_CTL_D1 0x28c0 + +#define MC_SEQ_STATUS_M 0x29f4 +# define PMG_PWRSTATE (1 << 16) + +#define MC_SEQ_MISC1 0x2a04 +#define MC_SEQ_RESERVE_M 0x2a08 +#define MC_PMG_CMD_EMRS 0x2a0c + +#define MC_SEQ_MISC3 0x2a2c + +#define MC_SEQ_MISC5 0x2a54 +#define MC_SEQ_MISC6 0x2a58 + +#define MC_SEQ_MISC7 0x2a64 + +#define MC_SEQ_CG 0x2a68 +#define CG_SEQ_REQ(x) ((x) << 0) +#define CG_SEQ_REQ_MASK (0xff << 0) +#define CG_SEQ_REQ_SHIFT 0 +#define CG_SEQ_RESP(x) ((x) << 8) +#define CG_SEQ_RESP_MASK (0xff << 8) +#define CG_SEQ_RESP_SHIFT 8 +#define SEQ_CG_REQ(x) ((x) << 16) +#define SEQ_CG_REQ_MASK (0xff << 16) +#define SEQ_CG_REQ_SHIFT 16 +#define SEQ_CG_RESP(x) ((x) << 24) +#define SEQ_CG_RESP_MASK (0xff << 24) +#define SEQ_CG_RESP_SHIFT 24 +#define MC_SEQ_RAS_TIMING_LP 0x2a6c +#define MC_SEQ_CAS_TIMING_LP 0x2a70 +#define MC_SEQ_MISC_TIMING_LP 0x2a74 +#define MC_SEQ_MISC_TIMING2_LP 0x2a78 +#define MC_SEQ_WR_CTL_D0_LP 0x2a7c +#define MC_SEQ_WR_CTL_D1_LP 0x2a80 +#define MC_SEQ_PMG_CMD_EMRS_LP 0x2a84 +#define MC_SEQ_PMG_CMD_MRS_LP 0x2a88 + +#define MC_PMG_CMD_MRS 0x2aac + +#define MC_SEQ_RD_CTL_D0_LP 0x2b1c +#define MC_SEQ_RD_CTL_D1_LP 0x2b20 + +#define MC_PMG_CMD_MRS1 0x2b44 +#define MC_SEQ_PMG_CMD_MRS1_LP 0x2b48 + +#define CGTS_SM_CTRL_REG 0x9150 + /* Registers */
#define RCU_IND_INDEX 0x100 @@ -522,6 +809,20 @@ #define CG_THERMAL_CTRL 0x72c #define TOFFSET_MASK 0x00003FE0 #define TOFFSET_SHIFT 5 +#define DIG_THERM_DPM(x) ((x) << 14) +#define DIG_THERM_DPM_MASK 0x003FC000 +#define DIG_THERM_DPM_SHIFT 14 + +#define CG_THERMAL_INT 0x734 +#define DIG_THERM_INTH(x) ((x) << 8) +#define DIG_THERM_INTH_MASK 0x0000FF00 +#define DIG_THERM_INTH_SHIFT 8 +#define DIG_THERM_INTL(x) ((x) << 16) +#define DIG_THERM_INTL_MASK 0x00FF0000 +#define DIG_THERM_INTL_SHIFT 16 +#define THERM_INT_MASK_HIGH (1 << 24) +#define THERM_INT_MASK_LOW (1 << 25) + #define CG_MULT_THERMAL_STATUS 0x740 #define ASIC_T(x) ((x) << 16) #define ASIC_T_MASK 0x07FF0000 @@ -529,6 +830,7 @@ #define CG_TS0_STATUS 0x760 #define TS0_ADC_DOUT_MASK 0x000003FF #define TS0_ADC_DOUT_SHIFT 0 + /* APU */ #define CG_THERMAL_STATUS 0x678
@@ -1039,6 +1341,9 @@ # define LC_SPEED_CHANGE_ATTEMPTS_ALLOWED_MASK (0x3 << 8) # define LC_SPEED_CHANGE_ATTEMPTS_ALLOWED_SHIFT 3 # define LC_CURRENT_DATA_RATE (1 << 11) +# define LC_HW_VOLTAGE_IF_CONTROL(x) ((x) << 12) +# define LC_HW_VOLTAGE_IF_CONTROL_MASK (3 << 12) +# define LC_HW_VOLTAGE_IF_CONTROL_SHIFT 12 # define LC_VOLTAGE_TIMER_SEL_MASK (0xf << 14) # define LC_CLR_FAILED_SPD_CHANGE_CNT (1 << 21) # define LC_OTHER_SIDE_EVER_SENT_GEN2 (1 << 23) diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index 0688016..dcac7fd 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -70,15 +70,19 @@ MODULE_FIRMWARE("radeon/R700_rlc.bin"); MODULE_FIRMWARE("radeon/CEDAR_pfp.bin"); MODULE_FIRMWARE("radeon/CEDAR_me.bin"); MODULE_FIRMWARE("radeon/CEDAR_rlc.bin"); +MODULE_FIRMWARE("radeon/CEDAR_smc.bin"); MODULE_FIRMWARE("radeon/REDWOOD_pfp.bin"); MODULE_FIRMWARE("radeon/REDWOOD_me.bin"); MODULE_FIRMWARE("radeon/REDWOOD_rlc.bin"); +MODULE_FIRMWARE("radeon/REDWOOD_smc.bin"); MODULE_FIRMWARE("radeon/JUNIPER_pfp.bin"); MODULE_FIRMWARE("radeon/JUNIPER_me.bin"); MODULE_FIRMWARE("radeon/JUNIPER_rlc.bin"); +MODULE_FIRMWARE("radeon/JUNIPER_smc.bin"); MODULE_FIRMWARE("radeon/CYPRESS_pfp.bin"); MODULE_FIRMWARE("radeon/CYPRESS_me.bin"); MODULE_FIRMWARE("radeon/CYPRESS_rlc.bin"); +MODULE_FIRMWARE("radeon/CYPRESS_smc.bin"); MODULE_FIRMWARE("radeon/PALM_pfp.bin"); MODULE_FIRMWARE("radeon/PALM_me.bin"); MODULE_FIRMWARE("radeon/SUMO_rlc.bin"); @@ -2214,19 +2218,27 @@ int r600_init_microcode(struct radeon_device *rdev) case CHIP_CEDAR: chip_name = "CEDAR"; rlc_chip_name = "CEDAR"; + smc_chip_name = "CEDAR"; + smc_req_size = ALIGN(CEDAR_SMC_UCODE_SIZE, 4); break; case CHIP_REDWOOD: chip_name = "REDWOOD"; rlc_chip_name = "REDWOOD"; + smc_chip_name = "REDWOOD"; + smc_req_size = ALIGN(REDWOOD_SMC_UCODE_SIZE, 4); break; case CHIP_JUNIPER: chip_name = "JUNIPER"; rlc_chip_name = "JUNIPER"; + smc_chip_name = "JUNIPER"; + smc_req_size = ALIGN(JUNIPER_SMC_UCODE_SIZE, 4); break; case CHIP_CYPRESS: case CHIP_HEMLOCK: chip_name = "CYPRESS"; rlc_chip_name = "CYPRESS"; + smc_chip_name = "CYPRESS"; + smc_req_size = ALIGN(CYPRESS_SMC_UCODE_SIZE, 4); break; case CHIP_PALM: chip_name = "PALM"; @@ -2293,7 +2305,7 @@ int r600_init_microcode(struct radeon_device *rdev) err = -EINVAL; }
- if ((rdev->family >= CHIP_RV770) && (rdev->family <= CHIP_RV740)) { + if ((rdev->family >= CHIP_RV770) && (rdev->family <= CHIP_HEMLOCK)) { snprintf(fw_name, sizeof(fw_name), "radeon/%s_smc.bin", smc_chip_name); err = request_firmware(&rdev->smc_fw, fw_name, &pdev->dev); if (err) diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 3e94920..037773d 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -2132,6 +2132,15 @@ void r100_pll_errata_after_index(struct radeon_device *rdev); #define ASIC_IS_NODCE(rdev) ((rdev->family == CHIP_HAINAN)) #define ASIC_IS_DCE8(rdev) ((rdev->family >= CHIP_BONAIRE))
+#define ASIC_IS_LOMBOK(rdev) ((rdev->ddev->pdev->device == 0x6849) || \ + (rdev->ddev->pdev->device == 0x6850) || \ + (rdev->ddev->pdev->device == 0x6858) || \ + (rdev->ddev->pdev->device == 0x6859) || \ + (rdev->ddev->pdev->device == 0x6840) || \ + (rdev->ddev->pdev->device == 0x6841) || \ + (rdev->ddev->pdev->device == 0x6842) || \ + (rdev->ddev->pdev->device == 0x6843)) + /* * BIOS helpers. */ @@ -2356,6 +2365,10 @@ extern int ni_mc_load_microcode(struct radeon_device *rdev); #if defined(CONFIG_ACPI) extern int radeon_acpi_init(struct radeon_device *rdev); extern void radeon_acpi_fini(struct radeon_device *rdev); +extern bool radeon_acpi_is_pcie_performance_request_supported(struct radeon_device *rdev); +extern int radeon_acpi_pcie_performance_request(struct radeon_device *rdev, + u8 ref_req, bool advertise); +extern int radeon_acpi_pcie_notify_device_ready(struct radeon_device *rdev); #else static inline int radeon_acpi_init(struct radeon_device *rdev) { return 0; } static inline void radeon_acpi_fini(struct radeon_device *rdev) { } diff --git a/drivers/gpu/drm/radeon/radeon_acpi.c b/drivers/gpu/drm/radeon/radeon_acpi.c index 196d28d..87419a4 100644 --- a/drivers/gpu/drm/radeon/radeon_acpi.c +++ b/drivers/gpu/drm/radeon/radeon_acpi.c @@ -78,6 +78,29 @@ struct atcs_verify_interface { u32 function_bits; /* supported functions bit vector */ } __packed;
+bool radeon_acpi_is_pcie_performance_request_supported(struct radeon_device *rdev) +{ + /* XXX: query ATIF */ + + return false; +} + +int radeon_acpi_pcie_notify_device_ready(struct radeon_device *rdev) +{ + /* XXX: call appropriate ATIF method */ + + return -EINVAL; + +} + +int radeon_acpi_pcie_performance_request(struct radeon_device *rdev, + u8 ref_req, bool advertise) +{ + /* XXX: call appropriate ATIF method */ + + return -EINVAL; +} + /* Call the ATIF method */ /** diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index dbbcf33..077448f 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1428,6 +1428,18 @@ static struct radeon_asic evergreen_asic = { .set_uvd_clocks = &evergreen_set_uvd_clocks, .get_temperature = &evergreen_get_temp, }, + .dpm = { + .init = &cypress_dpm_init, + .setup_asic = &cypress_dpm_setup_asic, + .enable = &cypress_dpm_enable, + .disable = &cypress_dpm_disable, + .set_power_state = &cypress_dpm_set_power_state, + .display_configuration_changed = &cypress_dpm_display_configuration_changed, + .fini = &cypress_dpm_fini, + .get_sclk = &rv770_dpm_get_sclk, + .get_mclk = &rv770_dpm_get_mclk, + .print_power_state = &rv770_dpm_print_power_state, + }, .pflip = { .pre_page_flip = &evergreen_pre_page_flip, .page_flip = &evergreen_page_flip, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index f7041a7..1f25b683 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -523,6 +523,13 @@ void evergreen_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode int evergreen_get_temp(struct radeon_device *rdev); int sumo_get_temp(struct radeon_device *rdev); int tn_get_temp(struct radeon_device *rdev); +int cypress_dpm_init(struct radeon_device *rdev); +void cypress_dpm_setup_asic(struct radeon_device *rdev); +int cypress_dpm_enable(struct radeon_device *rdev); +void cypress_dpm_disable(struct radeon_device *rdev); +int cypress_dpm_set_power_state(struct radeon_device *rdev); +void cypress_dpm_display_configuration_changed(struct radeon_device *rdev); +void cypress_dpm_fini(struct radeon_device *rdev);
/* * cayman diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 595607c..d2265f4 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -1044,6 +1044,11 @@ int radeon_pm_init(struct radeon_device *rdev) case CHIP_RV730: case CHIP_RV710: case CHIP_RV740: + case CHIP_CEDAR: + case CHIP_REDWOOD: + case CHIP_JUNIPER: + case CHIP_CYPRESS: + case CHIP_HEMLOCK: if (radeon_dpm == 1) rdev->pm.pm_method = PM_METHOD_DPM; else diff --git a/drivers/gpu/drm/radeon/radeon_ucode.h b/drivers/gpu/drm/radeon/radeon_ucode.h index 1910545..cb9c813 100644 --- a/drivers/gpu/drm/radeon/radeon_ucode.h +++ b/drivers/gpu/drm/radeon/radeon_ucode.h @@ -65,4 +65,24 @@ #define RV740_SMC_INT_VECTOR_START 0xffc0 #define RV740_SMC_INT_VECTOR_SIZE 0x0040
+#define CEDAR_SMC_UCODE_START 0x0100 +#define CEDAR_SMC_UCODE_SIZE 0x5d50 +#define CEDAR_SMC_INT_VECTOR_START 0xffc0 +#define CEDAR_SMC_INT_VECTOR_SIZE 0x0040 + +#define REDWOOD_SMC_UCODE_START 0x0100 +#define REDWOOD_SMC_UCODE_SIZE 0x5f0a +#define REDWOOD_SMC_INT_VECTOR_START 0xffc0 +#define REDWOOD_SMC_INT_VECTOR_SIZE 0x0040 + +#define JUNIPER_SMC_UCODE_START 0x0100 +#define JUNIPER_SMC_UCODE_SIZE 0x5f1f +#define JUNIPER_SMC_INT_VECTOR_START 0xffc0 +#define JUNIPER_SMC_INT_VECTOR_SIZE 0x0040 + +#define CYPRESS_SMC_UCODE_START 0x0100 +#define CYPRESS_SMC_UCODE_SIZE 0x61f7 +#define CYPRESS_SMC_INT_VECTOR_START 0xffc0 +#define CYPRESS_SMC_INT_VECTOR_SIZE 0x0040 + #endif diff --git a/drivers/gpu/drm/radeon/rv770_dpm.c b/drivers/gpu/drm/radeon/rv770_dpm.c index 81660a6..8ea6d69 100644 --- a/drivers/gpu/drm/radeon/rv770_dpm.c +++ b/drivers/gpu/drm/radeon/rv770_dpm.c @@ -27,6 +27,7 @@ #include "rv770d.h" #include "r600_dpm.h" #include "rv770_dpm.h" +#include "cypress_dpm.h" #include "atom.h"
#define MC_CG_ARB_FREQ_F0 0x0a @@ -56,6 +57,13 @@ struct rv7xx_power_info *rv770_get_pi(struct radeon_device *rdev) return pi; }
+struct evergreen_power_info *evergreen_get_pi(struct radeon_device *rdev) +{ + struct evergreen_power_info *pi = rdev->pm.dpm.priv; + + return pi; +} + static void rv770_enable_bif_dynamic_pcie_gen2(struct radeon_device *rdev, bool enable) { @@ -1795,8 +1803,8 @@ void rv770_enable_auto_throttle_source(struct radeon_device *rdev, } }
-static int rv770_set_thermal_temperature_range(struct radeon_device *rdev, - int min_temp, int max_temp) +int rv770_set_thermal_temperature_range(struct radeon_device *rdev, + int min_temp, int max_temp) { int low_temp = 0 * 1000; int high_temp = 255 * 1000; @@ -2045,6 +2053,7 @@ static void rv7xx_parse_pplib_clock_info(struct radeon_device *rdev, union pplib_clock_info *clock_info) { struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); struct rv7xx_ps *ps = rv770_get_ps(rps); u32 sclk, mclk; u16 vddc; @@ -2063,13 +2072,24 @@ static void rv7xx_parse_pplib_clock_info(struct radeon_device *rdev, break; }
- sclk = le16_to_cpu(clock_info->r600.usEngineClockLow); - sclk |= clock_info->r600.ucEngineClockHigh << 16; - mclk = le16_to_cpu(clock_info->r600.usMemoryClockLow); - mclk |= clock_info->r600.ucMemoryClockHigh << 16; + if (rdev->family >= CHIP_CEDAR) { + sclk = le16_to_cpu(clock_info->evergreen.usEngineClockLow); + sclk |= clock_info->evergreen.ucEngineClockHigh << 16; + mclk = le16_to_cpu(clock_info->evergreen.usMemoryClockLow); + mclk |= clock_info->evergreen.ucMemoryClockHigh << 16; + + pl->vddc = le16_to_cpu(clock_info->evergreen.usVDDC); + pl->vddci = le16_to_cpu(clock_info->evergreen.usVDDCI); + pl->flags = le32_to_cpu(clock_info->evergreen.ulFlags); + } else { + sclk = le16_to_cpu(clock_info->r600.usEngineClockLow); + sclk |= clock_info->r600.ucEngineClockHigh << 16; + mclk = le16_to_cpu(clock_info->r600.usMemoryClockLow); + mclk |= clock_info->r600.ucMemoryClockHigh << 16;
- pl->vddc = le16_to_cpu(clock_info->r600.usVDDC); - pl->flags = le32_to_cpu(clock_info->r600.ulFlags); + pl->vddc = le16_to_cpu(clock_info->r600.usVDDC); + pl->flags = le32_to_cpu(clock_info->r600.ulFlags); + }
pl->mclk = mclk; pl->sclk = sclk; @@ -2082,12 +2102,21 @@ static void rv7xx_parse_pplib_clock_info(struct radeon_device *rdev,
if (rps->class & ATOM_PPLIB_CLASSIFICATION_ACPI) { pi->acpi_vddc = pl->vddc; + if (rdev->family >= CHIP_CEDAR) + eg_pi->acpi_vddci = pl->vddci; if (ps->low.flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) pi->acpi_pcie_gen2 = true; else pi->acpi_pcie_gen2 = false; }
+ if (rps->class2 & ATOM_PPLIB_CLASSIFICATION2_ULV) { + if (rdev->family >= CHIP_BARTS) { + eg_pi->ulv.supported = true; + eg_pi->ulv.pl = pl; + } + } + if (pi->min_vddc_in_table > pl->vddc) pi->min_vddc_in_table = pl->vddc;
diff --git a/drivers/gpu/drm/radeon/rv770_dpm.h b/drivers/gpu/drm/radeon/rv770_dpm.h index df9aae5..bd6ea7b 100644 --- a/drivers/gpu/drm/radeon/rv770_dpm.h +++ b/drivers/gpu/drm/radeon/rv770_dpm.h @@ -269,4 +269,8 @@ int rv770_read_smc_soft_register(struct radeon_device *rdev, int rv770_write_smc_soft_register(struct radeon_device *rdev, u16 reg_offset, u32 value);
+/* thermal */ +int rv770_set_thermal_temperature_range(struct radeon_device *rdev, + int min_temp, int max_temp); + #endif diff --git a/drivers/gpu/drm/radeon/rv770_smc.c b/drivers/gpu/drm/radeon/rv770_smc.c index 8e07153..168aedb 100644 --- a/drivers/gpu/drm/radeon/rv770_smc.c +++ b/drivers/gpu/drm/radeon/rv770_smc.c @@ -114,6 +114,86 @@ static const u8 rv740_smc_int_vectors[] = 0x03, 0x51, 0x03, 0x51 };
+static const u8 cedar_smc_int_vectors[] = +{ + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x11, 0x8B, + 0x0B, 0x20, 0x0B, 0x05, + 0x04, 0xF6, 0x04, 0xF6, + 0x04, 0xF6, 0x04, 0xF6 +}; + +static const u8 redwood_smc_int_vectors[] = +{ + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x11, 0x8B, + 0x0B, 0x20, 0x0B, 0x05, + 0x04, 0xF6, 0x04, 0xF6, + 0x04, 0xF6, 0x04, 0xF6 +}; + +static const u8 juniper_smc_int_vectors[] = +{ + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x11, 0x8B, + 0x0B, 0x20, 0x0B, 0x05, + 0x04, 0xF6, 0x04, 0xF6, + 0x04, 0xF6, 0x04, 0xF6 +}; + +static const u8 cypress_smc_int_vectors[] = +{ + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x0B, 0x05, + 0x0B, 0x05, 0x11, 0x8B, + 0x0B, 0x20, 0x0B, 0x05, + 0x04, 0xF6, 0x04, 0xF6, + 0x04, 0xF6, 0x04, 0xF6 +}; + int rv770_set_smc_sram_address(struct radeon_device *rdev, u16 smc_address, u16 limit) { @@ -354,6 +434,35 @@ int rv770_load_smc_ucode(struct radeon_device *rdev, int_vect_start_address = RV740_SMC_INT_VECTOR_START; int_vect_size = RV740_SMC_INT_VECTOR_SIZE; break; + case CHIP_CEDAR: + ucode_start_address = CEDAR_SMC_UCODE_START; + ucode_size = CEDAR_SMC_UCODE_SIZE; + int_vect = (const u8 *)&cedar_smc_int_vectors; + int_vect_start_address = CEDAR_SMC_INT_VECTOR_START; + int_vect_size = CEDAR_SMC_INT_VECTOR_SIZE; + break; + case CHIP_REDWOOD: + ucode_start_address = REDWOOD_SMC_UCODE_START; + ucode_size = REDWOOD_SMC_UCODE_SIZE; + int_vect = (const u8 *)&redwood_smc_int_vectors; + int_vect_start_address = REDWOOD_SMC_INT_VECTOR_START; + int_vect_size = REDWOOD_SMC_INT_VECTOR_SIZE; + break; + case CHIP_JUNIPER: + ucode_start_address = JUNIPER_SMC_UCODE_START; + ucode_size = JUNIPER_SMC_UCODE_SIZE; + int_vect = (const u8 *)&juniper_smc_int_vectors; + int_vect_start_address = JUNIPER_SMC_INT_VECTOR_START; + int_vect_size = JUNIPER_SMC_INT_VECTOR_SIZE; + break; + case CHIP_CYPRESS: + case CHIP_HEMLOCK: + ucode_start_address = CYPRESS_SMC_UCODE_START; + ucode_size = CYPRESS_SMC_UCODE_SIZE; + int_vect = (const u8 *)&cypress_smc_int_vectors; + int_vect_start_address = CYPRESS_SMC_INT_VECTOR_START; + int_vect_size = CYPRESS_SMC_INT_VECTOR_SIZE; + break; default: DRM_ERROR("unknown asic in smc ucode loader\n"); BUG();
From: Alex Deucher alexander.deucher@amd.com
This adds dpm support for btc asics. This includes: - clockgating - dynamic engine clock scaling - dynamic memory clock scaling - dynamic voltage scaling - dynamic pcie gen1/gen2 switching (requires additional acpi support)
Set radeon.dpm=1 to enable.
v2: reduce stack usage
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/Makefile | 2 +- drivers/gpu/drm/radeon/btc_dpm.c | 2183 +++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/btc_dpm.h | 32 + drivers/gpu/drm/radeon/btcd.h | 181 +++ drivers/gpu/drm/radeon/ni.c | 21 + drivers/gpu/drm/radeon/radeon_asic.c | 12 + drivers/gpu/drm/radeon/radeon_asic.h | 6 + drivers/gpu/drm/radeon/radeon_pm.c | 3 + drivers/gpu/drm/radeon/radeon_ucode.h | 15 + drivers/gpu/drm/radeon/rv770_smc.c | 81 ++ 10 files changed, 2535 insertions(+), 1 deletions(-) create mode 100644 drivers/gpu/drm/radeon/btc_dpm.c create mode 100644 drivers/gpu/drm/radeon/btc_dpm.h create mode 100644 drivers/gpu/drm/radeon/btcd.h
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index 7092c96..af3dd8f 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile @@ -78,7 +78,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \ atombios_encoders.o radeon_semaphore.o radeon_sa.o atombios_i2c.o si.o \ si_blit_shaders.o radeon_prime.o radeon_uvd.o cik.o cik_blit_shaders.o \ r600_dpm.o rs780_dpm.o rv6xx_dpm.o rv770_dpm.o rv730_dpm.o rv740_dpm.o \ - rv770_smc.o cypress_dpm.o + rv770_smc.o cypress_dpm.o btc_dpm.o
radeon-$(CONFIG_COMPAT) += radeon_ioc32.o radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o diff --git a/drivers/gpu/drm/radeon/btc_dpm.c b/drivers/gpu/drm/radeon/btc_dpm.c new file mode 100644 index 0000000..6780120 --- /dev/null +++ b/drivers/gpu/drm/radeon/btc_dpm.c @@ -0,0 +1,2183 @@ +/* + * Copyright 2011 Advanced Micro Devices, 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: Alex Deucher + */ + +#include "drmP.h" +#include "radeon.h" +#include "btcd.h" +#include "r600_dpm.h" +#include "cypress_dpm.h" +#include "btc_dpm.h" +#include "atom.h" + +#define MC_CG_ARB_FREQ_F0 0x0a +#define MC_CG_ARB_FREQ_F1 0x0b +#define MC_CG_ARB_FREQ_F2 0x0c +#define MC_CG_ARB_FREQ_F3 0x0d + +#define MC_CG_SEQ_DRAMCONF_S0 0x05 +#define MC_CG_SEQ_DRAMCONF_S1 0x06 +#define MC_CG_SEQ_YCLK_SUSPEND 0x04 +#define MC_CG_SEQ_YCLK_RESUME 0x0a + +#define SMC_RAM_END 0x8000 + +#ifndef BTC_MGCG_SEQUENCE +#define BTC_MGCG_SEQUENCE 300 + +struct rv7xx_ps *rv770_get_ps(struct radeon_ps *rps); +struct rv7xx_power_info *rv770_get_pi(struct radeon_device *rdev); +struct evergreen_power_info *evergreen_get_pi(struct radeon_device *rdev); + + +//********* BARTS **************// +static const u32 barts_cgcg_cgls_default[] = +{ + /* Register, Value, Mask bits */ + 0x000008f8, 0x00000010, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000011, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000012, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000013, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000014, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000015, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000016, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000017, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000018, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000019, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000001a, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000001b, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000020, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000021, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000022, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000023, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000024, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000025, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000026, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000027, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000028, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000029, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000002a, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000002b, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff +}; +#define BARTS_CGCG_CGLS_DEFAULT_LENGTH sizeof(barts_cgcg_cgls_default) / (3 * sizeof(u32)) + +static const u32 barts_cgcg_cgls_disable[] = +{ + 0x000008f8, 0x00000010, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000011, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000012, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000013, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000014, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000015, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000016, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000017, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000018, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000019, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x0000001a, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x0000001b, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000020, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000021, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000022, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000023, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000024, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000025, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000026, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000027, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000028, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000029, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000002a, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000002b, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x00000644, 0x000f7912, 0x001f4180, + 0x00000644, 0x000f3812, 0x001f4180 +}; +#define BARTS_CGCG_CGLS_DISABLE_LENGTH sizeof(barts_cgcg_cgls_disable) / (3 * sizeof(u32)) + +static const u32 barts_cgcg_cgls_enable[] = +{ + /* 0x0000c124, 0x84180000, 0x00180000, */ + 0x00000644, 0x000f7892, 0x001f4080, + 0x000008f8, 0x00000010, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000011, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000012, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000013, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000014, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000015, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000016, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000017, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000018, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000019, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000001a, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000001b, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000020, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000021, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000022, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000023, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000024, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000025, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000026, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000027, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000028, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000029, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x0000002a, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x0000002b, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff +}; +#define BARTS_CGCG_CGLS_ENABLE_LENGTH sizeof(barts_cgcg_cgls_enable) / (3 * sizeof(u32)) + +static const u32 barts_mgcg_default[] = +{ + 0x0000802c, 0xc0000000, 0xffffffff, + 0x00005448, 0x00000100, 0xffffffff, + 0x000055e4, 0x00600100, 0xffffffff, + 0x0000160c, 0x00000100, 0xffffffff, + 0x0000c164, 0x00000100, 0xffffffff, + 0x00008a18, 0x00000100, 0xffffffff, + 0x0000897c, 0x06000100, 0xffffffff, + 0x00008b28, 0x00000100, 0xffffffff, + 0x00009144, 0x00000100, 0xffffffff, + 0x00009a60, 0x00000100, 0xffffffff, + 0x00009868, 0x00000100, 0xffffffff, + 0x00008d58, 0x00000100, 0xffffffff, + 0x00009510, 0x00000100, 0xffffffff, + 0x0000949c, 0x00000100, 0xffffffff, + 0x00009654, 0x00000100, 0xffffffff, + 0x00009030, 0x00000100, 0xffffffff, + 0x00009034, 0x00000100, 0xffffffff, + 0x00009038, 0x00000100, 0xffffffff, + 0x0000903c, 0x00000100, 0xffffffff, + 0x00009040, 0x00000100, 0xffffffff, + 0x0000a200, 0x00000100, 0xffffffff, + 0x0000a204, 0x00000100, 0xffffffff, + 0x0000a208, 0x00000100, 0xffffffff, + 0x0000a20c, 0x00000100, 0xffffffff, + 0x0000977c, 0x00000100, 0xffffffff, + 0x00003f80, 0x00000100, 0xffffffff, + 0x0000a210, 0x00000100, 0xffffffff, + 0x0000a214, 0x00000100, 0xffffffff, + 0x000004d8, 0x00000100, 0xffffffff, + 0x00009784, 0x00000100, 0xffffffff, + 0x00009698, 0x00000100, 0xffffffff, + 0x000004d4, 0x00000200, 0xffffffff, + 0x000004d0, 0x00000000, 0xffffffff, + 0x000030cc, 0x00000100, 0xffffffff, + 0x0000d0c0, 0xff000100, 0xffffffff, + 0x0000802c, 0x40000000, 0xffffffff, + 0x0000915c, 0x00010000, 0xffffffff, + 0x00009160, 0x00030002, 0xffffffff, + 0x00009164, 0x00050004, 0xffffffff, + 0x00009168, 0x00070006, 0xffffffff, + 0x00009178, 0x00070000, 0xffffffff, + 0x0000917c, 0x00030002, 0xffffffff, + 0x00009180, 0x00050004, 0xffffffff, + 0x0000918c, 0x00010006, 0xffffffff, + 0x00009190, 0x00090008, 0xffffffff, + 0x00009194, 0x00070000, 0xffffffff, + 0x00009198, 0x00030002, 0xffffffff, + 0x0000919c, 0x00050004, 0xffffffff, + 0x000091a8, 0x00010006, 0xffffffff, + 0x000091ac, 0x00090008, 0xffffffff, + 0x000091b0, 0x00070000, 0xffffffff, + 0x000091b4, 0x00030002, 0xffffffff, + 0x000091b8, 0x00050004, 0xffffffff, + 0x000091c4, 0x00010006, 0xffffffff, + 0x000091c8, 0x00090008, 0xffffffff, + 0x000091cc, 0x00070000, 0xffffffff, + 0x000091d0, 0x00030002, 0xffffffff, + 0x000091d4, 0x00050004, 0xffffffff, + 0x000091e0, 0x00010006, 0xffffffff, + 0x000091e4, 0x00090008, 0xffffffff, + 0x000091e8, 0x00000000, 0xffffffff, + 0x000091ec, 0x00070000, 0xffffffff, + 0x000091f0, 0x00030002, 0xffffffff, + 0x000091f4, 0x00050004, 0xffffffff, + 0x00009200, 0x00010006, 0xffffffff, + 0x00009204, 0x00090008, 0xffffffff, + 0x00009208, 0x00070000, 0xffffffff, + 0x0000920c, 0x00030002, 0xffffffff, + 0x00009210, 0x00050004, 0xffffffff, + 0x0000921c, 0x00010006, 0xffffffff, + 0x00009220, 0x00090008, 0xffffffff, + 0x00009224, 0x00070000, 0xffffffff, + 0x00009228, 0x00030002, 0xffffffff, + 0x0000922c, 0x00050004, 0xffffffff, + 0x00009238, 0x00010006, 0xffffffff, + 0x0000923c, 0x00090008, 0xffffffff, + 0x00009294, 0x00000000, 0xffffffff, + 0x0000802c, 0x40010000, 0xffffffff, + 0x0000915c, 0x00010000, 0xffffffff, + 0x00009160, 0x00030002, 0xffffffff, + 0x00009164, 0x00050004, 0xffffffff, + 0x00009168, 0x00070006, 0xffffffff, + 0x00009178, 0x00070000, 0xffffffff, + 0x0000917c, 0x00030002, 0xffffffff, + 0x00009180, 0x00050004, 0xffffffff, + 0x0000918c, 0x00010006, 0xffffffff, + 0x00009190, 0x00090008, 0xffffffff, + 0x00009194, 0x00070000, 0xffffffff, + 0x00009198, 0x00030002, 0xffffffff, + 0x0000919c, 0x00050004, 0xffffffff, + 0x000091a8, 0x00010006, 0xffffffff, + 0x000091ac, 0x00090008, 0xffffffff, + 0x000091b0, 0x00070000, 0xffffffff, + 0x000091b4, 0x00030002, 0xffffffff, + 0x000091b8, 0x00050004, 0xffffffff, + 0x000091c4, 0x00010006, 0xffffffff, + 0x000091c8, 0x00090008, 0xffffffff, + 0x000091cc, 0x00070000, 0xffffffff, + 0x000091d0, 0x00030002, 0xffffffff, + 0x000091d4, 0x00050004, 0xffffffff, + 0x000091e0, 0x00010006, 0xffffffff, + 0x000091e4, 0x00090008, 0xffffffff, + 0x000091e8, 0x00000000, 0xffffffff, + 0x000091ec, 0x00070000, 0xffffffff, + 0x000091f0, 0x00030002, 0xffffffff, + 0x000091f4, 0x00050004, 0xffffffff, + 0x00009200, 0x00010006, 0xffffffff, + 0x00009204, 0x00090008, 0xffffffff, + 0x00009208, 0x00070000, 0xffffffff, + 0x0000920c, 0x00030002, 0xffffffff, + 0x00009210, 0x00050004, 0xffffffff, + 0x0000921c, 0x00010006, 0xffffffff, + 0x00009220, 0x00090008, 0xffffffff, + 0x00009224, 0x00070000, 0xffffffff, + 0x00009228, 0x00030002, 0xffffffff, + 0x0000922c, 0x00050004, 0xffffffff, + 0x00009238, 0x00010006, 0xffffffff, + 0x0000923c, 0x00090008, 0xffffffff, + 0x00009294, 0x00000000, 0xffffffff, + 0x0000802c, 0xc0000000, 0xffffffff, + 0x000008f8, 0x00000010, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000011, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000012, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000013, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000014, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000015, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000016, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000017, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000018, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000019, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000001a, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000001b, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff +}; +#define BARTS_MGCG_DEFAULT_LENGTH sizeof(barts_mgcg_default) / (3 * sizeof(u32)) + +static const u32 barts_mgcg_disable[] = +{ + 0x0000802c, 0xc0000000, 0xffffffff, + 0x000008f8, 0x00000000, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000001, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000002, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000003, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x00009150, 0x00600000, 0xffffffff +}; +#define BARTS_MGCG_DISABLE_LENGTH sizeof(barts_mgcg_disable) / (3 * sizeof(u32)) + +static const u32 barts_mgcg_enable[] = +{ + 0x0000802c, 0xc0000000, 0xffffffff, + 0x000008f8, 0x00000000, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000001, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000002, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000003, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x00009150, 0x81944000, 0xffffffff +}; +#define BARTS_MGCG_ENABLE_LENGTH sizeof(barts_mgcg_enable) / (3 * sizeof(u32)) + +//********* CAICOS **************// +static const u32 caicos_cgcg_cgls_default[] = +{ + 0x000008f8, 0x00000010, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000011, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000012, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000013, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000014, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000015, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000016, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000017, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000018, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000019, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000001a, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000001b, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000020, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000021, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000022, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000023, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000024, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000025, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000026, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000027, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000028, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000029, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000002a, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000002b, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff +}; +#define CAICOS_CGCG_CGLS_DEFAULT_LENGTH sizeof(caicos_cgcg_cgls_default) / (3 * sizeof(u32)) + +static const u32 caicos_cgcg_cgls_disable[] = +{ + 0x000008f8, 0x00000010, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000011, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000012, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000013, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000014, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000015, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000016, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000017, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000018, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000019, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x0000001a, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x0000001b, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000020, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000021, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000022, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000023, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000024, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000025, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000026, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000027, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000028, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000029, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000002a, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000002b, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x00000644, 0x000f7912, 0x001f4180, + 0x00000644, 0x000f3812, 0x001f4180 +}; +#define CAICOS_CGCG_CGLS_DISABLE_LENGTH sizeof(caicos_cgcg_cgls_disable) / (3 * sizeof(u32)) + +static const u32 caicos_cgcg_cgls_enable[] = +{ + /* 0x0000c124, 0x84180000, 0x00180000, */ + 0x00000644, 0x000f7892, 0x001f4080, + 0x000008f8, 0x00000010, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000011, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000012, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000013, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000014, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000015, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000016, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000017, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000018, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000019, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000001a, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000001b, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000020, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000021, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000022, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000023, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000024, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000025, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000026, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000027, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000028, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000029, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x0000002a, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x0000002b, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff +}; +#define CAICOS_CGCG_CGLS_ENABLE_LENGTH sizeof(caicos_cgcg_cgls_enable) / (3 * sizeof(u32)) + +static const u32 caicos_mgcg_default[] = +{ + 0x0000802c, 0xc0000000, 0xffffffff, + 0x00005448, 0x00000100, 0xffffffff, + 0x000055e4, 0x00600100, 0xffffffff, + 0x0000160c, 0x00000100, 0xffffffff, + 0x0000c164, 0x00000100, 0xffffffff, + 0x00008a18, 0x00000100, 0xffffffff, + 0x0000897c, 0x06000100, 0xffffffff, + 0x00008b28, 0x00000100, 0xffffffff, + 0x00009144, 0x00000100, 0xffffffff, + 0x00009a60, 0x00000100, 0xffffffff, + 0x00009868, 0x00000100, 0xffffffff, + 0x00008d58, 0x00000100, 0xffffffff, + 0x00009510, 0x00000100, 0xffffffff, + 0x0000949c, 0x00000100, 0xffffffff, + 0x00009654, 0x00000100, 0xffffffff, + 0x00009030, 0x00000100, 0xffffffff, + 0x00009034, 0x00000100, 0xffffffff, + 0x00009038, 0x00000100, 0xffffffff, + 0x0000903c, 0x00000100, 0xffffffff, + 0x00009040, 0x00000100, 0xffffffff, + 0x0000a200, 0x00000100, 0xffffffff, + 0x0000a204, 0x00000100, 0xffffffff, + 0x0000a208, 0x00000100, 0xffffffff, + 0x0000a20c, 0x00000100, 0xffffffff, + 0x0000977c, 0x00000100, 0xffffffff, + 0x00003f80, 0x00000100, 0xffffffff, + 0x0000a210, 0x00000100, 0xffffffff, + 0x0000a214, 0x00000100, 0xffffffff, + 0x000004d8, 0x00000100, 0xffffffff, + 0x00009784, 0x00000100, 0xffffffff, + 0x00009698, 0x00000100, 0xffffffff, + 0x000004d4, 0x00000200, 0xffffffff, + 0x000004d0, 0x00000000, 0xffffffff, + 0x000030cc, 0x00000100, 0xffffffff, + 0x0000d0c0, 0xff000100, 0xffffffff, + 0x0000915c, 0x00010000, 0xffffffff, + 0x00009160, 0x00030002, 0xffffffff, + 0x00009164, 0x00050004, 0xffffffff, + 0x00009168, 0x00070006, 0xffffffff, + 0x00009178, 0x00070000, 0xffffffff, + 0x0000917c, 0x00030002, 0xffffffff, + 0x00009180, 0x00050004, 0xffffffff, + 0x0000918c, 0x00010006, 0xffffffff, + 0x00009190, 0x00090008, 0xffffffff, + 0x00009194, 0x00070000, 0xffffffff, + 0x00009198, 0x00030002, 0xffffffff, + 0x0000919c, 0x00050004, 0xffffffff, + 0x000091a8, 0x00010006, 0xffffffff, + 0x000091ac, 0x00090008, 0xffffffff, + 0x000091e8, 0x00000000, 0xffffffff, + 0x00009294, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000010, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000011, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000012, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000013, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000014, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000015, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000016, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000017, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000018, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000019, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000001a, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000001b, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff +}; +#define CAICOS_MGCG_DEFAULT_LENGTH sizeof(caicos_mgcg_default) / (3 * sizeof(u32)) + +static const u32 caicos_mgcg_disable[] = +{ + 0x0000802c, 0xc0000000, 0xffffffff, + 0x000008f8, 0x00000000, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000001, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000002, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000003, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x00009150, 0x00600000, 0xffffffff +}; +#define CAICOS_MGCG_DISABLE_LENGTH sizeof(caicos_mgcg_disable) / (3 * sizeof(u32)) + +static const u32 caicos_mgcg_enable[] = +{ + 0x0000802c, 0xc0000000, 0xffffffff, + 0x000008f8, 0x00000000, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000001, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000002, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000003, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x00009150, 0x46944040, 0xffffffff +}; +#define CAICOS_MGCG_ENABLE_LENGTH sizeof(caicos_mgcg_enable) / (3 * sizeof(u32)) + +//********* TURKS **************// +static const u32 turks_cgcg_cgls_default[] = +{ + 0x000008f8, 0x00000010, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000011, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000012, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000013, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000014, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000015, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000016, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000017, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000018, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000019, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000001a, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000001b, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000020, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000021, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000022, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000023, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000024, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000025, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000026, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000027, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000028, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000029, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000002a, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000002b, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff +}; +#define TURKS_CGCG_CGLS_DEFAULT_LENGTH sizeof(turks_cgcg_cgls_default) / (3 * sizeof(u32)) + +static const u32 turks_cgcg_cgls_disable[] = +{ + 0x000008f8, 0x00000010, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000011, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000012, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000013, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000014, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000015, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000016, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000017, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000018, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000019, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x0000001a, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x0000001b, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000020, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000021, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000022, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000023, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000024, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000025, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000026, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000027, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000028, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000029, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000002a, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000002b, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x00000644, 0x000f7912, 0x001f4180, + 0x00000644, 0x000f3812, 0x001f4180 +}; +#define TURKS_CGCG_CGLS_DISABLE_LENGTH sizeof(turks_cgcg_cgls_disable) / (3 * sizeof(u32)) + +static const u32 turks_cgcg_cgls_enable[] = +{ + /* 0x0000c124, 0x84180000, 0x00180000, */ + 0x00000644, 0x000f7892, 0x001f4080, + 0x000008f8, 0x00000010, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000011, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000012, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000013, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000014, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000015, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000016, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000017, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000018, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000019, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000001a, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000001b, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000020, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000021, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000022, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000023, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000024, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000025, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000026, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000027, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000028, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000029, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x0000002a, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x0000002b, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff +}; +#define TURKS_CGCG_CGLS_ENABLE_LENGTH sizeof(turks_cgcg_cgls_enable) / (3 * sizeof(u32)) + +// These are the sequences for turks_mgcg_shls +static const u32 turks_mgcg_default[] = +{ + 0x0000802c, 0xc0000000, 0xffffffff, + 0x00005448, 0x00000100, 0xffffffff, + 0x000055e4, 0x00600100, 0xffffffff, + 0x0000160c, 0x00000100, 0xffffffff, + 0x0000c164, 0x00000100, 0xffffffff, + 0x00008a18, 0x00000100, 0xffffffff, + 0x0000897c, 0x06000100, 0xffffffff, + 0x00008b28, 0x00000100, 0xffffffff, + 0x00009144, 0x00000100, 0xffffffff, + 0x00009a60, 0x00000100, 0xffffffff, + 0x00009868, 0x00000100, 0xffffffff, + 0x00008d58, 0x00000100, 0xffffffff, + 0x00009510, 0x00000100, 0xffffffff, + 0x0000949c, 0x00000100, 0xffffffff, + 0x00009654, 0x00000100, 0xffffffff, + 0x00009030, 0x00000100, 0xffffffff, + 0x00009034, 0x00000100, 0xffffffff, + 0x00009038, 0x00000100, 0xffffffff, + 0x0000903c, 0x00000100, 0xffffffff, + 0x00009040, 0x00000100, 0xffffffff, + 0x0000a200, 0x00000100, 0xffffffff, + 0x0000a204, 0x00000100, 0xffffffff, + 0x0000a208, 0x00000100, 0xffffffff, + 0x0000a20c, 0x00000100, 0xffffffff, + 0x0000977c, 0x00000100, 0xffffffff, + 0x00003f80, 0x00000100, 0xffffffff, + 0x0000a210, 0x00000100, 0xffffffff, + 0x0000a214, 0x00000100, 0xffffffff, + 0x000004d8, 0x00000100, 0xffffffff, + 0x00009784, 0x00000100, 0xffffffff, + 0x00009698, 0x00000100, 0xffffffff, + 0x000004d4, 0x00000200, 0xffffffff, + 0x000004d0, 0x00000000, 0xffffffff, + 0x000030cc, 0x00000100, 0xffffffff, + 0x0000d0c0, 0x00000100, 0xffffffff, + 0x0000915c, 0x00010000, 0xffffffff, + 0x00009160, 0x00030002, 0xffffffff, + 0x00009164, 0x00050004, 0xffffffff, + 0x00009168, 0x00070006, 0xffffffff, + 0x00009178, 0x00070000, 0xffffffff, + 0x0000917c, 0x00030002, 0xffffffff, + 0x00009180, 0x00050004, 0xffffffff, + 0x0000918c, 0x00010006, 0xffffffff, + 0x00009190, 0x00090008, 0xffffffff, + 0x00009194, 0x00070000, 0xffffffff, + 0x00009198, 0x00030002, 0xffffffff, + 0x0000919c, 0x00050004, 0xffffffff, + 0x000091a8, 0x00010006, 0xffffffff, + 0x000091ac, 0x00090008, 0xffffffff, + 0x000091b0, 0x00070000, 0xffffffff, + 0x000091b4, 0x00030002, 0xffffffff, + 0x000091b8, 0x00050004, 0xffffffff, + 0x000091c4, 0x00010006, 0xffffffff, + 0x000091c8, 0x00090008, 0xffffffff, + 0x000091cc, 0x00070000, 0xffffffff, + 0x000091d0, 0x00030002, 0xffffffff, + 0x000091d4, 0x00050004, 0xffffffff, + 0x000091e0, 0x00010006, 0xffffffff, + 0x000091e4, 0x00090008, 0xffffffff, + 0x000091e8, 0x00000000, 0xffffffff, + 0x000091ec, 0x00070000, 0xffffffff, + 0x000091f0, 0x00030002, 0xffffffff, + 0x000091f4, 0x00050004, 0xffffffff, + 0x00009200, 0x00010006, 0xffffffff, + 0x00009204, 0x00090008, 0xffffffff, + 0x00009208, 0x00070000, 0xffffffff, + 0x0000920c, 0x00030002, 0xffffffff, + 0x00009210, 0x00050004, 0xffffffff, + 0x0000921c, 0x00010006, 0xffffffff, + 0x00009220, 0x00090008, 0xffffffff, + 0x00009294, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000010, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000011, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000012, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000013, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000014, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000015, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000016, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000017, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000018, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000019, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000001a, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000001b, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff +}; +#define TURKS_MGCG_DEFAULT_LENGTH sizeof(turks_mgcg_default) / (3 * sizeof(u32)) + +static const u32 turks_mgcg_disable[] = +{ + 0x0000802c, 0xc0000000, 0xffffffff, + 0x000008f8, 0x00000000, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000001, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000002, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000003, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x00009150, 0x00600000, 0xffffffff +}; +#define TURKS_MGCG_DISABLE_LENGTH sizeof(turks_mgcg_disable) / (3 * sizeof(u32)) + +static const u32 turks_mgcg_enable[] = +{ + 0x0000802c, 0xc0000000, 0xffffffff, + 0x000008f8, 0x00000000, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000001, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000002, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000003, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x00009150, 0x6e944000, 0xffffffff +}; +#define TURKS_MGCG_ENABLE_LENGTH sizeof(turks_mgcg_enable) / (3 * sizeof(u32)) + +#endif + +#ifndef BTC_SYSLS_SEQUENCE +#define BTC_SYSLS_SEQUENCE 100 + + +//********* BARTS **************// +static const u32 barts_sysls_default[] = +{ + /* Register, Value, Mask bits */ + 0x000055e8, 0x00000000, 0xffffffff, + 0x0000d0bc, 0x00000000, 0xffffffff, + 0x000015c0, 0x000c1401, 0xffffffff, + 0x0000264c, 0x000c0400, 0xffffffff, + 0x00002648, 0x000c0400, 0xffffffff, + 0x00002650, 0x000c0400, 0xffffffff, + 0x000020b8, 0x000c0400, 0xffffffff, + 0x000020bc, 0x000c0400, 0xffffffff, + 0x000020c0, 0x000c0c80, 0xffffffff, + 0x0000f4a0, 0x000000c0, 0xffffffff, + 0x0000f4a4, 0x00680fff, 0xffffffff, + 0x000004c8, 0x00000001, 0xffffffff, + 0x000064ec, 0x00000000, 0xffffffff, + 0x00000c7c, 0x00000000, 0xffffffff, + 0x00006dfc, 0x00000000, 0xffffffff +}; +#define BARTS_SYSLS_DEFAULT_LENGTH sizeof(barts_sysls_default) / (3 * sizeof(u32)) + +static const u32 barts_sysls_disable[] = +{ + 0x000055e8, 0x00000000, 0xffffffff, + 0x0000d0bc, 0x00000000, 0xffffffff, + 0x000015c0, 0x00041401, 0xffffffff, + 0x0000264c, 0x00040400, 0xffffffff, + 0x00002648, 0x00040400, 0xffffffff, + 0x00002650, 0x00040400, 0xffffffff, + 0x000020b8, 0x00040400, 0xffffffff, + 0x000020bc, 0x00040400, 0xffffffff, + 0x000020c0, 0x00040c80, 0xffffffff, + 0x0000f4a0, 0x000000c0, 0xffffffff, + 0x0000f4a4, 0x00680000, 0xffffffff, + 0x000004c8, 0x00000001, 0xffffffff, + 0x000064ec, 0x00007ffd, 0xffffffff, + 0x00000c7c, 0x0000ff00, 0xffffffff, + 0x00006dfc, 0x0000007f, 0xffffffff +}; +#define BARTS_SYSLS_DISABLE_LENGTH sizeof(barts_sysls_disable) / (3 * sizeof(u32)) + +static const u32 barts_sysls_enable[] = +{ + 0x000055e8, 0x00000001, 0xffffffff, + 0x0000d0bc, 0x00000100, 0xffffffff, + 0x000015c0, 0x000c1401, 0xffffffff, + 0x0000264c, 0x000c0400, 0xffffffff, + 0x00002648, 0x000c0400, 0xffffffff, + 0x00002650, 0x000c0400, 0xffffffff, + 0x000020b8, 0x000c0400, 0xffffffff, + 0x000020bc, 0x000c0400, 0xffffffff, + 0x000020c0, 0x000c0c80, 0xffffffff, + 0x0000f4a0, 0x000000c0, 0xffffffff, + 0x0000f4a4, 0x00680fff, 0xffffffff, + 0x000004c8, 0x00000000, 0xffffffff, + 0x000064ec, 0x00000000, 0xffffffff, + 0x00000c7c, 0x00000000, 0xffffffff, + 0x00006dfc, 0x00000000, 0xffffffff +}; +#define BARTS_SYSLS_ENABLE_LENGTH sizeof(barts_sysls_enable) / (3 * sizeof(u32)) + +//********* CAICOS **************// +static const u32 caicos_sysls_default[] = +{ + 0x000055e8, 0x00000000, 0xffffffff, + 0x0000d0bc, 0x00000000, 0xffffffff, + 0x000015c0, 0x000c1401, 0xffffffff, + 0x0000264c, 0x000c0400, 0xffffffff, + 0x00002648, 0x000c0400, 0xffffffff, + 0x00002650, 0x000c0400, 0xffffffff, + 0x000020b8, 0x000c0400, 0xffffffff, + 0x000020bc, 0x000c0400, 0xffffffff, + 0x0000f4a0, 0x000000c0, 0xffffffff, + 0x0000f4a4, 0x00680fff, 0xffffffff, + 0x000004c8, 0x00000001, 0xffffffff, + 0x000064ec, 0x00000000, 0xffffffff, + 0x00000c7c, 0x00000000, 0xffffffff, + 0x00006dfc, 0x00000000, 0xffffffff +}; +#define CAICOS_SYSLS_DEFAULT_LENGTH sizeof(caicos_sysls_default) / (3 * sizeof(u32)) + +static const u32 caicos_sysls_disable[] = +{ + 0x000055e8, 0x00000000, 0xffffffff, + 0x0000d0bc, 0x00000000, 0xffffffff, + 0x000015c0, 0x00041401, 0xffffffff, + 0x0000264c, 0x00040400, 0xffffffff, + 0x00002648, 0x00040400, 0xffffffff, + 0x00002650, 0x00040400, 0xffffffff, + 0x000020b8, 0x00040400, 0xffffffff, + 0x000020bc, 0x00040400, 0xffffffff, + 0x0000f4a0, 0x000000c0, 0xffffffff, + 0x0000f4a4, 0x00680000, 0xffffffff, + 0x000004c8, 0x00000001, 0xffffffff, + 0x000064ec, 0x00007ffd, 0xffffffff, + 0x00000c7c, 0x0000ff00, 0xffffffff, + 0x00006dfc, 0x0000007f, 0xffffffff +}; +#define CAICOS_SYSLS_DISABLE_LENGTH sizeof(caicos_sysls_disable) / (3 * sizeof(u32)) + +static const u32 caicos_sysls_enable[] = +{ + 0x000055e8, 0x00000001, 0xffffffff, + 0x0000d0bc, 0x00000100, 0xffffffff, + 0x000015c0, 0x000c1401, 0xffffffff, + 0x0000264c, 0x000c0400, 0xffffffff, + 0x00002648, 0x000c0400, 0xffffffff, + 0x00002650, 0x000c0400, 0xffffffff, + 0x000020b8, 0x000c0400, 0xffffffff, + 0x000020bc, 0x000c0400, 0xffffffff, + 0x0000f4a0, 0x000000c0, 0xffffffff, + 0x0000f4a4, 0x00680fff, 0xffffffff, + 0x000064ec, 0x00000000, 0xffffffff, + 0x00000c7c, 0x00000000, 0xffffffff, + 0x00006dfc, 0x00000000, 0xffffffff, + 0x000004c8, 0x00000000, 0xffffffff +}; +#define CAICOS_SYSLS_ENABLE_LENGTH sizeof(caicos_sysls_enable) / (3 * sizeof(u32)) + +//********* TURKS **************// +static const u32 turks_sysls_default[] = +{ + 0x000055e8, 0x00000000, 0xffffffff, + 0x0000d0bc, 0x00000000, 0xffffffff, + 0x000015c0, 0x000c1401, 0xffffffff, + 0x0000264c, 0x000c0400, 0xffffffff, + 0x00002648, 0x000c0400, 0xffffffff, + 0x00002650, 0x000c0400, 0xffffffff, + 0x000020b8, 0x000c0400, 0xffffffff, + 0x000020bc, 0x000c0400, 0xffffffff, + 0x000020c0, 0x000c0c80, 0xffffffff, + 0x0000f4a0, 0x000000c0, 0xffffffff, + 0x0000f4a4, 0x00680fff, 0xffffffff, + 0x000004c8, 0x00000001, 0xffffffff, + 0x000064ec, 0x00000000, 0xffffffff, + 0x00000c7c, 0x00000000, 0xffffffff, + 0x00006dfc, 0x00000000, 0xffffffff +}; +#define TURKS_SYSLS_DEFAULT_LENGTH sizeof(turks_sysls_default) / (3 * sizeof(u32)) + +static const u32 turks_sysls_disable[] = +{ + 0x000055e8, 0x00000000, 0xffffffff, + 0x0000d0bc, 0x00000000, 0xffffffff, + 0x000015c0, 0x00041401, 0xffffffff, + 0x0000264c, 0x00040400, 0xffffffff, + 0x00002648, 0x00040400, 0xffffffff, + 0x00002650, 0x00040400, 0xffffffff, + 0x000020b8, 0x00040400, 0xffffffff, + 0x000020bc, 0x00040400, 0xffffffff, + 0x000020c0, 0x00040c80, 0xffffffff, + 0x0000f4a0, 0x000000c0, 0xffffffff, + 0x0000f4a4, 0x00680000, 0xffffffff, + 0x000004c8, 0x00000001, 0xffffffff, + 0x000064ec, 0x00007ffd, 0xffffffff, + 0x00000c7c, 0x0000ff00, 0xffffffff, + 0x00006dfc, 0x0000007f, 0xffffffff +}; +#define TURKS_SYSLS_DISABLE_LENGTH sizeof(turks_sysls_disable) / (3 * sizeof(u32)) + +static const u32 turks_sysls_enable[] = +{ + 0x000055e8, 0x00000001, 0xffffffff, + 0x0000d0bc, 0x00000100, 0xffffffff, + 0x000015c0, 0x000c1401, 0xffffffff, + 0x0000264c, 0x000c0400, 0xffffffff, + 0x00002648, 0x000c0400, 0xffffffff, + 0x00002650, 0x000c0400, 0xffffffff, + 0x000020b8, 0x000c0400, 0xffffffff, + 0x000020bc, 0x000c0400, 0xffffffff, + 0x000020c0, 0x000c0c80, 0xffffffff, + 0x0000f4a0, 0x000000c0, 0xffffffff, + 0x0000f4a4, 0x00680fff, 0xffffffff, + 0x000004c8, 0x00000000, 0xffffffff, + 0x000064ec, 0x00000000, 0xffffffff, + 0x00000c7c, 0x00000000, 0xffffffff, + 0x00006dfc, 0x00000000, 0xffffffff +}; +#define TURKS_SYSLS_ENABLE_LENGTH sizeof(turks_sysls_enable) / (3 * sizeof(u32)) + +#endif + +static void btc_enable_bif_dynamic_pcie_gen2(struct radeon_device *rdev, + bool enable) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + u32 tmp, bif; + + tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL); + if (enable) { + if ((tmp & LC_OTHER_SIDE_EVER_SENT_GEN2) && + (tmp & LC_OTHER_SIDE_SUPPORTS_GEN2)) { + if (!pi->boot_in_gen2) { + bif = RREG32(CG_BIF_REQ_AND_RSP) & ~CG_CLIENT_REQ_MASK; + bif |= CG_CLIENT_REQ(0xd); + WREG32(CG_BIF_REQ_AND_RSP, bif); + + tmp &= ~LC_HW_VOLTAGE_IF_CONTROL_MASK; + tmp |= LC_HW_VOLTAGE_IF_CONTROL(1); + tmp |= LC_GEN2_EN_STRAP; + + tmp |= LC_CLR_FAILED_SPD_CHANGE_CNT; + WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp); + udelay(10); + tmp &= ~LC_CLR_FAILED_SPD_CHANGE_CNT; + WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp); + } + } + } else { + if ((tmp & LC_OTHER_SIDE_EVER_SENT_GEN2) || + (tmp & LC_OTHER_SIDE_SUPPORTS_GEN2)) { + if (!pi->boot_in_gen2) { + bif = RREG32(CG_BIF_REQ_AND_RSP) & ~CG_CLIENT_REQ_MASK; + bif |= CG_CLIENT_REQ(0xd); + WREG32(CG_BIF_REQ_AND_RSP, bif); + + tmp &= ~LC_HW_VOLTAGE_IF_CONTROL_MASK; + tmp &= ~LC_GEN2_EN_STRAP; + } + WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp); + } + } +} + +static void btc_enable_dynamic_pcie_gen2(struct radeon_device *rdev, + bool enable) +{ + btc_enable_bif_dynamic_pcie_gen2(rdev, enable); + + if (enable) + WREG32_P(GENERAL_PWRMGT, ENABLE_GEN2PCIE, ~ENABLE_GEN2PCIE); + else + WREG32_P(GENERAL_PWRMGT, 0, ~ENABLE_GEN2PCIE); +} + +static int btc_disable_ulv(struct radeon_device *rdev) +{ + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + + if (eg_pi->ulv.supported) { + if (rv770_send_msg_to_smc(rdev, PPSMC_MSG_DisableULV) != PPSMC_Result_OK) + return -EINVAL; + } + return 0; +} + +static int btc_populate_ulv_state(struct radeon_device *rdev, + RV770_SMC_STATETABLE *table) +{ + int ret = -EINVAL; + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct rv7xx_pl *ulv_pl = eg_pi->ulv.pl; + + if (ulv_pl->vddc) { + ret = cypress_convert_power_level_to_smc(rdev, + ulv_pl, + &table->ULVState.levels[0], + PPSMC_DISPLAY_WATERMARK_LOW); + if (ret == 0) { + table->ULVState.levels[0].arbValue = MC_CG_ARB_FREQ_F0; + table->ULVState.levels[0].ACIndex = 1; + + table->ULVState.levels[1] = table->ULVState.levels[0]; + table->ULVState.levels[2] = table->ULVState.levels[0]; + + table->ULVState.flags |= PPSMC_SWSTATE_FLAG_DC; + + WREG32(CG_ULV_CONTROL, BTC_CGULVCONTROL_DFLT); + WREG32(CG_ULV_PARAMETER, BTC_CGULVPARAMETER_DFLT); + } + } + + return ret; +} + +static int btc_populate_smc_acpi_state(struct radeon_device *rdev, + RV770_SMC_STATETABLE *table) +{ + int ret = cypress_populate_smc_acpi_state(rdev, table); + + if (ret == 0) { + table->ACPIState.levels[0].ACIndex = 0; + table->ACPIState.levels[1].ACIndex = 0; + table->ACPIState.levels[2].ACIndex = 0; + } + + return ret; +} + +static void btc_program_mgcg_hw_sequence(struct radeon_device *rdev, + const u32 *sequence, u32 count) +{ + u32 i, length = count * 3; + u32 tmp; + + for (i = 0; i < length; i+=3) { + tmp = RREG32(sequence[i]); + tmp &= ~sequence[i+2]; + tmp |= sequence[i+1] & sequence[i+2]; + WREG32(sequence[i], tmp); + } +} + +static void btc_cg_clock_gating_default(struct radeon_device *rdev) +{ + u32 count; + const u32 *p = NULL; + + if (rdev->family == CHIP_BARTS) { + p = (const u32 *)&barts_cgcg_cgls_default; + count = BARTS_CGCG_CGLS_DEFAULT_LENGTH; + } else if (rdev->family == CHIP_TURKS) { + p = (const u32 *)&turks_cgcg_cgls_default; + count = TURKS_CGCG_CGLS_DEFAULT_LENGTH; + } else if (rdev->family == CHIP_CAICOS) { + p = (const u32 *)&caicos_cgcg_cgls_default; + count = CAICOS_CGCG_CGLS_DEFAULT_LENGTH; + } else + return; + + btc_program_mgcg_hw_sequence(rdev, p, count); +} + +static void btc_cg_clock_gating_enable(struct radeon_device *rdev, + bool enable) +{ + u32 count; + const u32 *p = NULL; + + if (enable) { + if (rdev->family == CHIP_BARTS) { + p = (const u32 *)&barts_cgcg_cgls_enable; + count = BARTS_CGCG_CGLS_ENABLE_LENGTH; + } else if (rdev->family == CHIP_TURKS) { + p = (const u32 *)&turks_cgcg_cgls_enable; + count = TURKS_CGCG_CGLS_ENABLE_LENGTH; + } else if (rdev->family == CHIP_CAICOS) { + p = (const u32 *)&caicos_cgcg_cgls_enable; + count = CAICOS_CGCG_CGLS_ENABLE_LENGTH; + } else + return; + } else { + if (rdev->family == CHIP_BARTS) { + p = (const u32 *)&barts_cgcg_cgls_disable; + count = BARTS_CGCG_CGLS_DISABLE_LENGTH; + } else if (rdev->family == CHIP_TURKS) { + p = (const u32 *)&turks_cgcg_cgls_disable; + count = TURKS_CGCG_CGLS_DISABLE_LENGTH; + } else if (rdev->family == CHIP_CAICOS) { + p = (const u32 *)&caicos_cgcg_cgls_disable; + count = CAICOS_CGCG_CGLS_DISABLE_LENGTH; + } else + return; + } + + btc_program_mgcg_hw_sequence(rdev, p, count); +} + +static void btc_mg_clock_gating_default(struct radeon_device *rdev) +{ + u32 count; + const u32 *p = NULL; + + if (rdev->family == CHIP_BARTS) { + p = (const u32 *)&barts_mgcg_default; + count = BARTS_MGCG_DEFAULT_LENGTH; + } else if (rdev->family == CHIP_TURKS) { + p = (const u32 *)&turks_mgcg_default; + count = TURKS_MGCG_DEFAULT_LENGTH; + } else if (rdev->family == CHIP_CAICOS) { + p = (const u32 *)&caicos_mgcg_default; + count = CAICOS_MGCG_DEFAULT_LENGTH; + } else + return; + + btc_program_mgcg_hw_sequence(rdev, p, count); +} + +static void btc_mg_clock_gating_enable(struct radeon_device *rdev, + bool enable) +{ + u32 count; + const u32 *p = NULL; + + if (enable) { + if (rdev->family == CHIP_BARTS) { + p = (const u32 *)&barts_mgcg_enable; + count = BARTS_MGCG_ENABLE_LENGTH; + } else if (rdev->family == CHIP_TURKS) { + p = (const u32 *)&turks_mgcg_enable; + count = TURKS_MGCG_ENABLE_LENGTH; + } else if (rdev->family == CHIP_CAICOS) { + p = (const u32 *)&caicos_mgcg_enable; + count = CAICOS_MGCG_ENABLE_LENGTH; + } else + return; + } else { + if (rdev->family == CHIP_BARTS) { + p = (const u32 *)&barts_mgcg_disable[0]; + count = BARTS_MGCG_DISABLE_LENGTH; + } else if (rdev->family == CHIP_TURKS) { + p = (const u32 *)&turks_mgcg_disable[0]; + count = TURKS_MGCG_DISABLE_LENGTH; + } else if (rdev->family == CHIP_CAICOS) { + p = (const u32 *)&caicos_mgcg_disable[0]; + count = CAICOS_MGCG_DISABLE_LENGTH; + } else + return; + } + + btc_program_mgcg_hw_sequence(rdev, p, count); +} + +static void btc_ls_clock_gating_default(struct radeon_device *rdev) +{ + u32 count; + const u32 *p = NULL; + + if (rdev->family == CHIP_BARTS) { + p = (const u32 *)&barts_sysls_default; + count = BARTS_SYSLS_DEFAULT_LENGTH; + } else if (rdev->family == CHIP_TURKS) { + p = (const u32 *)&turks_sysls_default; + count = TURKS_SYSLS_DEFAULT_LENGTH; + } else if (rdev->family == CHIP_CAICOS) { + p = (const u32 *)&caicos_sysls_default; + count = CAICOS_SYSLS_DEFAULT_LENGTH; + } else + return; + + btc_program_mgcg_hw_sequence(rdev, p, count); +} + +static void btc_ls_clock_gating_enable(struct radeon_device *rdev, + bool enable) +{ + u32 count; + const u32 *p = NULL; + + if (enable) { + if (rdev->family == CHIP_BARTS) { + p = (const u32 *)&barts_sysls_enable; + count = BARTS_SYSLS_ENABLE_LENGTH; + } else if (rdev->family == CHIP_TURKS) { + p = (const u32 *)&turks_sysls_enable; + count = TURKS_SYSLS_ENABLE_LENGTH; + } else if (rdev->family == CHIP_CAICOS) { + p = (const u32 *)&caicos_sysls_enable; + count = CAICOS_SYSLS_ENABLE_LENGTH; + } else + return; + } else { + if (rdev->family == CHIP_BARTS) { + p = (const u32 *)&barts_sysls_disable; + count = BARTS_SYSLS_DISABLE_LENGTH; + } else if (rdev->family == CHIP_TURKS) { + p = (const u32 *)&turks_sysls_disable; + count = TURKS_SYSLS_DISABLE_LENGTH; + } else if (rdev->family == CHIP_CAICOS) { + p = (const u32 *)&caicos_sysls_disable; + count = CAICOS_SYSLS_DISABLE_LENGTH; + } else + return; + } + + btc_program_mgcg_hw_sequence(rdev, p, count); +} + +static bool btc_dpm_enabled(struct radeon_device *rdev) +{ + if (rv770_is_smc_running(rdev)) + return true; + else + return false; +} + +static int btc_init_smc_table(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct radeon_ps *radeon_boot_state = rdev->pm.dpm.boot_ps; + RV770_SMC_STATETABLE *table = &pi->smc_statetable; + int ret; + + memset(table, 0, sizeof(RV770_SMC_STATETABLE)); + + cypress_populate_smc_voltage_tables(rdev, table); + + switch (rdev->pm.int_thermal_type) { + case THERMAL_TYPE_EVERGREEN: + case THERMAL_TYPE_EMC2103_WITH_INTERNAL: + table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_INTERNAL; + break; + case THERMAL_TYPE_NONE: + table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_NONE; + break; + default: + table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_EXTERNAL; + break; + } + + if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_HARDWAREDC) + table->systemFlags |= PPSMC_SYSTEMFLAG_GPIO_DC; + + if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_REGULATOR_HOT) + table->systemFlags |= PPSMC_SYSTEMFLAG_REGULATOR_HOT; + + if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_STEPVDDC) + table->systemFlags |= PPSMC_SYSTEMFLAG_STEPVDDC; + + if (pi->mem_gddr5) + table->systemFlags |= PPSMC_SYSTEMFLAG_GDDR5; + + ret = cypress_populate_smc_initial_state(rdev, radeon_boot_state, table); + if (ret) + return ret; + + if (eg_pi->sclk_deep_sleep) + WREG32_P(SCLK_PSKIP_CNTL, PSKIP_ON_ALLOW_STOP_HI(32), + ~PSKIP_ON_ALLOW_STOP_HI_MASK); + + ret = btc_populate_smc_acpi_state(rdev, table); + if (ret) + return ret; + + if (eg_pi->ulv.supported) { + ret = btc_populate_ulv_state(rdev, table); + if (ret) + eg_pi->ulv.supported = false; + } + + table->driverState = table->initialState; + + return rv770_copy_bytes_to_smc(rdev, + pi->state_table_start, + (u8 *)table, + sizeof(RV770_SMC_STATETABLE), + pi->sram_end); +} + +static int btc_reset_to_default(struct radeon_device *rdev) +{ + if (rv770_send_msg_to_smc(rdev, PPSMC_MSG_ResetToDefaults) != PPSMC_Result_OK) + return -EINVAL; + + return 0; +} + +static void btc_stop_smc(struct radeon_device *rdev) +{ + int i; + + for (i = 0; i < rdev->usec_timeout; i++) { + if (((RREG32(LB_SYNC_RESET_SEL) & LB_SYNC_RESET_SEL_MASK) >> LB_SYNC_RESET_SEL_SHIFT) != 1) + break; + udelay(1); + } + udelay(100); + + r7xx_stop_smc(rdev); +} + +static void btc_read_arb_registers(struct radeon_device *rdev) +{ + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct evergreen_arb_registers *arb_registers = + &eg_pi->bootup_arb_registers; + + arb_registers->mc_arb_dram_timing = RREG32(MC_ARB_DRAM_TIMING); + arb_registers->mc_arb_dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2); + arb_registers->mc_arb_rfsh_rate = RREG32(MC_ARB_RFSH_RATE); + arb_registers->mc_arb_burst_time = RREG32(MC_ARB_BURST_TIME); +} + + +static void btc_set_arb0_registers(struct radeon_device *rdev, + struct evergreen_arb_registers *arb_registers) +{ + u32 val; + + WREG32(MC_ARB_DRAM_TIMING, arb_registers->mc_arb_dram_timing); + WREG32(MC_ARB_DRAM_TIMING2, arb_registers->mc_arb_dram_timing2); + + val = (arb_registers->mc_arb_rfsh_rate & POWERMODE0_MASK) >> + POWERMODE0_SHIFT; + WREG32_P(MC_ARB_RFSH_RATE, POWERMODE0(val), ~POWERMODE0_MASK); + + val = (arb_registers->mc_arb_burst_time & STATE0_MASK) >> + STATE0_SHIFT; + WREG32_P(MC_ARB_BURST_TIME, STATE0(val), ~STATE0_MASK); +} + +static void btc_set_boot_state_timing(struct radeon_device *rdev) +{ + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + + if (eg_pi->ulv.supported) + btc_set_arb0_registers(rdev, &eg_pi->bootup_arb_registers); +} + +static bool btc_is_state_ulv_compatible(struct radeon_device *rdev, + struct radeon_ps *radeon_state) +{ + struct rv7xx_ps *state = rv770_get_ps(radeon_state); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct rv7xx_pl *ulv_pl = eg_pi->ulv.pl; + + if (state->low.mclk != ulv_pl->mclk) + return false; + + if (state->low.vddci != ulv_pl->vddci) + return false; + + /* XXX check minclocks, etc. */ + + return true; +} + + +static int btc_set_ulv_dram_timing(struct radeon_device *rdev) +{ + u32 val; + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct rv7xx_pl *ulv_pl = eg_pi->ulv.pl; + + radeon_atom_set_engine_dram_timings(rdev, + ulv_pl->sclk, + ulv_pl->mclk); + + val = rv770_calculate_memory_refresh_rate(rdev, ulv_pl->sclk); + WREG32_P(MC_ARB_RFSH_RATE, POWERMODE0(val), ~POWERMODE0_MASK); + + val = cypress_calculate_burst_time(rdev, ulv_pl->sclk, ulv_pl->mclk); + WREG32_P(MC_ARB_BURST_TIME, STATE0(val), ~STATE0_MASK); + + return 0; +} + +static int btc_enable_ulv(struct radeon_device *rdev) +{ + if (rv770_send_msg_to_smc(rdev, PPSMC_MSG_EnableULV) != PPSMC_Result_OK) + return -EINVAL; + + return 0; +} + +static int btc_set_power_state_conditionally_enable_ulv(struct radeon_device *rdev) +{ + int ret = 0; + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; + + if (eg_pi->ulv.supported) { + if (btc_is_state_ulv_compatible(rdev, radeon_new_state)) { + // Set ARB[0] to reflect the DRAM timing needed for ULV. + ret = btc_set_ulv_dram_timing(rdev); + if (ret == 0) + ret = btc_enable_ulv(rdev); + } + } + + return ret; +} + +static bool btc_check_s0_mc_reg_index(u16 in_reg, u16 *out_reg) +{ + bool result = true; + + switch (in_reg) { + case MC_SEQ_RAS_TIMING >> 2: + *out_reg = MC_SEQ_RAS_TIMING_LP >> 2; + break; + case MC_SEQ_CAS_TIMING >> 2: + *out_reg = MC_SEQ_CAS_TIMING_LP >> 2; + break; + case MC_SEQ_MISC_TIMING >> 2: + *out_reg = MC_SEQ_MISC_TIMING_LP >> 2; + break; + case MC_SEQ_MISC_TIMING2 >> 2: + *out_reg = MC_SEQ_MISC_TIMING2_LP >> 2; + break; + case MC_SEQ_RD_CTL_D0 >> 2: + *out_reg = MC_SEQ_RD_CTL_D0_LP >> 2; + break; + case MC_SEQ_RD_CTL_D1 >> 2: + *out_reg = MC_SEQ_RD_CTL_D1_LP >> 2; + break; + case MC_SEQ_WR_CTL_D0 >> 2: + *out_reg = MC_SEQ_WR_CTL_D0_LP >> 2; + break; + case MC_SEQ_WR_CTL_D1 >> 2: + *out_reg = MC_SEQ_WR_CTL_D1_LP >> 2; + break; + case MC_PMG_CMD_EMRS >> 2: + *out_reg = MC_SEQ_PMG_CMD_EMRS_LP >> 2; + break; + case MC_PMG_CMD_MRS >> 2: + *out_reg = MC_SEQ_PMG_CMD_MRS_LP >> 2; + break; + case MC_PMG_CMD_MRS1 >> 2: + *out_reg = MC_SEQ_PMG_CMD_MRS1_LP >> 2; + break; + default: + result = false; + break; + } + + return result; +} + +static void btc_set_valid_flag(struct evergreen_mc_reg_table *table) +{ + u8 i, j; + + for (i = 0; i < table->last; i++) { + for (j = 1; j < table->num_entries; j++) { + if (table->mc_reg_table_entry[j-1].mc_data[i] != + table->mc_reg_table_entry[j].mc_data[i]) { + table->valid_flag |= (1 << i); + break; + } + } + } +} + +static int btc_set_mc_special_registers(struct radeon_device *rdev, + struct evergreen_mc_reg_table *table) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + u8 i, j, k; + u32 tmp; + + for (i = 0, j = table->last; i < table->last; i++) { + switch (table->mc_reg_address[i].s1) { + case MC_SEQ_MISC1 >> 2: + tmp = RREG32(MC_PMG_CMD_EMRS); + table->mc_reg_address[j].s1 = MC_PMG_CMD_EMRS >> 2; + table->mc_reg_address[j].s0 = MC_SEQ_PMG_CMD_EMRS_LP >> 2; + for (k = 0; k < table->num_entries; k++) { + table->mc_reg_table_entry[k].mc_data[j] = + ((tmp & 0xffff0000)) | + ((table->mc_reg_table_entry[k].mc_data[i] & 0xffff0000) >> 16); + } + j++; + + if (j > SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE) + return -EINVAL; + + tmp = RREG32(MC_PMG_CMD_MRS); + table->mc_reg_address[j].s1 = MC_PMG_CMD_MRS >> 2; + table->mc_reg_address[j].s0 = MC_SEQ_PMG_CMD_MRS_LP >> 2; + for (k = 0; k < table->num_entries; k++) { + table->mc_reg_table_entry[k].mc_data[j] = + (tmp & 0xffff0000) | + (table->mc_reg_table_entry[k].mc_data[i] & 0x0000ffff); + if (!pi->mem_gddr5) + table->mc_reg_table_entry[k].mc_data[j] |= 0x100; + } + j++; + + if (j > SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE) + return -EINVAL; + break; + case MC_SEQ_RESERVE_M >> 2: + tmp = RREG32(MC_PMG_CMD_MRS1); + table->mc_reg_address[j].s1 = MC_PMG_CMD_MRS1 >> 2; + table->mc_reg_address[j].s0 = MC_SEQ_PMG_CMD_MRS1_LP >> 2; + for (k = 0; k < table->num_entries; k++) { + table->mc_reg_table_entry[k].mc_data[j] = + (tmp & 0xffff0000) | + (table->mc_reg_table_entry[k].mc_data[i] & 0x0000ffff); + } + j++; + + if (j > SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE) + return -EINVAL; + break; + default: + break; + } + } + + table->last = j; + + return 0; +} + +static void btc_set_s0_mc_reg_index(struct evergreen_mc_reg_table *table) +{ + u32 i; + u16 address; + + for (i = 0; i < table->last; i++) { + table->mc_reg_address[i].s0 = + btc_check_s0_mc_reg_index(table->mc_reg_address[i].s1, &address) ? + address : table->mc_reg_address[i].s1; + } +} + +static int btc_copy_vbios_mc_reg_table(struct atom_mc_reg_table *table, + struct evergreen_mc_reg_table *eg_table) +{ + u8 i, j; + + if (table->last > SMC_EVERGREEN_MC_REGISTER_ARRAY_SIZE) + return -EINVAL; + + if (table->num_entries > MAX_AC_TIMING_ENTRIES) + return -EINVAL; + + for (i = 0; i < table->last; i++) + eg_table->mc_reg_address[i].s1 = table->mc_reg_address[i].s1; + eg_table->last = table->last; + + for (i = 0; i < table->num_entries; i++) { + eg_table->mc_reg_table_entry[i].mclk_max = + table->mc_reg_table_entry[i].mclk_max; + for(j = 0; j < table->last; j++) + eg_table->mc_reg_table_entry[i].mc_data[j] = + table->mc_reg_table_entry[i].mc_data[j]; + } + eg_table->num_entries = table->num_entries; + + return 0; +} + +static int btc_initialize_mc_reg_table(struct radeon_device *rdev) +{ + int ret; + struct atom_mc_reg_table *table; + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct evergreen_mc_reg_table *eg_table = &eg_pi->mc_reg_table; + u8 module_index = rv770_get_memory_module_index(rdev); + + table = kzalloc(sizeof(struct atom_mc_reg_table), GFP_KERNEL); + if (!table) + return -ENOMEM; + + /* Program additional LP registers that are no longer programmed by VBIOS */ + WREG32(MC_SEQ_RAS_TIMING_LP, RREG32(MC_SEQ_RAS_TIMING)); + WREG32(MC_SEQ_CAS_TIMING_LP, RREG32(MC_SEQ_CAS_TIMING)); + WREG32(MC_SEQ_MISC_TIMING_LP, RREG32(MC_SEQ_MISC_TIMING)); + WREG32(MC_SEQ_MISC_TIMING2_LP, RREG32(MC_SEQ_MISC_TIMING2)); + WREG32(MC_SEQ_RD_CTL_D0_LP, RREG32(MC_SEQ_RD_CTL_D0)); + WREG32(MC_SEQ_RD_CTL_D1_LP, RREG32(MC_SEQ_RD_CTL_D1)); + WREG32(MC_SEQ_WR_CTL_D0_LP, RREG32(MC_SEQ_WR_CTL_D0)); + WREG32(MC_SEQ_WR_CTL_D1_LP, RREG32(MC_SEQ_WR_CTL_D1)); + WREG32(MC_SEQ_PMG_CMD_EMRS_LP, RREG32(MC_PMG_CMD_EMRS)); + WREG32(MC_SEQ_PMG_CMD_MRS_LP, RREG32(MC_PMG_CMD_MRS)); + WREG32(MC_SEQ_PMG_CMD_MRS1_LP, RREG32(MC_PMG_CMD_MRS1)); + + ret = radeon_atom_init_mc_reg_table(rdev, module_index, table); + + if (ret) + goto init_mc_done; + + ret = btc_copy_vbios_mc_reg_table(table, eg_table); + + if (ret) + goto init_mc_done; + + btc_set_s0_mc_reg_index(eg_table); + ret = btc_set_mc_special_registers(rdev, eg_table); + + if (ret) + goto init_mc_done; + + btc_set_valid_flag(eg_table); + +init_mc_done: + kfree(table); + + return ret; +} + +static void btc_init_stutter_mode(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + u32 tmp; + + if (pi->mclk_stutter_mode_threshold) { + if (pi->mem_gddr5) { + tmp = RREG32(MC_PMG_AUTO_CFG); + if ((0x200 & tmp) == 0) { + tmp = (tmp & 0xfffffc0b) | 0x204; + WREG32(MC_PMG_AUTO_CFG, tmp); + } + } + } +} + +void btc_dpm_reset_asic(struct radeon_device *rdev) +{ + rv770_restrict_performance_levels_before_switch(rdev); + btc_disable_ulv(rdev); + btc_set_boot_state_timing(rdev); + rv770_set_boot_state(rdev); +} + +int btc_dpm_set_power_state(struct radeon_device *rdev) +{ + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + + btc_disable_ulv(rdev); + btc_set_boot_state_timing(rdev); + rv770_restrict_performance_levels_before_switch(rdev); + + if (eg_pi->pcie_performance_request) + cypress_notify_link_speed_change_before_state_change(rdev); + + rv770_halt_smc(rdev); + cypress_upload_sw_state(rdev); + + if (eg_pi->dynamic_ac_timing) + cypress_upload_mc_reg_table(rdev); + + cypress_program_memory_timing_parameters(rdev); + + rv770_resume_smc(rdev); + rv770_set_sw_state(rdev); + + if (eg_pi->pcie_performance_request) + cypress_notify_link_speed_change_after_state_change(rdev); + + btc_set_power_state_conditionally_enable_ulv(rdev); + + return 0; +} + +int btc_dpm_enable(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + + if (pi->gfx_clock_gating) + btc_cg_clock_gating_default(rdev); + + if (btc_dpm_enabled(rdev)) + return -EINVAL; + + if (pi->mg_clock_gating) + btc_mg_clock_gating_default(rdev); + + if (eg_pi->ls_clock_gating) + btc_ls_clock_gating_default(rdev); + + if (pi->voltage_control) { + rv770_enable_voltage_control(rdev, true); + cypress_construct_voltage_tables(rdev); + } + + if (pi->mvdd_control) + cypress_get_mvdd_configuration(rdev); + + if (eg_pi->dynamic_ac_timing) + btc_initialize_mc_reg_table(rdev); + + if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_BACKBIAS) + rv770_enable_backbias(rdev, true); + + if (pi->dynamic_ss) + cypress_enable_spread_spectrum(rdev, true); + + if (pi->thermal_protection) + rv770_enable_thermal_protection(rdev, true); + + rv770_setup_bsp(rdev); + rv770_program_git(rdev); + rv770_program_tp(rdev); + rv770_program_tpp(rdev); + rv770_program_sstp(rdev); + rv770_program_engine_speed_parameters(rdev); + cypress_enable_display_gap(rdev); + rv770_program_vc(rdev); + + if (pi->dynamic_pcie_gen2) + btc_enable_dynamic_pcie_gen2(rdev, true); + + if (rv770_upload_firmware(rdev)) + return -EINVAL; + + cypress_get_table_locations(rdev); + btc_init_smc_table(rdev); + + if (eg_pi->dynamic_ac_timing) + cypress_populate_mc_reg_table(rdev); + + cypress_program_response_times(rdev); + r7xx_start_smc(rdev); + cypress_notify_smc_display_change(rdev, false); + cypress_enable_sclk_control(rdev, true); + + if (eg_pi->memory_transition) + cypress_enable_mclk_control(rdev, true); + + cypress_start_dpm(rdev); + + if (pi->gfx_clock_gating) + btc_cg_clock_gating_enable(rdev, true); + + if (pi->mg_clock_gating) + btc_mg_clock_gating_enable(rdev, true); + + if (eg_pi->ls_clock_gating) + btc_ls_clock_gating_enable(rdev, true); + + if (rdev->irq.installed && + r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) { + PPSMC_Result result; + + rv770_set_thermal_temperature_range(rdev, R600_TEMP_RANGE_MIN, R600_TEMP_RANGE_MAX); + rdev->irq.dpm_thermal = true; + radeon_irq_set(rdev); + result = rv770_send_msg_to_smc(rdev, PPSMC_MSG_EnableThermalInterrupt); + + if (result != PPSMC_Result_OK) + DRM_DEBUG_KMS("Could not enable thermal interrupts.\n"); + } + + rv770_enable_auto_throttle_source(rdev, RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL, true); + + btc_init_stutter_mode(rdev); + + return 0; +}; + +void btc_dpm_disable(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + + if (!btc_dpm_enabled(rdev)) + return; + + rv770_clear_vc(rdev); + + if (pi->thermal_protection) + rv770_enable_thermal_protection(rdev, false); + + if (pi->dynamic_pcie_gen2) + btc_enable_dynamic_pcie_gen2(rdev, false); + + if (rdev->irq.installed && + r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) { + rdev->irq.dpm_thermal = false; + radeon_irq_set(rdev); + } + + if (pi->gfx_clock_gating) + btc_cg_clock_gating_enable(rdev, false); + + if (pi->mg_clock_gating) + btc_mg_clock_gating_enable(rdev, false); + + if (eg_pi->ls_clock_gating) + btc_ls_clock_gating_enable(rdev, false); + + rv770_stop_dpm(rdev); + btc_reset_to_default(rdev); + btc_stop_smc(rdev); + cypress_enable_spread_spectrum(rdev, false); +} + +void btc_dpm_setup_asic(struct radeon_device *rdev) +{ + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + + rv770_get_memory_type(rdev); + rv740_read_clock_registers(rdev); + btc_read_arb_registers(rdev); + rv770_read_voltage_smio_registers(rdev); + + if (eg_pi->pcie_performance_request) + cypress_advertise_gen2_capability(rdev); + + rv770_get_pcie_gen2_status(rdev); + rv770_enable_acpi_pm(rdev); +} + +int btc_dpm_init(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi; + struct evergreen_power_info *eg_pi; + int index = GetIndexIntoMasterTable(DATA, ASIC_InternalSS_Info); + u16 data_offset, size; + u8 frev, crev; + struct atom_clock_dividers dividers; + int ret; + + eg_pi = kzalloc(sizeof(struct evergreen_power_info), GFP_KERNEL); + if (eg_pi == NULL) + return -ENOMEM; + rdev->pm.dpm.priv = eg_pi; + pi = &eg_pi->rv7xx; + + rv770_get_max_vddc(rdev); + + eg_pi->ulv.supported = false; + pi->acpi_vddc = 0; + eg_pi->acpi_vddci = 0; + pi->min_vddc_in_table = 0; + pi->max_vddc_in_table = 0; + + ret = rv7xx_parse_power_table(rdev); + if (ret) + return ret; + + if (rdev->pm.dpm.voltage_response_time == 0) + rdev->pm.dpm.voltage_response_time = R600_VOLTAGERESPONSETIME_DFLT; + if (rdev->pm.dpm.backbias_response_time == 0) + rdev->pm.dpm.backbias_response_time = R600_BACKBIASRESPONSETIME_DFLT; + + ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM, + 0, false, ÷rs); + if (ret) + pi->ref_div = dividers.ref_div + 1; + else + pi->ref_div = R600_REFERENCEDIVIDER_DFLT; + + pi->mclk_strobe_mode_threshold = 40000; + pi->mclk_edc_enable_threshold = 40000; + eg_pi->mclk_edc_wr_enable_threshold = 40000; + + pi->voltage_control = + radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC); + + pi->mvdd_control = + radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_MVDDC); + + eg_pi->vddci_control = + radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDCI); + + if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size, + &frev, &crev, &data_offset)) { + pi->sclk_ss = true; + pi->mclk_ss = true; + pi->dynamic_ss = true; + } else { + pi->sclk_ss = false; + pi->mclk_ss = false; + pi->dynamic_ss = true; + } + + pi->asi = RV770_ASI_DFLT; + pi->pasi = CYPRESS_HASI_DFLT; + pi->vrc = CYPRESS_VRC_DFLT; + + pi->power_gating = false; + + pi->gfx_clock_gating = true; + + pi->mg_clock_gating = true; + pi->mgcgtssm = true; + eg_pi->ls_clock_gating = false; + eg_pi->sclk_deep_sleep = false; + + pi->dynamic_pcie_gen2 = true; + + if (pi->gfx_clock_gating && + (rdev->pm.int_thermal_type != THERMAL_TYPE_NONE)) + pi->thermal_protection = true; + else + pi->thermal_protection = false; + + pi->display_gap = true; + + if (rdev->flags & RADEON_IS_MOBILITY) + pi->dcodt = true; + else + pi->dcodt = false; + + pi->ulps = true; + + eg_pi->dynamic_ac_timing = true; + eg_pi->abm = true; + eg_pi->mcls = true; + eg_pi->light_sleep = true; + eg_pi->memory_transition = true; +#if defined(CONFIG_ACPI) + eg_pi->pcie_performance_request = + radeon_acpi_is_pcie_performance_request_supported(rdev); +#else + eg_pi->pcie_performance_request = false; +#endif + + if (rdev->family == CHIP_BARTS) + eg_pi->dll_default_on = true; + else + eg_pi->dll_default_on = false; + + eg_pi->sclk_deep_sleep = false; + if (ASIC_IS_LOMBOK(rdev)) + pi->mclk_stutter_mode_threshold = 30000; + else + pi->mclk_stutter_mode_threshold = 0; + + pi->sram_end = SMC_RAM_END; + + return 0; +} + +void btc_dpm_fini(struct radeon_device *rdev) +{ + int i; + + for (i = 0; i < rdev->pm.dpm.num_ps; i++) { + kfree(rdev->pm.dpm.ps[i].ps_priv); + } + kfree(rdev->pm.dpm.ps); + kfree(rdev->pm.dpm.priv); +} diff --git a/drivers/gpu/drm/radeon/btc_dpm.h b/drivers/gpu/drm/radeon/btc_dpm.h new file mode 100644 index 0000000..a095d40 --- /dev/null +++ b/drivers/gpu/drm/radeon/btc_dpm.h @@ -0,0 +1,32 @@ +/* + * Copyright 2011 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ +#ifndef __BTC_DPM_H__ +#define __BTC_DPM_H__ + +#define BARTS_MGCGCGTSSMCTRL_DFLT 0x81944000 +#define TURKS_MGCGCGTSSMCTRL_DFLT 0x6e944000 +#define CAICOS_MGCGCGTSSMCTRL_DFLT 0x46944040 +#define BTC_CGULVPARAMETER_DFLT 0x00040035 +#define BTC_CGULVCONTROL_DFLT 0x00001450 + +#endif diff --git a/drivers/gpu/drm/radeon/btcd.h b/drivers/gpu/drm/radeon/btcd.h new file mode 100644 index 0000000..29e32de --- /dev/null +++ b/drivers/gpu/drm/radeon/btcd.h @@ -0,0 +1,181 @@ +/* + * Copyright 2010 Advanced Micro Devices, 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: Alex Deucher + */ +#ifndef _BTCD_H_ +#define _BTCD_H_ + +/* pm registers */ + +#define GENERAL_PWRMGT 0x63c +# define GLOBAL_PWRMGT_EN (1 << 0) +# define STATIC_PM_EN (1 << 1) +# define THERMAL_PROTECTION_DIS (1 << 2) +# define THERMAL_PROTECTION_TYPE (1 << 3) +# define ENABLE_GEN2PCIE (1 << 4) +# define ENABLE_GEN2XSP (1 << 5) +# define SW_SMIO_INDEX(x) ((x) << 6) +# define SW_SMIO_INDEX_MASK (3 << 6) +# define SW_SMIO_INDEX_SHIFT 6 +# define LOW_VOLT_D2_ACPI (1 << 8) +# define LOW_VOLT_D3_ACPI (1 << 9) +# define VOLT_PWRMGT_EN (1 << 10) +# define BACKBIAS_PAD_EN (1 << 18) +# define BACKBIAS_VALUE (1 << 19) +# define DYN_SPREAD_SPECTRUM_EN (1 << 23) +# define AC_DC_SW (1 << 24) + +#define CG_BIF_REQ_AND_RSP 0x7f4 +#define CG_CLIENT_REQ(x) ((x) << 0) +#define CG_CLIENT_REQ_MASK (0xff << 0) +#define CG_CLIENT_REQ_SHIFT 0 +#define CG_CLIENT_RESP(x) ((x) << 8) +#define CG_CLIENT_RESP_MASK (0xff << 8) +#define CG_CLIENT_RESP_SHIFT 8 +#define CLIENT_CG_REQ(x) ((x) << 16) +#define CLIENT_CG_REQ_MASK (0xff << 16) +#define CLIENT_CG_REQ_SHIFT 16 +#define CLIENT_CG_RESP(x) ((x) << 24) +#define CLIENT_CG_RESP_MASK (0xff << 24) +#define CLIENT_CG_RESP_SHIFT 24 + +#define SCLK_PSKIP_CNTL 0x8c0 +#define PSKIP_ON_ALLOW_STOP_HI(x) ((x) << 16) +#define PSKIP_ON_ALLOW_STOP_HI_MASK (0xff << 16) +#define PSKIP_ON_ALLOW_STOP_HI_SHIFT 16 + +#define CG_ULV_CONTROL 0x8c8 +#define CG_ULV_PARAMETER 0x8cc + +#define MC_ARB_DRAM_TIMING 0x2774 +#define MC_ARB_DRAM_TIMING2 0x2778 + +#define MC_ARB_RFSH_RATE 0x27b0 +#define POWERMODE0(x) ((x) << 0) +#define POWERMODE0_MASK (0xff << 0) +#define POWERMODE0_SHIFT 0 +#define POWERMODE1(x) ((x) << 8) +#define POWERMODE1_MASK (0xff << 8) +#define POWERMODE1_SHIFT 8 +#define POWERMODE2(x) ((x) << 16) +#define POWERMODE2_MASK (0xff << 16) +#define POWERMODE2_SHIFT 16 +#define POWERMODE3(x) ((x) << 24) +#define POWERMODE3_MASK (0xff << 24) +#define POWERMODE3_SHIFT 24 + +#define MC_ARB_BURST_TIME 0x2808 +#define STATE0(x) ((x) << 0) +#define STATE0_MASK (0x1f << 0) +#define STATE0_SHIFT 0 +#define STATE1(x) ((x) << 5) +#define STATE1_MASK (0x1f << 5) +#define STATE1_SHIFT 5 +#define STATE2(x) ((x) << 10) +#define STATE2_MASK (0x1f << 10) +#define STATE2_SHIFT 10 +#define STATE3(x) ((x) << 15) +#define STATE3_MASK (0x1f << 15) +#define STATE3_SHIFT 15 + +#define MC_SEQ_RAS_TIMING 0x28a0 +#define MC_SEQ_CAS_TIMING 0x28a4 +#define MC_SEQ_MISC_TIMING 0x28a8 +#define MC_SEQ_MISC_TIMING2 0x28ac + +#define MC_SEQ_RD_CTL_D0 0x28b4 +#define MC_SEQ_RD_CTL_D1 0x28b8 +#define MC_SEQ_WR_CTL_D0 0x28bc +#define MC_SEQ_WR_CTL_D1 0x28c0 + +#define MC_PMG_AUTO_CFG 0x28d4 + +#define MC_SEQ_STATUS_M 0x29f4 +# define PMG_PWRSTATE (1 << 16) + +#define MC_SEQ_MISC0 0x2a00 +#define MC_SEQ_MISC0_GDDR5_SHIFT 28 +#define MC_SEQ_MISC0_GDDR5_MASK 0xf0000000 +#define MC_SEQ_MISC0_GDDR5_VALUE 5 +#define MC_SEQ_MISC1 0x2a04 +#define MC_SEQ_RESERVE_M 0x2a08 +#define MC_PMG_CMD_EMRS 0x2a0c + +#define MC_SEQ_MISC3 0x2a2c + +#define MC_SEQ_MISC5 0x2a54 +#define MC_SEQ_MISC6 0x2a58 + +#define MC_SEQ_MISC7 0x2a64 + +#define MC_SEQ_CG 0x2a68 +#define CG_SEQ_REQ(x) ((x) << 0) +#define CG_SEQ_REQ_MASK (0xff << 0) +#define CG_SEQ_REQ_SHIFT 0 +#define CG_SEQ_RESP(x) ((x) << 8) +#define CG_SEQ_RESP_MASK (0xff << 8) +#define CG_SEQ_RESP_SHIFT 8 +#define SEQ_CG_REQ(x) ((x) << 16) +#define SEQ_CG_REQ_MASK (0xff << 16) +#define SEQ_CG_REQ_SHIFT 16 +#define SEQ_CG_RESP(x) ((x) << 24) +#define SEQ_CG_RESP_MASK (0xff << 24) +#define SEQ_CG_RESP_SHIFT 24 +#define MC_SEQ_RAS_TIMING_LP 0x2a6c +#define MC_SEQ_CAS_TIMING_LP 0x2a70 +#define MC_SEQ_MISC_TIMING_LP 0x2a74 +#define MC_SEQ_MISC_TIMING2_LP 0x2a78 +#define MC_SEQ_WR_CTL_D0_LP 0x2a7c +#define MC_SEQ_WR_CTL_D1_LP 0x2a80 +#define MC_SEQ_PMG_CMD_EMRS_LP 0x2a84 +#define MC_SEQ_PMG_CMD_MRS_LP 0x2a88 + +#define MC_PMG_CMD_MRS 0x2aac + +#define MC_SEQ_RD_CTL_D0_LP 0x2b1c +#define MC_SEQ_RD_CTL_D1_LP 0x2b20 + +#define MC_PMG_CMD_MRS1 0x2b44 +#define MC_SEQ_PMG_CMD_MRS1_LP 0x2b48 + +#define LB_SYNC_RESET_SEL 0x6b28 +#define LB_SYNC_RESET_SEL_MASK (3 << 0) +#define LB_SYNC_RESET_SEL_SHIFT 0 + +/* PCIE link stuff */ +#define PCIE_LC_SPEED_CNTL 0xa4 /* PCIE_P */ +# define LC_GEN2_EN_STRAP (1 << 0) +# define LC_TARGET_LINK_SPEED_OVERRIDE_EN (1 << 1) +# define LC_FORCE_EN_HW_SPEED_CHANGE (1 << 5) +# define LC_FORCE_DIS_HW_SPEED_CHANGE (1 << 6) +# define LC_SPEED_CHANGE_ATTEMPTS_ALLOWED_MASK (0x3 << 8) +# define LC_SPEED_CHANGE_ATTEMPTS_ALLOWED_SHIFT 3 +# define LC_CURRENT_DATA_RATE (1 << 11) +# define LC_HW_VOLTAGE_IF_CONTROL(x) ((x) << 12) +# define LC_HW_VOLTAGE_IF_CONTROL_MASK (3 << 12) +# define LC_HW_VOLTAGE_IF_CONTROL_SHIFT 12 +# define LC_VOLTAGE_TIMER_SEL_MASK (0xf << 14) +# define LC_CLR_FAILED_SPD_CHANGE_CNT (1 << 21) +# define LC_OTHER_SIDE_EVER_SENT_GEN2 (1 << 23) +# define LC_OTHER_SIDE_SUPPORTS_GEN2 (1 << 24) + +#endif diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c index c73d713..7407762 100644 --- a/drivers/gpu/drm/radeon/ni.c +++ b/drivers/gpu/drm/radeon/ni.c @@ -180,13 +180,16 @@ extern int sumo_rlc_init(struct radeon_device *rdev); MODULE_FIRMWARE("radeon/BARTS_pfp.bin"); MODULE_FIRMWARE("radeon/BARTS_me.bin"); MODULE_FIRMWARE("radeon/BARTS_mc.bin"); +MODULE_FIRMWARE("radeon/BARTS_smc.bin"); MODULE_FIRMWARE("radeon/BTC_rlc.bin"); MODULE_FIRMWARE("radeon/TURKS_pfp.bin"); MODULE_FIRMWARE("radeon/TURKS_me.bin"); MODULE_FIRMWARE("radeon/TURKS_mc.bin"); +MODULE_FIRMWARE("radeon/TURKS_smc.bin"); MODULE_FIRMWARE("radeon/CAICOS_pfp.bin"); MODULE_FIRMWARE("radeon/CAICOS_me.bin"); MODULE_FIRMWARE("radeon/CAICOS_mc.bin"); +MODULE_FIRMWARE("radeon/CAICOS_smc.bin"); MODULE_FIRMWARE("radeon/CAYMAN_pfp.bin"); MODULE_FIRMWARE("radeon/CAYMAN_me.bin"); MODULE_FIRMWARE("radeon/CAYMAN_mc.bin"); @@ -683,6 +686,7 @@ int ni_init_microcode(struct radeon_device *rdev) const char *chip_name; const char *rlc_chip_name; size_t pfp_req_size, me_req_size, rlc_req_size, mc_req_size; + size_t smc_req_size = 0; char fw_name[30]; int err;
@@ -703,6 +707,7 @@ int ni_init_microcode(struct radeon_device *rdev) me_req_size = EVERGREEN_PM4_UCODE_SIZE * 4; rlc_req_size = EVERGREEN_RLC_UCODE_SIZE * 4; mc_req_size = BTC_MC_UCODE_SIZE * 4; + smc_req_size = ALIGN(BARTS_SMC_UCODE_SIZE, 4); break; case CHIP_TURKS: chip_name = "TURKS"; @@ -711,6 +716,7 @@ int ni_init_microcode(struct radeon_device *rdev) me_req_size = EVERGREEN_PM4_UCODE_SIZE * 4; rlc_req_size = EVERGREEN_RLC_UCODE_SIZE * 4; mc_req_size = BTC_MC_UCODE_SIZE * 4; + smc_req_size = ALIGN(TURKS_SMC_UCODE_SIZE, 4); break; case CHIP_CAICOS: chip_name = "CAICOS"; @@ -719,6 +725,7 @@ int ni_init_microcode(struct radeon_device *rdev) me_req_size = EVERGREEN_PM4_UCODE_SIZE * 4; rlc_req_size = EVERGREEN_RLC_UCODE_SIZE * 4; mc_req_size = BTC_MC_UCODE_SIZE * 4; + smc_req_size = ALIGN(CAICOS_SMC_UCODE_SIZE, 4); break; case CHIP_CAYMAN: chip_name = "CAYMAN"; @@ -789,6 +796,20 @@ int ni_init_microcode(struct radeon_device *rdev) err = -EINVAL; } } + + if ((rdev->family >= CHIP_BARTS) && (rdev->family <= CHIP_CAICOS)) { + snprintf(fw_name, sizeof(fw_name), "radeon/%s_smc.bin", chip_name); + err = request_firmware(&rdev->smc_fw, fw_name, &pdev->dev); + if (err) + goto out; + if (rdev->smc_fw->size != smc_req_size) { + printk(KERN_ERR + "ni_mc: Bogus length %zu in firmware "%s"\n", + rdev->mc_fw->size, fw_name); + err = -EINVAL; + } + } + out: platform_device_unregister(pdev);
diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index 077448f..20f65ad 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1638,6 +1638,18 @@ static struct radeon_asic btc_asic = { .set_uvd_clocks = &evergreen_set_uvd_clocks, .get_temperature = &evergreen_get_temp, }, + .dpm = { + .init = &btc_dpm_init, + .setup_asic = &btc_dpm_setup_asic, + .enable = &btc_dpm_enable, + .disable = &btc_dpm_disable, + .set_power_state = &btc_dpm_set_power_state, + .display_configuration_changed = &cypress_dpm_display_configuration_changed, + .fini = &btc_dpm_fini, + .get_sclk = &rv770_dpm_get_sclk, + .get_mclk = &rv770_dpm_get_mclk, + .print_power_state = &rv770_dpm_print_power_state, + }, .pflip = { .pre_page_flip = &evergreen_pre_page_flip, .page_flip = &evergreen_page_flip, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 1f25b683..214429b 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -530,6 +530,12 @@ void cypress_dpm_disable(struct radeon_device *rdev); int cypress_dpm_set_power_state(struct radeon_device *rdev); void cypress_dpm_display_configuration_changed(struct radeon_device *rdev); void cypress_dpm_fini(struct radeon_device *rdev); +int btc_dpm_init(struct radeon_device *rdev); +void btc_dpm_setup_asic(struct radeon_device *rdev); +int btc_dpm_enable(struct radeon_device *rdev); +void btc_dpm_disable(struct radeon_device *rdev); +int btc_dpm_set_power_state(struct radeon_device *rdev); +void btc_dpm_fini(struct radeon_device *rdev);
/* * cayman diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index d2265f4..0a770b5 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -1049,6 +1049,9 @@ int radeon_pm_init(struct radeon_device *rdev) case CHIP_JUNIPER: case CHIP_CYPRESS: case CHIP_HEMLOCK: + case CHIP_BARTS: + case CHIP_TURKS: + case CHIP_CAICOS: if (radeon_dpm == 1) rdev->pm.pm_method = PM_METHOD_DPM; else diff --git a/drivers/gpu/drm/radeon/radeon_ucode.h b/drivers/gpu/drm/radeon/radeon_ucode.h index cb9c813..e592e27 100644 --- a/drivers/gpu/drm/radeon/radeon_ucode.h +++ b/drivers/gpu/drm/radeon/radeon_ucode.h @@ -85,4 +85,19 @@ #define CYPRESS_SMC_INT_VECTOR_START 0xffc0 #define CYPRESS_SMC_INT_VECTOR_SIZE 0x0040
+#define BARTS_SMC_UCODE_START 0x0100 +#define BARTS_SMC_UCODE_SIZE 0x6107 +#define BARTS_SMC_INT_VECTOR_START 0xffc0 +#define BARTS_SMC_INT_VECTOR_SIZE 0x0040 + +#define TURKS_SMC_UCODE_START 0x0100 +#define TURKS_SMC_UCODE_SIZE 0x605b +#define TURKS_SMC_INT_VECTOR_START 0xffc0 +#define TURKS_SMC_INT_VECTOR_SIZE 0x0040 + +#define CAICOS_SMC_UCODE_START 0x0100 +#define CAICOS_SMC_UCODE_SIZE 0x5fbd +#define CAICOS_SMC_INT_VECTOR_START 0xffc0 +#define CAICOS_SMC_INT_VECTOR_SIZE 0x0040 + #endif diff --git a/drivers/gpu/drm/radeon/rv770_smc.c b/drivers/gpu/drm/radeon/rv770_smc.c index 168aedb..0078c59 100644 --- a/drivers/gpu/drm/radeon/rv770_smc.c +++ b/drivers/gpu/drm/radeon/rv770_smc.c @@ -194,6 +194,66 @@ static const u8 cypress_smc_int_vectors[] = 0x04, 0xF6, 0x04, 0xF6 };
+static const u8 barts_smc_int_vectors[] = +{ + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x12, 0xAA, + 0x0C, 0x2F, 0x15, 0xF6, + 0x15, 0xF6, 0x05, 0x0A, + 0x05, 0x0A, 0x05, 0x0A +}; + +static const u8 turks_smc_int_vectors[] = +{ + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x12, 0xAA, + 0x0C, 0x2F, 0x15, 0xF6, + 0x15, 0xF6, 0x05, 0x0A, + 0x05, 0x0A, 0x05, 0x0A +}; + +static const u8 caicos_smc_int_vectors[] = +{ + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x0C, 0x14, + 0x0C, 0x14, 0x12, 0xAA, + 0x0C, 0x2F, 0x15, 0xF6, + 0x15, 0xF6, 0x05, 0x0A, + 0x05, 0x0A, 0x05, 0x0A +}; + int rv770_set_smc_sram_address(struct radeon_device *rdev, u16 smc_address, u16 limit) { @@ -463,6 +523,27 @@ int rv770_load_smc_ucode(struct radeon_device *rdev, int_vect_start_address = CYPRESS_SMC_INT_VECTOR_START; int_vect_size = CYPRESS_SMC_INT_VECTOR_SIZE; break; + case CHIP_BARTS: + ucode_start_address = BARTS_SMC_UCODE_START; + ucode_size = BARTS_SMC_UCODE_SIZE; + int_vect = (const u8 *)&barts_smc_int_vectors; + int_vect_start_address = BARTS_SMC_INT_VECTOR_START; + int_vect_size = BARTS_SMC_INT_VECTOR_SIZE; + break; + case CHIP_TURKS: + ucode_start_address = TURKS_SMC_UCODE_START; + ucode_size = TURKS_SMC_UCODE_SIZE; + int_vect = (const u8 *)&turks_smc_int_vectors; + int_vect_start_address = TURKS_SMC_INT_VECTOR_START; + int_vect_size = TURKS_SMC_INT_VECTOR_SIZE; + break; + case CHIP_CAICOS: + ucode_start_address = CAICOS_SMC_UCODE_START; + ucode_size = CAICOS_SMC_UCODE_SIZE; + int_vect = (const u8 *)&caicos_smc_int_vectors; + int_vect_start_address = CAICOS_SMC_INT_VECTOR_START; + int_vect_size = CAICOS_SMC_INT_VECTOR_SIZE; + break; default: DRM_ERROR("unknown asic in smc ucode loader\n"); BUG();
From: Alex Deucher alexander.deucher@amd.com
This adds dpm support for sumo asics. This includes: - clockgating - powergating - dynamic engine clock scaling - dynamic voltage scaling
set radeon.dpm=1 to enable it.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/Makefile | 2 +- drivers/gpu/drm/radeon/radeon_asic.c | 12 + drivers/gpu/drm/radeon/radeon_asic.h | 11 + drivers/gpu/drm/radeon/radeon_pm.c | 3 + drivers/gpu/drm/radeon/sumo_dpm.c | 1677 ++++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/sumo_dpm.h | 199 ++++ drivers/gpu/drm/radeon/sumo_smc.c | 224 +++++ drivers/gpu/drm/radeon/sumod.h | 362 ++++++++ 8 files changed, 2489 insertions(+), 1 deletions(-) create mode 100644 drivers/gpu/drm/radeon/sumo_dpm.c create mode 100644 drivers/gpu/drm/radeon/sumo_dpm.h create mode 100644 drivers/gpu/drm/radeon/sumo_smc.c create mode 100644 drivers/gpu/drm/radeon/sumod.h
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index af3dd8f..7c77e1d 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile @@ -78,7 +78,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \ atombios_encoders.o radeon_semaphore.o radeon_sa.o atombios_i2c.o si.o \ si_blit_shaders.o radeon_prime.o radeon_uvd.o cik.o cik_blit_shaders.o \ r600_dpm.o rs780_dpm.o rv6xx_dpm.o rv770_dpm.o rv730_dpm.o rv740_dpm.o \ - rv770_smc.o cypress_dpm.o btc_dpm.o + rv770_smc.o cypress_dpm.o btc_dpm.o sumo_dpm.o sumo_smc.o
radeon-$(CONFIG_COMPAT) += radeon_ioc32.o radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index 20f65ad..8b541b4 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1539,6 +1539,18 @@ static struct radeon_asic sumo_asic = { .set_uvd_clocks = &sumo_set_uvd_clocks, .get_temperature = &sumo_get_temp, }, + .dpm = { + .init = &sumo_dpm_init, + .setup_asic = &sumo_dpm_setup_asic, + .enable = &sumo_dpm_enable, + .disable = &sumo_dpm_disable, + .set_power_state = &sumo_dpm_set_power_state, + .display_configuration_changed = &sumo_dpm_display_configuration_changed, + .fini = &sumo_dpm_fini, + .get_sclk = &sumo_dpm_get_sclk, + .get_mclk = &sumo_dpm_get_mclk, + .print_power_state = &sumo_dpm_print_power_state, + }, .pflip = { .pre_page_flip = &evergreen_pre_page_flip, .page_flip = &evergreen_page_flip, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 214429b..f6c0984 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -536,6 +536,17 @@ int btc_dpm_enable(struct radeon_device *rdev); void btc_dpm_disable(struct radeon_device *rdev); int btc_dpm_set_power_state(struct radeon_device *rdev); void btc_dpm_fini(struct radeon_device *rdev); +int sumo_dpm_init(struct radeon_device *rdev); +int sumo_dpm_enable(struct radeon_device *rdev); +void sumo_dpm_disable(struct radeon_device *rdev); +int sumo_dpm_set_power_state(struct radeon_device *rdev); +void sumo_dpm_setup_asic(struct radeon_device *rdev); +void sumo_dpm_display_configuration_changed(struct radeon_device *rdev); +void sumo_dpm_fini(struct radeon_device *rdev); +u32 sumo_dpm_get_sclk(struct radeon_device *rdev, bool low); +u32 sumo_dpm_get_mclk(struct radeon_device *rdev, bool low); +void sumo_dpm_print_power_state(struct radeon_device *rdev, + struct radeon_ps *ps);
/* * cayman diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 0a770b5..b924bcc1 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -1049,6 +1049,9 @@ int radeon_pm_init(struct radeon_device *rdev) case CHIP_JUNIPER: case CHIP_CYPRESS: case CHIP_HEMLOCK: + case CHIP_PALM: + case CHIP_SUMO: + case CHIP_SUMO2: case CHIP_BARTS: case CHIP_TURKS: case CHIP_CAICOS: diff --git a/drivers/gpu/drm/radeon/sumo_dpm.c b/drivers/gpu/drm/radeon/sumo_dpm.c new file mode 100644 index 0000000..4f49450 --- /dev/null +++ b/drivers/gpu/drm/radeon/sumo_dpm.c @@ -0,0 +1,1677 @@ +/* + * Copyright 2012 Advanced Micro Devices, 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. + * + */ + +#include "drmP.h" +#include "radeon.h" +#include "sumod.h" +#include "r600_dpm.h" +#include "cypress_dpm.h" +#include "sumo_dpm.h" +#include "atom.h" + +#define SUMO_MAX_DEEPSLEEP_DIVIDER_ID 5 +#define SUMO_MINIMUM_ENGINE_CLOCK 800 +#define BOOST_DPM_LEVEL 7 + +static const u32 sumo_utc[SUMO_PM_NUMBER_OF_TC] = +{ + SUMO_UTC_DFLT_00, + SUMO_UTC_DFLT_01, + SUMO_UTC_DFLT_02, + SUMO_UTC_DFLT_03, + SUMO_UTC_DFLT_04, + SUMO_UTC_DFLT_05, + SUMO_UTC_DFLT_06, + SUMO_UTC_DFLT_07, + SUMO_UTC_DFLT_08, + SUMO_UTC_DFLT_09, + SUMO_UTC_DFLT_10, + SUMO_UTC_DFLT_11, + SUMO_UTC_DFLT_12, + SUMO_UTC_DFLT_13, + SUMO_UTC_DFLT_14, +}; + +static const u32 sumo_dtc[SUMO_PM_NUMBER_OF_TC] = +{ + SUMO_DTC_DFLT_00, + SUMO_DTC_DFLT_01, + SUMO_DTC_DFLT_02, + SUMO_DTC_DFLT_03, + SUMO_DTC_DFLT_04, + SUMO_DTC_DFLT_05, + SUMO_DTC_DFLT_06, + SUMO_DTC_DFLT_07, + SUMO_DTC_DFLT_08, + SUMO_DTC_DFLT_09, + SUMO_DTC_DFLT_10, + SUMO_DTC_DFLT_11, + SUMO_DTC_DFLT_12, + SUMO_DTC_DFLT_13, + SUMO_DTC_DFLT_14, +}; + +struct sumo_ps *sumo_get_ps(struct radeon_ps *rps) +{ + struct sumo_ps *ps = rps->ps_priv; + + return ps; +} + +struct sumo_power_info *sumo_get_pi(struct radeon_device *rdev) +{ + struct sumo_power_info *pi = rdev->pm.dpm.priv; + + return pi; +} + +u32 sumo_get_xclk(struct radeon_device *rdev) +{ + return rdev->clock.spll.reference_freq; +} + +static void sumo_gfx_clockgating_enable(struct radeon_device *rdev, bool enable) +{ + if (enable) + WREG32_P(SCLK_PWRMGT_CNTL, DYN_GFX_CLK_OFF_EN, ~DYN_GFX_CLK_OFF_EN); + else { + WREG32_P(SCLK_PWRMGT_CNTL, 0, ~DYN_GFX_CLK_OFF_EN); + WREG32_P(SCLK_PWRMGT_CNTL, GFX_CLK_FORCE_ON, ~GFX_CLK_FORCE_ON); + WREG32_P(SCLK_PWRMGT_CNTL, 0, ~GFX_CLK_FORCE_ON); + RREG32(GB_ADDR_CONFIG); + } +} + +#define CGCG_CGTT_LOCAL0_MASK 0xE5BFFFFF +#define CGCG_CGTT_LOCAL1_MASK 0xEFFF07FF + +static void sumo_mg_clockgating_enable(struct radeon_device *rdev, bool enable) +{ + u32 local0; + u32 local1; + + local0 = RREG32(CG_CGTT_LOCAL_0); + local1 = RREG32(CG_CGTT_LOCAL_1); + + if (enable) { + WREG32(CG_CGTT_LOCAL_0, (0 & CGCG_CGTT_LOCAL0_MASK) | (local0 & ~CGCG_CGTT_LOCAL0_MASK) ); + WREG32(CG_CGTT_LOCAL_1, (0 & CGCG_CGTT_LOCAL1_MASK) | (local1 & ~CGCG_CGTT_LOCAL1_MASK) ); + } else { + WREG32(CG_CGTT_LOCAL_0, (0xFFFFFFFF & CGCG_CGTT_LOCAL0_MASK) | (local0 & ~CGCG_CGTT_LOCAL0_MASK) ); + WREG32(CG_CGTT_LOCAL_1, (0xFFFFCFFF & CGCG_CGTT_LOCAL1_MASK) | (local1 & ~CGCG_CGTT_LOCAL1_MASK) ); + } +} + +static void sumo_program_git(struct radeon_device *rdev) +{ + u32 p, u; + u32 xclk = sumo_get_xclk(rdev); + + r600_calculate_u_and_p(SUMO_GICST_DFLT, + xclk, 16, &p, &u); + + WREG32_P(CG_GIT, CG_GICST(p), ~CG_GICST_MASK); +} + +static void sumo_program_grsd(struct radeon_device *rdev) +{ + u32 p, u; + u32 xclk = sumo_get_xclk(rdev); + u32 grs = 256 * 25 / 100; + + r600_calculate_u_and_p(1, xclk, 14, &p, &u); + + WREG32(CG_GCOOR, PHC(grs) | SDC(p) | SU(u)); +} + +static void sumo_gfx_clockgating_initialize(struct radeon_device *rdev) +{ + sumo_program_git(rdev); + sumo_program_grsd(rdev); +} + +static void sumo_gfx_powergating_initialize(struct radeon_device *rdev) +{ + u32 rcu_pwr_gating_cntl; + u32 p, u; + u32 p_c, p_p, d_p; + u32 r_t, i_t; + u32 xclk = sumo_get_xclk(rdev); + + if (rdev->family == CHIP_PALM) { + p_c = 4; + d_p = 10; + r_t = 10; + i_t = 4; + p_p = 50 + 1000/200 + 6 * 32; + } else { + p_c = 16; + d_p = 50; + r_t = 50; + i_t = 50; + p_p = 113; + } + + WREG32(CG_SCRATCH2, 0x01B60A17); + + r600_calculate_u_and_p(SUMO_GFXPOWERGATINGT_DFLT, + xclk, 16, &p, &u); + + WREG32_P(CG_PWR_GATING_CNTL, PGP(p) | PGU(u), + ~(PGP_MASK | PGU_MASK)); + + r600_calculate_u_and_p(SUMO_VOLTAGEDROPT_DFLT, + xclk, 16, &p, &u); + + WREG32_P(CG_CG_VOLTAGE_CNTL, PGP(p) | PGU(u), + ~(PGP_MASK | PGU_MASK)); + + if (rdev->family == CHIP_PALM) { + WREG32_RCU(RCU_PWR_GATING_SEQ0, 0x10103210); + WREG32_RCU(RCU_PWR_GATING_SEQ1, 0x10101010); + } else { + WREG32_RCU(RCU_PWR_GATING_SEQ0, 0x76543210); + WREG32_RCU(RCU_PWR_GATING_SEQ1, 0xFEDCBA98); + } + + rcu_pwr_gating_cntl = RREG32_RCU(RCU_PWR_GATING_CNTL); + rcu_pwr_gating_cntl &= + ~(RSVD_MASK | PCV_MASK | PGS_MASK); + rcu_pwr_gating_cntl |= PCV(p_c) | PGS(1) | PWR_GATING_EN; + if (rdev->family == CHIP_PALM) { + rcu_pwr_gating_cntl &= ~PCP_MASK; + rcu_pwr_gating_cntl |= PCP(0x77); + } + WREG32_RCU(RCU_PWR_GATING_CNTL, rcu_pwr_gating_cntl); + + rcu_pwr_gating_cntl = RREG32_RCU(RCU_PWR_GATING_CNTL_2); + rcu_pwr_gating_cntl &= ~(MPPU_MASK | MPPD_MASK); + rcu_pwr_gating_cntl |= MPPU(p_p) | MPPD(50); + WREG32_RCU(RCU_PWR_GATING_CNTL_2, rcu_pwr_gating_cntl); + + rcu_pwr_gating_cntl = RREG32_RCU(RCU_PWR_GATING_CNTL_3); + rcu_pwr_gating_cntl &= ~(DPPU_MASK | DPPD_MASK); + rcu_pwr_gating_cntl |= DPPU(d_p) | DPPD(50); + WREG32_RCU(RCU_PWR_GATING_CNTL_3, rcu_pwr_gating_cntl); + + rcu_pwr_gating_cntl = RREG32_RCU(RCU_PWR_GATING_CNTL_4); + rcu_pwr_gating_cntl &= ~(RT_MASK | IT_MASK); + rcu_pwr_gating_cntl |= RT(r_t) | IT(i_t); + WREG32_RCU(RCU_PWR_GATING_CNTL_4, rcu_pwr_gating_cntl); + + if (rdev->family == CHIP_PALM) + WREG32_RCU(RCU_PWR_GATING_CNTL_5, 0xA02); + + sumo_smu_pg_init(rdev); + + rcu_pwr_gating_cntl = RREG32_RCU(RCU_PWR_GATING_CNTL); + rcu_pwr_gating_cntl &= + ~(RSVD_MASK | PCV_MASK | PGS_MASK); + rcu_pwr_gating_cntl |= PCV(p_c) | PGS(4) | PWR_GATING_EN; + if (rdev->family == CHIP_PALM) { + rcu_pwr_gating_cntl &= ~PCP_MASK; + rcu_pwr_gating_cntl |= PCP(0x77); + } + WREG32_RCU(RCU_PWR_GATING_CNTL, rcu_pwr_gating_cntl); + + if (rdev->family == CHIP_PALM) { + rcu_pwr_gating_cntl = RREG32_RCU(RCU_PWR_GATING_CNTL_2); + rcu_pwr_gating_cntl &= ~(MPPU_MASK | MPPD_MASK); + rcu_pwr_gating_cntl |= MPPU(113) | MPPD(50); + WREG32_RCU(RCU_PWR_GATING_CNTL_2, rcu_pwr_gating_cntl); + + rcu_pwr_gating_cntl = RREG32_RCU(RCU_PWR_GATING_CNTL_3); + rcu_pwr_gating_cntl &= ~(DPPU_MASK | DPPD_MASK); + rcu_pwr_gating_cntl |= DPPU(16) | DPPD(50); + WREG32_RCU(RCU_PWR_GATING_CNTL_3, rcu_pwr_gating_cntl); + } + + sumo_smu_pg_init(rdev); + + rcu_pwr_gating_cntl = RREG32_RCU(RCU_PWR_GATING_CNTL); + rcu_pwr_gating_cntl &= + ~(RSVD_MASK | PCV_MASK | PGS_MASK); + rcu_pwr_gating_cntl |= PGS(5) | PWR_GATING_EN; + + if (rdev->family == CHIP_PALM) { + rcu_pwr_gating_cntl |= PCV(4); + rcu_pwr_gating_cntl &= ~PCP_MASK; + rcu_pwr_gating_cntl |= PCP(0x77); + } else + rcu_pwr_gating_cntl |= PCV(11); + WREG32_RCU(RCU_PWR_GATING_CNTL, rcu_pwr_gating_cntl); + + if (rdev->family == CHIP_PALM) { + rcu_pwr_gating_cntl = RREG32_RCU(RCU_PWR_GATING_CNTL_2); + rcu_pwr_gating_cntl &= ~(MPPU_MASK | MPPD_MASK); + rcu_pwr_gating_cntl |= MPPU(113) | MPPD(50); + WREG32_RCU(RCU_PWR_GATING_CNTL_2, rcu_pwr_gating_cntl); + + rcu_pwr_gating_cntl = RREG32_RCU(RCU_PWR_GATING_CNTL_3); + rcu_pwr_gating_cntl &= ~(DPPU_MASK | DPPD_MASK); + rcu_pwr_gating_cntl |= DPPU(22) | DPPD(50); + WREG32_RCU(RCU_PWR_GATING_CNTL_3, rcu_pwr_gating_cntl); + } + + sumo_smu_pg_init(rdev); +} + +static void sumo_gfx_powergating_enable(struct radeon_device *rdev, bool enable) +{ + if (enable) + WREG32_P(CG_PWR_GATING_CNTL, DYN_PWR_DOWN_EN, ~DYN_PWR_DOWN_EN); + else { + WREG32_P(CG_PWR_GATING_CNTL, 0, ~DYN_PWR_DOWN_EN); + RREG32(GB_ADDR_CONFIG); + } +} + +static int sumo_enable_clock_power_gating(struct radeon_device *rdev) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + + if (pi->enable_gfx_clock_gating) + sumo_gfx_clockgating_initialize(rdev); + if (pi->enable_gfx_power_gating) + sumo_gfx_powergating_initialize(rdev); + if (pi->enable_mg_clock_gating) + sumo_mg_clockgating_enable(rdev, true); + if (pi->enable_gfx_clock_gating) + sumo_gfx_clockgating_enable(rdev, true); + if (pi->enable_gfx_power_gating) + sumo_gfx_powergating_enable(rdev, true); + + return 0; +} + +static void sumo_disable_clock_power_gating(struct radeon_device *rdev) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + + if (pi->enable_gfx_clock_gating) + sumo_gfx_clockgating_enable(rdev, false); + if (pi->enable_gfx_power_gating) + sumo_gfx_powergating_enable(rdev, false); + if (pi->enable_mg_clock_gating) + sumo_mg_clockgating_enable(rdev, false); +} + +static void sumo_calculate_bsp(struct radeon_device *rdev, + u32 high_clk) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + u32 xclk = sumo_get_xclk(rdev); + + pi->pasi = 65535 * 100 / high_clk; + pi->asi = 65535 * 100 / high_clk; + + r600_calculate_u_and_p(pi->asi, + xclk, 16, &pi->bsp, &pi->bsu); + + r600_calculate_u_and_p(pi->pasi, + xclk, 16, &pi->pbsp, &pi->pbsu); + + pi->dsp = BSP(pi->bsp) | BSU(pi->bsu); + pi->psp = BSP(pi->pbsp) | BSU(pi->pbsu); +} + +static void sumo_init_bsp(struct radeon_device *rdev) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + + WREG32(CG_BSP_0, pi->psp); +} + + +static void sumo_program_bsp(struct radeon_device *rdev) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + struct sumo_ps *ps = sumo_get_ps(rdev->pm.dpm.requested_ps); + u32 i; + u32 highest_engine_clock = ps->levels[ps->num_levels - 1].sclk; + + if (ps->flags & SUMO_POWERSTATE_FLAGS_BOOST_STATE) + highest_engine_clock = pi->boost_pl.sclk; + + sumo_calculate_bsp(rdev, highest_engine_clock); + + for (i = 0; i < ps->num_levels - 1; i++) + WREG32(CG_BSP_0 + (i * 4), pi->dsp); + + WREG32(CG_BSP_0 + (i * 4), pi->psp); + + if (ps->flags & SUMO_POWERSTATE_FLAGS_BOOST_STATE) + WREG32(CG_BSP_0 + (BOOST_DPM_LEVEL * 4), pi->psp); +} + +static void sumo_write_at(struct radeon_device *rdev, + u32 index, u32 value) +{ + if (index == 0) + WREG32(CG_AT_0, value); + else if (index == 1) + WREG32(CG_AT_1, value); + else if (index == 2) + WREG32(CG_AT_2, value); + else if (index == 3) + WREG32(CG_AT_3, value); + else if (index == 4) + WREG32(CG_AT_4, value); + else if (index == 5) + WREG32(CG_AT_5, value); + else if (index == 6) + WREG32(CG_AT_6, value); + else if (index == 7) + WREG32(CG_AT_7, value); +} + +static void sumo_program_at(struct radeon_device *rdev) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + struct sumo_ps *ps = sumo_get_ps(rdev->pm.dpm.requested_ps); + u32 asi; + u32 i; + u32 m_a; + u32 a_t; + u32 r[SUMO_MAX_HARDWARE_POWERLEVELS]; + u32 l[SUMO_MAX_HARDWARE_POWERLEVELS]; + + r[0] = SUMO_R_DFLT0; + r[1] = SUMO_R_DFLT1; + r[2] = SUMO_R_DFLT2; + r[3] = SUMO_R_DFLT3; + r[4] = SUMO_R_DFLT4; + + l[0] = SUMO_L_DFLT0; + l[1] = SUMO_L_DFLT1; + l[2] = SUMO_L_DFLT2; + l[3] = SUMO_L_DFLT3; + l[4] = SUMO_L_DFLT4; + + for (i = 0; i < ps->num_levels; i++) { + asi = (i == ps->num_levels - 1) ? pi->pasi : pi->asi; + + m_a = asi * ps->levels[i].sclk / 100; + + a_t = CG_R(m_a * r[i] / 100) | CG_L(m_a * l[i] / 100); + + sumo_write_at(rdev, i, a_t); + } + + if (ps->flags & SUMO_POWERSTATE_FLAGS_BOOST_STATE) { + asi = pi->pasi; + + m_a = asi * pi->boost_pl.sclk / 100; + + a_t = CG_R(m_a * r[ps->num_levels - 1] / 100) | + CG_L(m_a * l[ps->num_levels - 1] / 100); + + sumo_write_at(rdev, BOOST_DPM_LEVEL, a_t); + } +} + +static void sumo_program_tp(struct radeon_device *rdev) +{ + int i; + enum r600_td td = R600_TD_DFLT; + + for (i = 0; i < SUMO_PM_NUMBER_OF_TC; i++) { + WREG32_P(CG_FFCT_0 + (i * 4), UTC_0(sumo_utc[i]), ~UTC_0_MASK); + WREG32_P(CG_FFCT_0 + (i * 4), DTC_0(sumo_dtc[i]), ~DTC_0_MASK); + } + + if (td == R600_TD_AUTO) + WREG32_P(SCLK_PWRMGT_CNTL, 0, ~FIR_FORCE_TREND_SEL); + else + WREG32_P(SCLK_PWRMGT_CNTL, FIR_FORCE_TREND_SEL, ~FIR_FORCE_TREND_SEL); + + if (td == R600_TD_UP) + WREG32_P(SCLK_PWRMGT_CNTL, 0, ~FIR_TREND_MODE); + + if (td == R600_TD_DOWN) + WREG32_P(SCLK_PWRMGT_CNTL, FIR_TREND_MODE, ~FIR_TREND_MODE); +} + +static void sumo_program_vc(struct radeon_device *rdev) +{ + WREG32(CG_FTV, SUMO_VRC_DFLT); +} + +static void sumo_clear_vc(struct radeon_device *rdev) +{ + WREG32(CG_FTV, 0); +} + +static void sumo_program_sstp(struct radeon_device *rdev) +{ + u32 p, u; + u32 xclk = sumo_get_xclk(rdev); + + r600_calculate_u_and_p(SUMO_SST_DFLT, + xclk, 16, &p, &u); + + WREG32(CG_SSP, SSTU(u) | SST(p)); +} + +static void sumo_set_divider_value(struct radeon_device *rdev, + u32 index, u32 divider) +{ + u32 reg_index = index / 4; + u32 field_index = index % 4; + + if (field_index == 0) + WREG32_P(CG_SCLK_DPM_CTRL + (reg_index * 4), + SCLK_FSTATE_0_DIV(divider), ~SCLK_FSTATE_0_DIV_MASK); + else if (field_index == 1) + WREG32_P(CG_SCLK_DPM_CTRL + (reg_index * 4), + SCLK_FSTATE_1_DIV(divider), ~SCLK_FSTATE_1_DIV_MASK); + else if (field_index == 2) + WREG32_P(CG_SCLK_DPM_CTRL + (reg_index * 4), + SCLK_FSTATE_2_DIV(divider), ~SCLK_FSTATE_2_DIV_MASK); + else if (field_index == 3) + WREG32_P(CG_SCLK_DPM_CTRL + (reg_index * 4), + SCLK_FSTATE_3_DIV(divider), ~SCLK_FSTATE_3_DIV_MASK); +} + +static void sumo_set_ds_dividers(struct radeon_device *rdev, + u32 index, u32 divider) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + + if (pi->enable_sclk_ds) { + u32 dpm_ctrl = RREG32(CG_SCLK_DPM_CTRL_6); + + dpm_ctrl &= ~(0x7 << (index * 3)); + dpm_ctrl |= (divider << (index * 3)); + WREG32(CG_SCLK_DPM_CTRL_6, dpm_ctrl); + } +} + +static void sumo_set_ss_dividers(struct radeon_device *rdev, + u32 index, u32 divider) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + + if (pi->enable_sclk_ds) { + u32 dpm_ctrl = RREG32(CG_SCLK_DPM_CTRL_11); + + dpm_ctrl &= ~(0x7 << (index * 3)); + dpm_ctrl |= (divider << (index * 3)); + WREG32(CG_SCLK_DPM_CTRL_11, dpm_ctrl); + } +} + +static void sumo_set_vid(struct radeon_device *rdev, u32 index, u32 vid) +{ + u32 voltage_cntl = RREG32(CG_DPM_VOLTAGE_CNTL); + + voltage_cntl &= ~(DPM_STATE0_LEVEL_MASK << (index * 2)); + voltage_cntl |= (vid << (DPM_STATE0_LEVEL_SHIFT + index * 2)); + WREG32(CG_DPM_VOLTAGE_CNTL, voltage_cntl); +} + +static void sumo_set_allos_gnb_slow(struct radeon_device *rdev, u32 index, u32 gnb_slow) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + u32 temp = gnb_slow; + u32 cg_sclk_dpm_ctrl_3; + + if (pi->driver_nbps_policy_disable) + temp = 1; + + cg_sclk_dpm_ctrl_3 = RREG32(CG_SCLK_DPM_CTRL_3); + cg_sclk_dpm_ctrl_3 &= ~(GNB_SLOW_FSTATE_0_MASK << index); + cg_sclk_dpm_ctrl_3 |= (temp << (GNB_SLOW_FSTATE_0_SHIFT + index)); + + WREG32(CG_SCLK_DPM_CTRL_3, cg_sclk_dpm_ctrl_3); +} + +static void sumo_program_power_level(struct radeon_device *rdev, + struct sumo_pl *pl, u32 index) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + int ret; + struct atom_clock_dividers dividers; + u32 ds_en = RREG32(DEEP_SLEEP_CNTL) & ENABLE_DS; + + ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM, + pl->sclk, false, ÷rs); + if (ret) + return; + + sumo_set_divider_value(rdev, index, dividers.post_div); + + sumo_set_vid(rdev, index, pl->vddc_index); + + if (pl->ss_divider_index == 0 || pl->ds_divider_index == 0) { + if (ds_en) + WREG32_P(DEEP_SLEEP_CNTL, 0, ~ENABLE_DS); + } else { + sumo_set_ss_dividers(rdev, index, pl->ss_divider_index); + sumo_set_ds_dividers(rdev, index, pl->ds_divider_index); + + if (!ds_en) + WREG32_P(DEEP_SLEEP_CNTL, ENABLE_DS, ~ENABLE_DS); + } + + sumo_set_allos_gnb_slow(rdev, index, pl->allow_gnb_slow); + + if (pi->enable_boost) + sumo_set_tdp_limit(rdev, index, pl->sclk_dpm_tdp_limit); +} + +static void sumo_power_level_enable(struct radeon_device *rdev, u32 index, bool enable) +{ + u32 reg_index = index / 4; + u32 field_index = index % 4; + + if (field_index == 0) + WREG32_P(CG_SCLK_DPM_CTRL + (reg_index * 4), + enable ? SCLK_FSTATE_0_VLD : 0, ~SCLK_FSTATE_0_VLD); + else if (field_index == 1) + WREG32_P(CG_SCLK_DPM_CTRL + (reg_index * 4), + enable ? SCLK_FSTATE_1_VLD : 0, ~SCLK_FSTATE_1_VLD); + else if (field_index == 2) + WREG32_P(CG_SCLK_DPM_CTRL + (reg_index * 4), + enable ? SCLK_FSTATE_2_VLD : 0, ~SCLK_FSTATE_2_VLD); + else if (field_index == 3) + WREG32_P(CG_SCLK_DPM_CTRL + (reg_index * 4), + enable ? SCLK_FSTATE_3_VLD : 0, ~SCLK_FSTATE_3_VLD); +} + +static bool sumo_dpm_enabled(struct radeon_device *rdev) +{ + if (RREG32(CG_SCLK_DPM_CTRL_3) & DPM_SCLK_ENABLE) + return true; + else + return false; +} + +static void sumo_start_dpm(struct radeon_device *rdev) +{ + WREG32_P(CG_SCLK_DPM_CTRL_3, DPM_SCLK_ENABLE, ~DPM_SCLK_ENABLE); +} + +static void sumo_stop_dpm(struct radeon_device *rdev) +{ + WREG32_P(CG_SCLK_DPM_CTRL_3, 0, ~DPM_SCLK_ENABLE); +} + +static void sumo_set_forced_mode(struct radeon_device *rdev, bool enable) +{ + if (enable) + WREG32_P(CG_SCLK_DPM_CTRL_3, FORCE_SCLK_STATE_EN, ~FORCE_SCLK_STATE_EN); + else + WREG32_P(CG_SCLK_DPM_CTRL_3, 0, ~FORCE_SCLK_STATE_EN); +} + +static void sumo_set_forced_mode_enabled(struct radeon_device *rdev) +{ + int i; + + sumo_set_forced_mode(rdev, true); + for (i = 0; i < rdev->usec_timeout; i++) { + if (RREG32(CG_SCLK_STATUS) & SCLK_OVERCLK_DETECT) + break; + udelay(1); + } +} + +static void sumo_wait_for_level_0(struct radeon_device *rdev) +{ + int i; + + for (i = 0; i < rdev->usec_timeout; i++) { + if ((RREG32(TARGET_AND_CURRENT_PROFILE_INDEX) & CURR_SCLK_INDEX_MASK) == 0) + break; + udelay(1); + } + for (i = 0; i < rdev->usec_timeout; i++) { + if ((RREG32(TARGET_AND_CURRENT_PROFILE_INDEX) & CURR_INDEX_MASK) == 0) + break; + udelay(1); + } +} + +static void sumo_set_forced_mode_disabled(struct radeon_device *rdev) +{ + sumo_set_forced_mode(rdev, false); +} + +static void sumo_enable_power_level_0(struct radeon_device *rdev) +{ + sumo_power_level_enable(rdev, 0, true); +} + +static void sumo_patch_boost_state(struct radeon_device *rdev) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + struct sumo_ps *new_ps = sumo_get_ps(rdev->pm.dpm.requested_ps); + + if (new_ps->flags & SUMO_POWERSTATE_FLAGS_BOOST_STATE) { + pi->boost_pl = new_ps->levels[new_ps->num_levels - 1]; + pi->boost_pl.sclk = pi->sys_info.boost_sclk; + pi->boost_pl.vddc_index = pi->sys_info.boost_vid_2bit; + pi->boost_pl.sclk_dpm_tdp_limit = pi->sys_info.sclk_dpm_tdp_limit_boost; + } +} + +static void sumo_pre_notify_alt_vddnb_change(struct radeon_device *rdev) +{ + struct sumo_ps *new_ps = sumo_get_ps(rdev->pm.dpm.requested_ps); + struct sumo_ps *old_ps = sumo_get_ps(rdev->pm.dpm.current_ps); + u32 nbps1_old = 0; + u32 nbps1_new = 0; + + if (old_ps != NULL) + nbps1_old = (old_ps->flags & SUMO_POWERSTATE_FLAGS_FORCE_NBPS1_STATE) ? 1 : 0; + + nbps1_new = (new_ps->flags & SUMO_POWERSTATE_FLAGS_FORCE_NBPS1_STATE) ? 1 : 0; + + if (nbps1_old == 1 && nbps1_new == 0) + sumo_smu_notify_alt_vddnb_change(rdev, 0, 0); +} + +static void sumo_post_notify_alt_vddnb_change(struct radeon_device *rdev) +{ + struct sumo_ps *new_ps = sumo_get_ps(rdev->pm.dpm.requested_ps); + struct sumo_ps *old_ps = sumo_get_ps(rdev->pm.dpm.current_ps); + u32 nbps1_old = 0; + u32 nbps1_new = 0; + + if (old_ps != NULL) + nbps1_old = (old_ps->flags & SUMO_POWERSTATE_FLAGS_FORCE_NBPS1_STATE)? 1 : 0; + + nbps1_new = (new_ps->flags & SUMO_POWERSTATE_FLAGS_FORCE_NBPS1_STATE)? 1 : 0; + + if (nbps1_old == 0 && nbps1_new == 1) + sumo_smu_notify_alt_vddnb_change(rdev, 1, 1); +} + +static void sumo_enable_boost(struct radeon_device *rdev, bool enable) +{ + struct sumo_ps *new_ps = sumo_get_ps(rdev->pm.dpm.requested_ps); + + if (enable) { + if (new_ps->flags & SUMO_POWERSTATE_FLAGS_BOOST_STATE) + sumo_boost_state_enable(rdev, true); + } else + sumo_boost_state_enable(rdev, false); +} + +static void sumo_update_current_power_levels(struct radeon_device *rdev) +{ + struct sumo_ps *new_ps = sumo_get_ps(rdev->pm.dpm.requested_ps); + struct sumo_power_info *pi = sumo_get_pi(rdev); + + pi->current_ps = *new_ps; +} + +static void sumo_set_forced_level(struct radeon_device *rdev, u32 index) +{ + WREG32_P(CG_SCLK_DPM_CTRL_3, FORCE_SCLK_STATE(index), ~FORCE_SCLK_STATE_MASK); +} + +static void sumo_set_forced_level_0(struct radeon_device *rdev) +{ + sumo_set_forced_level(rdev, 0); +} + +static void sumo_program_wl(struct radeon_device *rdev) +{ + struct sumo_ps *new_ps = sumo_get_ps(rdev->pm.dpm.requested_ps); + u32 dpm_ctrl4 = RREG32(CG_SCLK_DPM_CTRL_4); + + dpm_ctrl4 &= 0xFFFFFF00; + dpm_ctrl4 |= (1 << (new_ps->num_levels - 1)); + + if (new_ps->flags & SUMO_POWERSTATE_FLAGS_BOOST_STATE) + dpm_ctrl4 |= (1 << BOOST_DPM_LEVEL); + + WREG32(CG_SCLK_DPM_CTRL_4, dpm_ctrl4); +} + +static void sumo_program_power_levels_0_to_n(struct radeon_device *rdev) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + struct sumo_ps *new_ps = sumo_get_ps(rdev->pm.dpm.requested_ps); + struct sumo_ps *old_ps = sumo_get_ps(rdev->pm.dpm.current_ps); + u32 i; + u32 n_current_state_levels = (old_ps == NULL) ? 1 : old_ps->num_levels; + + for (i = 0; i < new_ps->num_levels; i++) { + sumo_program_power_level(rdev, &new_ps->levels[i], i); + sumo_power_level_enable(rdev, i, true); + } + + for (i = new_ps->num_levels; i < n_current_state_levels; i++) + sumo_power_level_enable(rdev, i, false); + + if (new_ps->flags & SUMO_POWERSTATE_FLAGS_BOOST_STATE) + sumo_program_power_level(rdev, &pi->boost_pl, BOOST_DPM_LEVEL); +} + +static void sumo_enable_acpi_pm(struct radeon_device *rdev) +{ + WREG32_P(GENERAL_PWRMGT, STATIC_PM_EN, ~STATIC_PM_EN); +} + +static void sumo_program_power_level_enter_state(struct radeon_device *rdev) +{ + WREG32_P(CG_SCLK_DPM_CTRL_5, SCLK_FSTATE_BOOTUP(0), ~SCLK_FSTATE_BOOTUP_MASK); +} + +static void sumo_program_acpi_power_level(struct radeon_device *rdev) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + struct atom_clock_dividers dividers; + int ret; + + ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM, + pi->acpi_pl.sclk, + false, ÷rs); + if (ret) + return; + + WREG32_P(CG_ACPI_CNTL, SCLK_ACPI_DIV(dividers.post_div), ~SCLK_ACPI_DIV_MASK); + WREG32_P(CG_ACPI_VOLTAGE_CNTL, 0, ~ACPI_VOLTAGE_EN); +} + +static void sumo_program_bootup_state(struct radeon_device *rdev) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + u32 dpm_ctrl4 = RREG32(CG_SCLK_DPM_CTRL_4); + u32 i; + + sumo_program_power_level(rdev, &pi->boot_pl, 0); + + dpm_ctrl4 &= 0xFFFFFF00; + WREG32(CG_SCLK_DPM_CTRL_4, dpm_ctrl4); + + for (i = 1; i < 8; i++) + sumo_power_level_enable(rdev, i, false); +} + +static void sumo_take_smu_control(struct radeon_device *rdev, bool enable) +{ + u32 v = RREG32(DOUT_SCRATCH3); + + if (enable) + v |= 0x4; + else + v &= 0xFFFFFFFB; + + WREG32(DOUT_SCRATCH3, v); +} + +static void sumo_enable_sclk_ds(struct radeon_device *rdev, bool enable) +{ + if (enable) { + u32 deep_sleep_cntl = RREG32(DEEP_SLEEP_CNTL); + u32 deep_sleep_cntl2 = RREG32(DEEP_SLEEP_CNTL2); + u32 t = 1; + + deep_sleep_cntl &= ~R_DIS; + deep_sleep_cntl &= ~HS_MASK; + deep_sleep_cntl |= HS(t > 4095 ? 4095 : t); + + deep_sleep_cntl2 |= LB_UFP_EN; + deep_sleep_cntl2 &= INOUT_C_MASK; + deep_sleep_cntl2 |= INOUT_C(0xf); + + WREG32(DEEP_SLEEP_CNTL2, deep_sleep_cntl2); + WREG32(DEEP_SLEEP_CNTL, deep_sleep_cntl); + } else + WREG32_P(DEEP_SLEEP_CNTL, 0, ~ENABLE_DS); +} + +static void sumo_program_bootup_at(struct radeon_device *rdev) +{ + WREG32_P(CG_AT_0, CG_R(0xffff), ~CG_R_MASK); + WREG32_P(CG_AT_0, CG_L(0), ~CG_L_MASK); +} + +static void sumo_reset_am(struct radeon_device *rdev) +{ + WREG32_P(SCLK_PWRMGT_CNTL, FIR_RESET, ~FIR_RESET); +} + +static void sumo_start_am(struct radeon_device *rdev) +{ + WREG32_P(SCLK_PWRMGT_CNTL, 0, ~FIR_RESET); +} + +static void sumo_program_ttp(struct radeon_device *rdev) +{ + u32 xclk = sumo_get_xclk(rdev); + u32 p, u; + u32 cg_sclk_dpm_ctrl_5 = RREG32(CG_SCLK_DPM_CTRL_5); + + r600_calculate_u_and_p(1000, + xclk, 16, &p, &u); + + cg_sclk_dpm_ctrl_5 &= ~(TT_TP_MASK | TT_TU_MASK); + cg_sclk_dpm_ctrl_5 |= TT_TP(p) | TT_TU(u); + + WREG32(CG_SCLK_DPM_CTRL_5, cg_sclk_dpm_ctrl_5); +} + +static void sumo_program_ttt(struct radeon_device *rdev) +{ + u32 cg_sclk_dpm_ctrl_3 = RREG32(CG_SCLK_DPM_CTRL_3); + struct sumo_power_info *pi = sumo_get_pi(rdev); + + cg_sclk_dpm_ctrl_3 &= ~(GNB_TT_MASK | GNB_THERMTHRO_MASK); + cg_sclk_dpm_ctrl_3 |= GNB_TT(pi->thermal_auto_throttling + 49); + + WREG32(CG_SCLK_DPM_CTRL_3, cg_sclk_dpm_ctrl_3); +} + + +static void sumo_enable_voltage_scaling(struct radeon_device *rdev, bool enable) +{ + if (enable) { + WREG32_P(CG_DPM_VOLTAGE_CNTL, DPM_VOLTAGE_EN, ~DPM_VOLTAGE_EN); + WREG32_P(CG_CG_VOLTAGE_CNTL, 0, ~CG_VOLTAGE_EN); + } else { + WREG32_P(CG_CG_VOLTAGE_CNTL, CG_VOLTAGE_EN, ~CG_VOLTAGE_EN); + WREG32_P(CG_DPM_VOLTAGE_CNTL, 0, ~DPM_VOLTAGE_EN); + } +} + +static void sumo_override_cnb_thermal_events(struct radeon_device *rdev) +{ + WREG32_P(CG_SCLK_DPM_CTRL_3, CNB_THERMTHRO_MASK_SCLK, + ~CNB_THERMTHRO_MASK_SCLK); +} + +static void sumo_program_dc_hto(struct radeon_device *rdev) +{ + u32 cg_sclk_dpm_ctrl_4 = RREG32(CG_SCLK_DPM_CTRL_4); + u32 p, u; + u32 xclk = sumo_get_xclk(rdev); + + r600_calculate_u_and_p(100000, + xclk, 14, &p, &u); + + cg_sclk_dpm_ctrl_4 &= ~(DC_HDC_MASK | DC_HU_MASK); + cg_sclk_dpm_ctrl_4 |= DC_HDC(p) | DC_HU(u); + + WREG32(CG_SCLK_DPM_CTRL_4, cg_sclk_dpm_ctrl_4); +} + +static void sumo_force_nbp_state(struct radeon_device *rdev) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + struct sumo_ps *new_ps = sumo_get_ps(rdev->pm.dpm.requested_ps); + + if (!pi->driver_nbps_policy_disable) { + if (new_ps->flags & SUMO_POWERSTATE_FLAGS_FORCE_NBPS1_STATE) + WREG32_P(CG_SCLK_DPM_CTRL_3, FORCE_NB_PSTATE_1, ~FORCE_NB_PSTATE_1); + else + WREG32_P(CG_SCLK_DPM_CTRL_3, 0, ~FORCE_NB_PSTATE_1); + } +} + +static u32 sumo_get_sleep_divider_from_id(u32 id) +{ + return 1 << id; +} + +static u32 sumo_get_sleep_divider_id_from_clock(struct radeon_device *rdev, + u32 sclk, + u32 min_sclk_in_sr) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + u32 i; + u32 temp; + u32 min = (min_sclk_in_sr > SUMO_MINIMUM_ENGINE_CLOCK) ? + min_sclk_in_sr : SUMO_MINIMUM_ENGINE_CLOCK; + + if (sclk < min) + return 0; + + if (!pi->enable_sclk_ds) + return 0; + + for (i = SUMO_MAX_DEEPSLEEP_DIVIDER_ID; ; i--) { + temp = sclk / sumo_get_sleep_divider_from_id(i); + + if (temp >= min || i == 0) + break; + } + return i; +} + +static u32 sumo_get_valid_engine_clock(struct radeon_device *rdev, + u32 lower_limit) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + u32 i; + + for (i = 0; i < pi->sys_info.sclk_voltage_mapping_table.num_max_dpm_entries; i++) { + if (pi->sys_info.sclk_voltage_mapping_table.entries[i].sclk_frequency >= lower_limit) + return pi->sys_info.sclk_voltage_mapping_table.entries[i].sclk_frequency; + } + + return pi->sys_info.sclk_voltage_mapping_table.entries[pi->sys_info.sclk_voltage_mapping_table.num_max_dpm_entries - 1].sclk_frequency; +} + +static void sumo_patch_thermal_state(struct radeon_device *rdev, + struct sumo_ps *ps, + struct sumo_ps *current_ps) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + u32 sclk_in_sr = pi->sys_info.min_sclk; /* ??? */ + u32 current_vddc; + u32 current_sclk; + u32 current_index = 0; + + if (current_ps) { + current_vddc = current_ps->levels[current_index].vddc_index; + current_sclk = current_ps->levels[current_index].sclk; + } else { + current_vddc = pi->boot_pl.vddc_index; + current_sclk = pi->boot_pl.sclk; + } + + ps->levels[0].vddc_index = current_vddc; + + if (ps->levels[0].sclk > current_sclk) + ps->levels[0].sclk = current_sclk; + + ps->levels[0].ss_divider_index = + sumo_get_sleep_divider_id_from_clock(rdev, ps->levels[0].sclk, sclk_in_sr); + + ps->levels[0].ds_divider_index = + sumo_get_sleep_divider_id_from_clock(rdev, ps->levels[0].sclk, SUMO_MINIMUM_ENGINE_CLOCK); + + if (ps->levels[0].ds_divider_index > ps->levels[0].ss_divider_index + 1) + ps->levels[0].ds_divider_index = ps->levels[0].ss_divider_index + 1; + + if (ps->levels[0].ss_divider_index == ps->levels[0].ds_divider_index) { + if (ps->levels[0].ss_divider_index > 1) + ps->levels[0].ss_divider_index = ps->levels[0].ss_divider_index - 1; + } + + if (ps->levels[0].ss_divider_index == 0) + ps->levels[0].ds_divider_index = 0; + + if (ps->levels[0].ds_divider_index == 0) + ps->levels[0].ss_divider_index = 0; +} + +static void sumo_apply_state_adjust_rules(struct radeon_device *rdev) +{ + struct radeon_ps *rps = rdev->pm.dpm.requested_ps; + struct sumo_ps *ps = sumo_get_ps(rps); + struct sumo_ps *current_ps = sumo_get_ps(rdev->pm.dpm.current_ps); + struct sumo_power_info *pi = sumo_get_pi(rdev); + u32 min_voltage = 0; /* ??? */ + u32 min_sclk = pi->sys_info.min_sclk; /* XXX check against disp reqs */ + u32 sclk_in_sr = pi->sys_info.min_sclk; /* ??? */ + u32 i; + + if (rps->class & ATOM_PPLIB_CLASSIFICATION_THERMAL) + return sumo_patch_thermal_state(rdev, ps, current_ps); + + if (pi->enable_boost) { + if (rps->class & ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE) + ps->flags |= SUMO_POWERSTATE_FLAGS_BOOST_STATE; + } + + if ((rps->class & ATOM_PPLIB_CLASSIFICATION_UI_BATTERY) || + (rps->class & ATOM_PPLIB_CLASSIFICATION_SDSTATE) || + (rps->class & ATOM_PPLIB_CLASSIFICATION_HDSTATE)) + ps->flags |= SUMO_POWERSTATE_FLAGS_FORCE_NBPS1_STATE; + + for (i = 0; i < ps->num_levels; i++) { + if (ps->levels[i].vddc_index < min_voltage) + ps->levels[i].vddc_index = min_voltage; + + if (ps->levels[i].sclk < min_sclk) + ps->levels[i].sclk = + sumo_get_valid_engine_clock(rdev, min_sclk); + + ps->levels[i].ss_divider_index = + sumo_get_sleep_divider_id_from_clock(rdev, ps->levels[i].sclk, sclk_in_sr); + + ps->levels[i].ds_divider_index = + sumo_get_sleep_divider_id_from_clock(rdev, ps->levels[i].sclk, SUMO_MINIMUM_ENGINE_CLOCK); + + if (ps->levels[i].ds_divider_index > ps->levels[i].ss_divider_index + 1) + ps->levels[i].ds_divider_index = ps->levels[i].ss_divider_index + 1; + + if (ps->levels[i].ss_divider_index == ps->levels[i].ds_divider_index) { + if (ps->levels[i].ss_divider_index > 1) + ps->levels[i].ss_divider_index = ps->levels[i].ss_divider_index - 1; + } + + if (ps->levels[i].ss_divider_index == 0) + ps->levels[i].ds_divider_index = 0; + + if (ps->levels[i].ds_divider_index == 0) + ps->levels[i].ss_divider_index = 0; + + if (ps->flags & SUMO_POWERSTATE_FLAGS_FORCE_NBPS1_STATE) + ps->levels[i].allow_gnb_slow = 1; + else if ((rps->class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE) || + (rps->class2 & ATOM_PPLIB_CLASSIFICATION2_MVC)) + ps->levels[i].allow_gnb_slow = 0; + else if (i == ps->num_levels - 1) + ps->levels[i].allow_gnb_slow = 0; + else + ps->levels[i].allow_gnb_slow = 1; + } +} + +static void sumo_cleanup_asic(struct radeon_device *rdev) +{ + sumo_take_smu_control(rdev, false); +} + +static int sumo_set_thermal_temperature_range(struct radeon_device *rdev, + int min_temp, int max_temp) +{ + int low_temp = 0 * 1000; + int high_temp = 255 * 1000; + + if (low_temp < min_temp) + low_temp = min_temp; + if (high_temp > max_temp) + high_temp = max_temp; + if (high_temp < low_temp) { + DRM_ERROR("invalid thermal range: %d - %d\n", low_temp, high_temp); + return -EINVAL; + } + + WREG32_P(CG_THERMAL_INT, DIG_THERM_INTH(49 + (high_temp / 1000)), ~DIG_THERM_INTH_MASK); + WREG32_P(CG_THERMAL_INT, DIG_THERM_INTL(49 + (low_temp / 1000)), ~DIG_THERM_INTL_MASK); + + rdev->pm.dpm.thermal.min_temp = low_temp; + rdev->pm.dpm.thermal.max_temp = high_temp; + + return 0; +} + +int sumo_dpm_enable(struct radeon_device *rdev) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + + if (sumo_dpm_enabled(rdev)) + return -EINVAL; + + sumo_enable_clock_power_gating(rdev); + sumo_program_bootup_state(rdev); + sumo_init_bsp(rdev); + sumo_reset_am(rdev); + sumo_program_tp(rdev); + sumo_program_bootup_at(rdev); + sumo_start_am(rdev); + if (pi->enable_auto_thermal_throttling) { + sumo_program_ttp(rdev); + sumo_program_ttt(rdev); + } + sumo_program_dc_hto(rdev); + sumo_program_power_level_enter_state(rdev); + sumo_enable_voltage_scaling(rdev, true); + sumo_program_sstp(rdev); + sumo_program_vc(rdev); + sumo_override_cnb_thermal_events(rdev); + sumo_start_dpm(rdev); + sumo_wait_for_level_0(rdev); + if (pi->enable_sclk_ds) + sumo_enable_sclk_ds(rdev, true); + if (pi->enable_boost) + sumo_enable_boost_timer(rdev); + + if (rdev->irq.installed && + r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) { + sumo_set_thermal_temperature_range(rdev, R600_TEMP_RANGE_MIN, R600_TEMP_RANGE_MAX); + rdev->irq.dpm_thermal = true; + radeon_irq_set(rdev); + } + + return 0; +} + +void sumo_dpm_disable(struct radeon_device *rdev) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + + if (!sumo_dpm_enabled(rdev)) + return; + sumo_disable_clock_power_gating(rdev); + if (pi->enable_sclk_ds) + sumo_enable_sclk_ds(rdev, false); + sumo_clear_vc(rdev); + sumo_wait_for_level_0(rdev); + sumo_stop_dpm(rdev); + sumo_enable_voltage_scaling(rdev, false); + + if (rdev->irq.installed && + r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) { + rdev->irq.dpm_thermal = false; + radeon_irq_set(rdev); + } +} + +int sumo_dpm_set_power_state(struct radeon_device *rdev) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + + if (pi->enable_dynamic_patch_ps) + sumo_apply_state_adjust_rules(rdev); + sumo_update_current_power_levels(rdev); + if (pi->enable_boost) { + sumo_enable_boost(rdev, false); + sumo_patch_boost_state(rdev); + } + if (pi->enable_dpm) { + sumo_pre_notify_alt_vddnb_change(rdev); + sumo_enable_power_level_0(rdev); + sumo_set_forced_level_0(rdev); + sumo_set_forced_mode_enabled(rdev); + sumo_wait_for_level_0(rdev); + sumo_program_power_levels_0_to_n(rdev); + sumo_program_wl(rdev); + sumo_program_bsp(rdev); + sumo_program_at(rdev); + sumo_force_nbp_state(rdev); + sumo_set_forced_mode_disabled(rdev); + sumo_set_forced_mode_enabled(rdev); + sumo_set_forced_mode_disabled(rdev); + sumo_post_notify_alt_vddnb_change(rdev); + } + if (pi->enable_boost) + sumo_enable_boost(rdev, true); + + return 0; +} + +void sumo_dpm_reset_asic(struct radeon_device *rdev) +{ + sumo_program_bootup_state(rdev); + sumo_enable_power_level_0(rdev); + sumo_set_forced_level_0(rdev); + sumo_set_forced_mode_enabled(rdev); + sumo_wait_for_level_0(rdev); + sumo_set_forced_mode_disabled(rdev); + sumo_set_forced_mode_enabled(rdev); + sumo_set_forced_mode_disabled(rdev); +} + +void sumo_dpm_setup_asic(struct radeon_device *rdev) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + + sumo_initialize_m3_arb(rdev); + pi->fw_version = sumo_get_running_fw_version(rdev); + DRM_INFO("Found smc ucode version: 0x%08x\n", pi->fw_version); + sumo_program_acpi_power_level(rdev); + sumo_enable_acpi_pm(rdev); + sumo_take_smu_control(rdev, true); +} + +void sumo_dpm_display_configuration_changed(struct radeon_device *rdev) +{ + +} + +union power_info { + struct _ATOM_POWERPLAY_INFO info; + struct _ATOM_POWERPLAY_INFO_V2 info_2; + struct _ATOM_POWERPLAY_INFO_V3 info_3; + struct _ATOM_PPLIB_POWERPLAYTABLE pplib; + struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2; + struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3; +}; + +union pplib_clock_info { + struct _ATOM_PPLIB_R600_CLOCK_INFO r600; + struct _ATOM_PPLIB_RS780_CLOCK_INFO rs780; + struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO evergreen; + struct _ATOM_PPLIB_SUMO_CLOCK_INFO sumo; +}; + +union pplib_power_state { + struct _ATOM_PPLIB_STATE v1; + struct _ATOM_PPLIB_STATE_V2 v2; +}; + +static void sumo_patch_boot_state(struct radeon_device *rdev, + struct sumo_ps *ps) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + + ps->num_levels = 1; + ps->flags = 0; + ps->levels[0] = pi->boot_pl; +} + +static void sumo_parse_pplib_non_clock_info(struct radeon_device *rdev, + struct radeon_ps *rps, + struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info, + u8 table_rev) +{ + struct sumo_ps *ps = sumo_get_ps(rps); + + rps->caps = le32_to_cpu(non_clock_info->ulCapsAndSettings); + rps->class = le16_to_cpu(non_clock_info->usClassification); + rps->class2 = le16_to_cpu(non_clock_info->usClassification2); + + if (ATOM_PPLIB_NONCLOCKINFO_VER1 < table_rev) { + rps->vclk = le32_to_cpu(non_clock_info->ulVCLK); + rps->dclk = le32_to_cpu(non_clock_info->ulDCLK); + } else { + rps->vclk = 0; + rps->dclk = 0; + } + + if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT) { + rdev->pm.dpm.boot_ps = rps; + sumo_patch_boot_state(rdev, ps); + } + if (rps->class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE) + rdev->pm.dpm.uvd_ps = rps; +} + +static void sumo_parse_pplib_clock_info(struct radeon_device *rdev, + struct radeon_ps *rps, int index, + union pplib_clock_info *clock_info) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + struct sumo_ps *ps = sumo_get_ps(rps); + struct sumo_pl *pl = &ps->levels[index]; + u32 sclk; + + sclk = le16_to_cpu(clock_info->sumo.usEngineClockLow); + sclk |= clock_info->sumo.ucEngineClockHigh << 16; + pl->sclk = sclk; + pl->vddc_index = clock_info->sumo.vddcIndex; + pl->sclk_dpm_tdp_limit = clock_info->sumo.tdpLimit; + + ps->num_levels = index + 1; + + if (pi->enable_sclk_ds) { + pl->ds_divider_index = 5; + pl->ss_divider_index = 4; + } +} + +static int sumo_parse_power_table(struct radeon_device *rdev) +{ + struct radeon_mode_info *mode_info = &rdev->mode_info; + struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info; + union pplib_power_state *power_state; + int i, j, k, non_clock_array_index, clock_array_index; + union pplib_clock_info *clock_info; + struct _StateArray *state_array; + struct _ClockInfoArray *clock_info_array; + struct _NonClockInfoArray *non_clock_info_array; + union power_info *power_info; + int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo); + u16 data_offset; + u8 frev, crev; + u8 *power_state_offset; + struct sumo_ps *ps; + + if (!atom_parse_data_header(mode_info->atom_context, index, NULL, + &frev, &crev, &data_offset)) + return -EINVAL; + power_info = (union power_info *)(mode_info->atom_context->bios + data_offset); + + state_array = (struct _StateArray *) + (mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib.usStateArrayOffset)); + clock_info_array = (struct _ClockInfoArray *) + (mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib.usClockInfoArrayOffset)); + non_clock_info_array = (struct _NonClockInfoArray *) + (mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset)); + + rdev->pm.dpm.ps = kzalloc(sizeof(struct radeon_ps) * + state_array->ucNumEntries, GFP_KERNEL); + if (!rdev->pm.dpm.ps) + return -ENOMEM; + power_state_offset = (u8 *)state_array->states; + rdev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps); + rdev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime); + rdev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime); + for (i = 0; i < state_array->ucNumEntries; i++) { + power_state = (union pplib_power_state *)power_state_offset; + non_clock_array_index = power_state->v2.nonClockInfoIndex; + non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *) + &non_clock_info_array->nonClockInfo[non_clock_array_index]; + if (!rdev->pm.power_state[i].clock_info) + return -EINVAL; + ps = kzalloc(sizeof(struct sumo_ps), GFP_KERNEL); + if (ps == NULL) { + kfree(rdev->pm.dpm.ps); + return -ENOMEM; + } + rdev->pm.dpm.ps[i].ps_priv = ps; + k = 0; + for (j = 0; j < power_state->v2.ucNumDPMLevels; j++) { + clock_array_index = power_state->v2.clockInfoIndex[j]; + if (k >= SUMO_MAX_HARDWARE_POWERLEVELS) + break; + clock_info = (union pplib_clock_info *) + &clock_info_array->clockInfo[clock_array_index * clock_info_array->ucEntrySize]; + sumo_parse_pplib_clock_info(rdev, + &rdev->pm.dpm.ps[i], k, + clock_info); + k++; + } + sumo_parse_pplib_non_clock_info(rdev, &rdev->pm.dpm.ps[i], + non_clock_info, + non_clock_info_array->ucEntrySize); + power_state_offset += 2 + power_state->v2.ucNumDPMLevels; + } + rdev->pm.dpm.num_ps = state_array->ucNumEntries; + return 0; +} + +static u32 sumo_convert_vid2_to_vid7(struct radeon_device *rdev, u32 vid_2bit) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + u32 i; + + for (i = 0; i < pi->sys_info.vid_mapping_table.num_entries; i++) { + if (pi->sys_info.vid_mapping_table.entries[i].vid_2bit == vid_2bit) + return pi->sys_info.vid_mapping_table.entries[i].vid_7bit; + } + + return pi->sys_info.vid_mapping_table.entries[pi->sys_info.vid_mapping_table.num_entries - 1].vid_7bit; +} + +static u16 sumo_convert_voltage_index_to_value(struct radeon_device *rdev, + u32 vid_2bit) +{ + u32 vid_7bit = sumo_convert_vid2_to_vid7(rdev, vid_2bit); + + if (vid_7bit > 0x7C) + return 0; + + return (15500 - vid_7bit * 125 + 5) / 10; +} + +static void sumo_construct_display_voltage_mapping_table(struct radeon_device *rdev, + ATOM_CLK_VOLT_CAPABILITY *table) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + u32 i; + + for (i = 0; i < SUMO_MAX_NUMBER_VOLTAGES; i++) { + if (table[i].ulMaximumSupportedCLK == 0) + break; + + pi->sys_info.disp_clk_voltage_mapping_table.display_clock_frequency[i] = + table[i].ulMaximumSupportedCLK; + } + + pi->sys_info.disp_clk_voltage_mapping_table.num_max_voltage_levels = i; + + if (pi->sys_info.disp_clk_voltage_mapping_table.num_max_voltage_levels == 0) { + pi->sys_info.disp_clk_voltage_mapping_table.display_clock_frequency[0] = 80000; + pi->sys_info.disp_clk_voltage_mapping_table.num_max_voltage_levels = 1; + } +} + +static void sumo_construct_sclk_voltage_mapping_table(struct radeon_device *rdev, + ATOM_AVAILABLE_SCLK_LIST *table) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + u32 i; + u32 n = 0; + u32 prev_sclk = 0; + + for (i = 0; i < SUMO_MAX_HARDWARE_POWERLEVELS; i++) { + if (table[i].ulSupportedSCLK > prev_sclk) { + pi->sys_info.sclk_voltage_mapping_table.entries[n].sclk_frequency = + table[i].ulSupportedSCLK; + pi->sys_info.sclk_voltage_mapping_table.entries[n].vid_2bit = + table[i].usVoltageIndex; + prev_sclk = table[i].ulSupportedSCLK; + n++; + } + } + + pi->sys_info.sclk_voltage_mapping_table.num_max_dpm_entries = n; +} + +static void sumo_construct_vid_mapping_table(struct radeon_device *rdev, + ATOM_AVAILABLE_SCLK_LIST *table) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + u32 i, j; + + for (i = 0; i < SUMO_MAX_HARDWARE_POWERLEVELS; i++) { + if (table[i].ulSupportedSCLK != 0) { + pi->sys_info.vid_mapping_table.entries[table[i].usVoltageIndex].vid_7bit = + table[i].usVoltageID; + pi->sys_info.vid_mapping_table.entries[table[i].usVoltageIndex].vid_2bit = + table[i].usVoltageIndex; + } + } + + for (i = 0; i < SUMO_MAX_NUMBER_VOLTAGES; i++) { + if (pi->sys_info.vid_mapping_table.entries[i].vid_7bit == 0) { + for (j = i + 1; j < SUMO_MAX_NUMBER_VOLTAGES; j++) { + if (pi->sys_info.vid_mapping_table.entries[j].vid_7bit != 0) { + pi->sys_info.vid_mapping_table.entries[i] = + pi->sys_info.vid_mapping_table.entries[j]; + pi->sys_info.vid_mapping_table.entries[j].vid_7bit = 0; + break; + } + } + + if (j == SUMO_MAX_NUMBER_VOLTAGES) + break; + } + } + + pi->sys_info.vid_mapping_table.num_entries = i; +} + +union igp_info { + struct _ATOM_INTEGRATED_SYSTEM_INFO info; + struct _ATOM_INTEGRATED_SYSTEM_INFO_V2 info_2; + struct _ATOM_INTEGRATED_SYSTEM_INFO_V5 info_5; + struct _ATOM_INTEGRATED_SYSTEM_INFO_V6 info_6; +}; + +static int sumo_parse_sys_info_table(struct radeon_device *rdev) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + struct radeon_mode_info *mode_info = &rdev->mode_info; + int index = GetIndexIntoMasterTable(DATA, IntegratedSystemInfo); + union igp_info *igp_info; + u8 frev, crev; + u16 data_offset; + int i; + + if (atom_parse_data_header(mode_info->atom_context, index, NULL, + &frev, &crev, &data_offset)) { + igp_info = (union igp_info *)(mode_info->atom_context->bios + + data_offset); + + if (crev != 6) { + DRM_ERROR("Unsupported IGP table: %d %d\n", frev, crev); + return -EINVAL; + } + pi->sys_info.bootup_sclk = le32_to_cpu(igp_info->info_6.ulBootUpEngineClock); + pi->sys_info.min_sclk = le32_to_cpu(igp_info->info_6.ulMinEngineClock); + pi->sys_info.bootup_uma_clk = le32_to_cpu(igp_info->info_6.ulBootUpUMAClock); + pi->sys_info.bootup_nb_voltage_index = + le16_to_cpu(igp_info->info_6.usBootUpNBVoltage); + if (igp_info->info_6.ucHtcTmpLmt == 0) + pi->sys_info.htc_tmp_lmt = 203; + else + pi->sys_info.htc_tmp_lmt = igp_info->info_6.ucHtcTmpLmt; + if (igp_info->info_6.ucHtcHystLmt == 0) + pi->sys_info.htc_hyst_lmt = 5; + else + pi->sys_info.htc_hyst_lmt = igp_info->info_6.ucHtcHystLmt; + if (pi->sys_info.htc_tmp_lmt <= pi->sys_info.htc_hyst_lmt) { + DRM_ERROR("The htcTmpLmt should be larger than htcHystLmt.\n"); + } + for (i = 0; i < NUMBER_OF_M3ARB_PARAM_SETS; i++) { + pi->sys_info.csr_m3_arb_cntl_default[i] = + le32_to_cpu(igp_info->info_6.ulCSR_M3_ARB_CNTL_DEFAULT[i]); + pi->sys_info.csr_m3_arb_cntl_uvd[i] = + le32_to_cpu(igp_info->info_6.ulCSR_M3_ARB_CNTL_UVD[i]); + pi->sys_info.csr_m3_arb_cntl_fs3d[i] = + le32_to_cpu(igp_info->info_6.ulCSR_M3_ARB_CNTL_FS3D[i]); + } + pi->sys_info.sclk_dpm_boost_margin = + le32_to_cpu(igp_info->info_6.SclkDpmBoostMargin); + pi->sys_info.sclk_dpm_throttle_margin = + le32_to_cpu(igp_info->info_6.SclkDpmThrottleMargin); + pi->sys_info.sclk_dpm_tdp_limit_pg = + le16_to_cpu(igp_info->info_6.SclkDpmTdpLimitPG); + pi->sys_info.gnb_tdp_limit = le16_to_cpu(igp_info->info_6.GnbTdpLimit); + pi->sys_info.sclk_dpm_tdp_limit_boost = + le16_to_cpu(igp_info->info_6.SclkDpmTdpLimitBoost); + pi->sys_info.boost_sclk = le32_to_cpu(igp_info->info_6.ulBoostEngineCLock); + pi->sys_info.boost_vid_2bit = igp_info->info_6.ulBoostVid_2bit; + if (igp_info->info_6.EnableBoost) + pi->sys_info.enable_boost = true; + else + pi->sys_info.enable_boost = false; + sumo_construct_display_voltage_mapping_table(rdev, + igp_info->info_6.sDISPCLK_Voltage); + sumo_construct_sclk_voltage_mapping_table(rdev, + igp_info->info_6.sAvail_SCLK); + sumo_construct_vid_mapping_table(rdev, igp_info->info_6.sAvail_SCLK); + + } + return 0; +} + +static void sumo_construct_boot_and_acpi_state(struct radeon_device *rdev) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + + pi->boot_pl.sclk = pi->sys_info.bootup_sclk; + pi->boot_pl.vddc_index = pi->sys_info.bootup_nb_voltage_index; + pi->boot_pl.ds_divider_index = 0; + pi->boot_pl.ss_divider_index = 0; + pi->boot_pl.allow_gnb_slow = 1; + pi->acpi_pl = pi->boot_pl; + pi->current_ps.num_levels = 1; + pi->current_ps.levels[0] = pi->boot_pl; +} + +int sumo_dpm_init(struct radeon_device *rdev) +{ + struct sumo_power_info *pi; + u32 hw_rev = (RREG32(HW_REV) & ATI_REV_ID_MASK) >> ATI_REV_ID_SHIFT; + int ret; + + pi = kzalloc(sizeof(struct sumo_power_info), GFP_KERNEL); + if (pi == NULL) + return -ENOMEM; + rdev->pm.dpm.priv = pi; + + pi->driver_nbps_policy_disable = false; + if ((rdev->family == CHIP_PALM) && (hw_rev < 3)) + pi->disable_gfx_power_gating_in_uvd = true; + else + pi->disable_gfx_power_gating_in_uvd = false; + pi->enable_alt_vddnb = true; + pi->enable_sclk_ds = true; + pi->enable_dynamic_m3_arbiter = false; + pi->enable_dynamic_patch_ps = true; + pi->enable_gfx_power_gating = true; + pi->enable_gfx_clock_gating = true; + pi->enable_mg_clock_gating = true; + pi->enable_auto_thermal_throttling = true; + + ret = sumo_parse_sys_info_table(rdev); + if (ret) + return ret; + + sumo_construct_boot_and_acpi_state(rdev); + + ret = sumo_parse_power_table(rdev); + if (ret) + return ret; + + pi->pasi = CYPRESS_HASI_DFLT; + pi->asi = RV770_ASI_DFLT; + pi->thermal_auto_throttling = pi->sys_info.htc_tmp_lmt; + pi->enable_boost = pi->sys_info.enable_boost; + pi->enable_dpm = true; + + return 0; +} + +void sumo_dpm_print_power_state(struct radeon_device *rdev, + struct radeon_ps *rps) +{ + int i; + struct sumo_ps *ps = sumo_get_ps(rps); + + r600_dpm_print_class_info(rps->class, rps->class2); + r600_dpm_print_cap_info(rps->caps); + printk("\tuvd vclk: %d dclk: %d\n", rps->vclk, rps->dclk); + for (i = 0; i < ps->num_levels; i++) { + struct sumo_pl *pl = &ps->levels[i]; + printk("\t\tpower level %d sclk: %u vddc: %u\n", + i, pl->sclk, + sumo_convert_voltage_index_to_value(rdev, pl->vddc_index)); + } + r600_dpm_print_ps_status(rdev, rps); +} + +void sumo_dpm_fini(struct radeon_device *rdev) +{ + int i; + + sumo_cleanup_asic(rdev); /* ??? */ + + for (i = 0; i < rdev->pm.dpm.num_ps; i++) { + kfree(rdev->pm.dpm.ps[i].ps_priv); + } + kfree(rdev->pm.dpm.ps); + kfree(rdev->pm.dpm.priv); +} + +u32 sumo_dpm_get_sclk(struct radeon_device *rdev, bool low) +{ + struct sumo_ps *requested_state = sumo_get_ps(rdev->pm.dpm.requested_ps); + + if (low) + return requested_state->levels[0].sclk; + else + return requested_state->levels[requested_state->num_levels - 1].sclk; +} + +u32 sumo_dpm_get_mclk(struct radeon_device *rdev, bool low) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + + return pi->sys_info.bootup_uma_clk; +} diff --git a/drivers/gpu/drm/radeon/sumo_dpm.h b/drivers/gpu/drm/radeon/sumo_dpm.h new file mode 100644 index 0000000..561bee1 --- /dev/null +++ b/drivers/gpu/drm/radeon/sumo_dpm.h @@ -0,0 +1,199 @@ +/* + * Copyright 2012 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ +#ifndef __SUMO_DPM_H__ +#define __SUMO_DPM_H__ + +#define SUMO_MAX_HARDWARE_POWERLEVELS 5 +#define SUMO_PM_NUMBER_OF_TC 15 + +struct sumo_pl { + u32 sclk; + u32 vddc_index; + u32 ds_divider_index; + u32 ss_divider_index; + u32 allow_gnb_slow; + u32 sclk_dpm_tdp_limit; +}; + +/* used for the flags field */ +#define SUMO_POWERSTATE_FLAGS_FORCE_NBPS1_STATE (1 << 0) +#define SUMO_POWERSTATE_FLAGS_BOOST_STATE (1 << 1) + +struct sumo_ps { + struct sumo_pl levels[SUMO_MAX_HARDWARE_POWERLEVELS]; + u32 num_levels; + /* flags */ + u32 flags; +}; + +#define NUMBER_OF_M3ARB_PARAM_SETS 10 +#define SUMO_MAX_NUMBER_VOLTAGES 4 + +struct sumo_disp_clock_voltage_mapping_table { + u32 num_max_voltage_levels; + u32 display_clock_frequency[SUMO_MAX_NUMBER_VOLTAGES]; +}; + +struct sumo_vid_mapping_entry { + u16 vid_2bit; + u16 vid_7bit; +}; + +struct sumo_vid_mapping_table { + u32 num_entries; + struct sumo_vid_mapping_entry entries[SUMO_MAX_NUMBER_VOLTAGES]; +}; + +struct sumo_sclk_voltage_mapping_entry { + u32 sclk_frequency; + u16 vid_2bit; + u16 rsv; +}; + +struct sumo_sclk_voltage_mapping_table { + u32 num_max_dpm_entries; + struct sumo_sclk_voltage_mapping_entry entries[SUMO_MAX_HARDWARE_POWERLEVELS]; +}; + +struct sumo_sys_info { + u32 bootup_sclk; + u32 min_sclk; + u32 bootup_uma_clk; + u16 bootup_nb_voltage_index; + u8 htc_tmp_lmt; + u8 htc_hyst_lmt; + struct sumo_sclk_voltage_mapping_table sclk_voltage_mapping_table; + struct sumo_disp_clock_voltage_mapping_table disp_clk_voltage_mapping_table; + struct sumo_vid_mapping_table vid_mapping_table; + u32 csr_m3_arb_cntl_default[NUMBER_OF_M3ARB_PARAM_SETS]; + u32 csr_m3_arb_cntl_uvd[NUMBER_OF_M3ARB_PARAM_SETS]; + u32 csr_m3_arb_cntl_fs3d[NUMBER_OF_M3ARB_PARAM_SETS]; + u32 sclk_dpm_boost_margin; + u32 sclk_dpm_throttle_margin; + u32 sclk_dpm_tdp_limit_pg; + u32 gnb_tdp_limit; + u32 sclk_dpm_tdp_limit_boost; + u32 boost_sclk; + u32 boost_vid_2bit; + bool enable_boost; +}; + +struct sumo_power_info { + u32 asi; + u32 pasi; + u32 bsp; + u32 bsu; + u32 pbsp; + u32 pbsu; + u32 dsp; + u32 psp; + u32 thermal_auto_throttling; + u32 uvd_m3_arbiter; + u32 fw_version; + struct sumo_sys_info sys_info; + struct sumo_pl acpi_pl; + struct sumo_pl boot_pl; + struct sumo_pl boost_pl; + struct sumo_ps current_ps; + bool disable_gfx_power_gating_in_uvd; + bool driver_nbps_policy_disable; + bool enable_alt_vddnb; + bool enable_dynamic_m3_arbiter; + bool enable_gfx_clock_gating; + bool enable_gfx_power_gating; + bool enable_mg_clock_gating; + bool enable_sclk_ds; + bool enable_auto_thermal_throttling; + bool enable_dynamic_patch_ps; + bool enable_dpm; + bool enable_boost; +}; + +#define SUMO_UTC_DFLT_00 0x48 +#define SUMO_UTC_DFLT_01 0x44 +#define SUMO_UTC_DFLT_02 0x44 +#define SUMO_UTC_DFLT_03 0x44 +#define SUMO_UTC_DFLT_04 0x44 +#define SUMO_UTC_DFLT_05 0x44 +#define SUMO_UTC_DFLT_06 0x44 +#define SUMO_UTC_DFLT_07 0x44 +#define SUMO_UTC_DFLT_08 0x44 +#define SUMO_UTC_DFLT_09 0x44 +#define SUMO_UTC_DFLT_10 0x44 +#define SUMO_UTC_DFLT_11 0x44 +#define SUMO_UTC_DFLT_12 0x44 +#define SUMO_UTC_DFLT_13 0x44 +#define SUMO_UTC_DFLT_14 0x44 + +#define SUMO_DTC_DFLT_00 0x48 +#define SUMO_DTC_DFLT_01 0x44 +#define SUMO_DTC_DFLT_02 0x44 +#define SUMO_DTC_DFLT_03 0x44 +#define SUMO_DTC_DFLT_04 0x44 +#define SUMO_DTC_DFLT_05 0x44 +#define SUMO_DTC_DFLT_06 0x44 +#define SUMO_DTC_DFLT_07 0x44 +#define SUMO_DTC_DFLT_08 0x44 +#define SUMO_DTC_DFLT_09 0x44 +#define SUMO_DTC_DFLT_10 0x44 +#define SUMO_DTC_DFLT_11 0x44 +#define SUMO_DTC_DFLT_12 0x44 +#define SUMO_DTC_DFLT_13 0x44 +#define SUMO_DTC_DFLT_14 0x44 + +#define SUMO_AH_DFLT 5 + +#define SUMO_R_DFLT0 70 +#define SUMO_R_DFLT1 70 +#define SUMO_R_DFLT2 70 +#define SUMO_R_DFLT3 70 +#define SUMO_R_DFLT4 100 + +#define SUMO_L_DFLT0 0 +#define SUMO_L_DFLT1 20 +#define SUMO_L_DFLT2 20 +#define SUMO_L_DFLT3 20 +#define SUMO_L_DFLT4 20 +#define SUMO_VRC_DFLT 0x30033 +#define SUMO_MGCGTTLOCAL0_DFLT 0 +#define SUMO_MGCGTTLOCAL1_DFLT 0 +#define SUMO_GICST_DFLT 19 +#define SUMO_SST_DFLT 8 +#define SUMO_VOLTAGEDROPT_DFLT 1 +#define SUMO_GFXPOWERGATINGT_DFLT 100 + +/* sumo_dpm.c */ +u32 sumo_get_xclk(struct radeon_device *rdev); + + +/* sumo_smc.c */ +void sumo_initialize_m3_arb(struct radeon_device *rdev); +void sumo_smu_pg_init(struct radeon_device *rdev); +void sumo_set_tdp_limit(struct radeon_device *rdev, u32 index, u32 tdp_limit); +void sumo_smu_notify_alt_vddnb_change(struct radeon_device *rdev, + bool powersaving, bool force_nbps1); +void sumo_boost_state_enable(struct radeon_device *rdev, bool enable); +void sumo_enable_boost_timer(struct radeon_device *rdev); +u32 sumo_get_running_fw_version(struct radeon_device *rdev); + +#endif diff --git a/drivers/gpu/drm/radeon/sumo_smc.c b/drivers/gpu/drm/radeon/sumo_smc.c new file mode 100644 index 0000000..9d0ae9b --- /dev/null +++ b/drivers/gpu/drm/radeon/sumo_smc.c @@ -0,0 +1,224 @@ +/* + * Copyright 2012 Advanced Micro Devices, 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. + * + */ + +#include <linux/firmware.h> +#include "drmP.h" +#include "radeon.h" +#include "sumod.h" +#include "sumo_dpm.h" +#include "ppsmc.h" +#include "radeon_ucode.h" + +#define SUMO_SMU_SERVICE_ROUTINE_PG_INIT 1 +#define SUMO_SMU_SERVICE_ROUTINE_ALTVDDNB_NOTIFY 27 +#define SUMO_SMU_SERVICE_ROUTINE_GFX_SRV_ID_20 20 + +struct sumo_ps *sumo_get_ps(struct radeon_ps *rps); +struct sumo_power_info *sumo_get_pi(struct radeon_device *rdev); + +static void sumo_send_msg_to_smu(struct radeon_device *rdev, u32 id) +{ + u32 gfx_int_req; + int i; + + for (i = 0; i < rdev->usec_timeout; i++) { + if (RREG32(GFX_INT_STATUS) & INT_DONE) + break; + udelay(1); + } + + gfx_int_req = SERV_INDEX(id) | INT_REQ; + WREG32(GFX_INT_REQ, gfx_int_req); + + for (i = 0; i < rdev->usec_timeout; i++) { + if (RREG32(GFX_INT_REQ) & INT_REQ) + break; + udelay(1); + } + + for (i = 0; i < rdev->usec_timeout; i++) { + if (RREG32(GFX_INT_STATUS) & INT_ACK) + break; + udelay(1); + } + + for (i = 0; i < rdev->usec_timeout; i++) { + if (RREG32(GFX_INT_STATUS) & INT_DONE) + break; + udelay(1); + } + + gfx_int_req &= ~INT_REQ; + WREG32(GFX_INT_REQ, gfx_int_req); +} + +void sumo_initialize_m3_arb(struct radeon_device *rdev) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + u32 i; + + if (!pi->enable_dynamic_m3_arbiter) + return; + + for (i = 0; i < NUMBER_OF_M3ARB_PARAM_SETS; i++) + WREG32_RCU(MCU_M3ARB_PARAMS + (i * 4), + pi->sys_info.csr_m3_arb_cntl_default[i]); + + for (; i < NUMBER_OF_M3ARB_PARAM_SETS * 2; i++) + WREG32_RCU(MCU_M3ARB_PARAMS + (i * 4), + pi->sys_info.csr_m3_arb_cntl_uvd[i % NUMBER_OF_M3ARB_PARAM_SETS]); + + for (; i < NUMBER_OF_M3ARB_PARAM_SETS * 3; i++) + WREG32_RCU(MCU_M3ARB_PARAMS + (i * 4), + pi->sys_info.csr_m3_arb_cntl_fs3d[i % NUMBER_OF_M3ARB_PARAM_SETS]); +} + +static bool sumo_is_alt_vddnb_supported(struct radeon_device *rdev) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + bool return_code = false; + + if (!pi->enable_alt_vddnb) + return return_code; + + if ((rdev->family == CHIP_SUMO) || (rdev->family == CHIP_SUMO2)) { + if (pi->fw_version >= 0x00010C00) + return_code = true; + } + + return return_code; +} + +void sumo_smu_notify_alt_vddnb_change(struct radeon_device *rdev, + bool powersaving, bool force_nbps1) +{ + u32 param = 0; + + if (!sumo_is_alt_vddnb_supported(rdev)) + return; + + if (powersaving) + param |= 1; + + if (force_nbps1) + param |= 2; + + WREG32_RCU(RCU_ALTVDDNB_NOTIFY, param); + + sumo_send_msg_to_smu(rdev, SUMO_SMU_SERVICE_ROUTINE_ALTVDDNB_NOTIFY); +} + +void sumo_smu_pg_init(struct radeon_device *rdev) +{ + sumo_send_msg_to_smu(rdev, SUMO_SMU_SERVICE_ROUTINE_PG_INIT); +} + +static u32 sumo_power_of_4(u32 unit) +{ + u32 ret = 1; + u32 i; + + for (i = 0; i < unit; i++) + ret *= 4; + + return ret; +} + +void sumo_enable_boost_timer(struct radeon_device *rdev) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + u32 period, unit, timer_value; + u32 xclk = sumo_get_xclk(rdev); + + unit = (RREG32_RCU(RCU_LCLK_SCALING_CNTL) & LCLK_SCALING_TIMER_PRESCALER_MASK) + >> LCLK_SCALING_TIMER_PRESCALER_SHIFT; + + period = 100 * (xclk / 100 / sumo_power_of_4(unit)); + + timer_value = (period << 16) | (unit << 4); + + WREG32_RCU(RCU_GNB_PWR_REP_TIMER_CNTL, timer_value); + WREG32_RCU(RCU_BOOST_MARGIN, pi->sys_info.sclk_dpm_boost_margin); + WREG32_RCU(RCU_THROTTLE_MARGIN, pi->sys_info.sclk_dpm_throttle_margin); + WREG32_RCU(GNB_TDP_LIMIT, pi->sys_info.gnb_tdp_limit); + WREG32_RCU(RCU_SclkDpmTdpLimitPG, pi->sys_info.sclk_dpm_tdp_limit_pg); + + sumo_send_msg_to_smu(rdev, SUMO_SMU_SERVICE_ROUTINE_GFX_SRV_ID_20); +} + +void sumo_set_tdp_limit(struct radeon_device *rdev, u32 index, u32 tdp_limit) +{ + u32 regoffset = 0; + u32 shift = 0; + u32 mask = 0xFFF; + u32 sclk_dpm_tdp_limit; + + switch (index) { + case 0: + regoffset = RCU_SclkDpmTdpLimit01; + shift = 16; + break; + case 1: + regoffset = RCU_SclkDpmTdpLimit01; + shift = 0; + break; + case 2: + regoffset = RCU_SclkDpmTdpLimit23; + shift = 16; + break; + case 3: + regoffset = RCU_SclkDpmTdpLimit23; + shift = 0; + break; + case 4: + regoffset = RCU_SclkDpmTdpLimit47; + shift = 16; + break; + case 7: + regoffset = RCU_SclkDpmTdpLimit47; + shift = 0; + break; + default: + break; + } + + sclk_dpm_tdp_limit = RREG32_RCU(regoffset); + sclk_dpm_tdp_limit &= ~(mask << shift); + sclk_dpm_tdp_limit |= (tdp_limit << shift); + WREG32_RCU(regoffset, sclk_dpm_tdp_limit); +} + +void sumo_boost_state_enable(struct radeon_device *rdev, bool enable) +{ + u32 boost_disable = RREG32_RCU(RCU_GPU_BOOST_DISABLE); + + boost_disable &= 0xFFFFFFFE; + boost_disable |= (enable ? 0 : 1); + WREG32_RCU(RCU_GPU_BOOST_DISABLE, boost_disable); +} + +u32 sumo_get_running_fw_version(struct radeon_device *rdev) +{ + return RREG32_RCU(RCU_FW_VERSION); +} + diff --git a/drivers/gpu/drm/radeon/sumod.h b/drivers/gpu/drm/radeon/sumod.h new file mode 100644 index 0000000..a5deba6 --- /dev/null +++ b/drivers/gpu/drm/radeon/sumod.h @@ -0,0 +1,362 @@ +/* + * Copyright 2012 Advanced Micro Devices, 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: Alex Deucher + */ +#ifndef _SUMOD_H_ +#define _SUMOD_H_ + +/* pm registers */ + +/* rcu */ +#define RCU_FW_VERSION 0x30c + +#define RCU_PWR_GATING_SEQ0 0x408 +#define RCU_PWR_GATING_SEQ1 0x40c +#define RCU_PWR_GATING_CNTL 0x410 +# define PWR_GATING_EN (1 << 0) +# define RSVD_MASK (0x3 << 1) +# define PCV(x) ((x) << 3) +# define PCV_MASK (0x1f << 3) +# define PCV_SHIFT 3 +# define PCP(x) ((x) << 8) +# define PCP_MASK (0xf << 8) +# define PCP_SHIFT 8 +# define RPW(x) ((x) << 16) +# define RPW_MASK (0xf << 16) +# define RPW_SHIFT 16 +# define ID(x) ((x) << 24) +# define ID_MASK (0xf << 24) +# define ID_SHIFT 24 +# define PGS(x) ((x) << 28) +# define PGS_MASK (0xf << 28) +# define PGS_SHIFT 28 + +#define RCU_ALTVDDNB_NOTIFY 0x430 +#define RCU_LCLK_SCALING_CNTL 0x434 +# define LCLK_SCALING_EN (1 << 0) +# define LCLK_SCALING_TYPE (1 << 1) +# define LCLK_SCALING_TIMER_PRESCALER(x) ((x) << 4) +# define LCLK_SCALING_TIMER_PRESCALER_MASK (0xf << 4) +# define LCLK_SCALING_TIMER_PRESCALER_SHIFT 4 +# define LCLK_SCALING_TIMER_PERIOD(x) ((x) << 16) +# define LCLK_SCALING_TIMER_PERIOD_MASK (0xf << 16) +# define LCLK_SCALING_TIMER_PERIOD_SHIFT 16 + +#define RCU_PWR_GATING_CNTL_2 0x4a0 +# define MPPU(x) ((x) << 0) +# define MPPU_MASK (0xffff << 0) +# define MPPU_SHIFT 0 +# define MPPD(x) ((x) << 16) +# define MPPD_MASK (0xffff << 16) +# define MPPD_SHIFT 16 +#define RCU_PWR_GATING_CNTL_3 0x4a4 +# define DPPU(x) ((x) << 0) +# define DPPU_MASK (0xffff << 0) +# define DPPU_SHIFT 0 +# define DPPD(x) ((x) << 16) +# define DPPD_MASK (0xffff << 16) +# define DPPD_SHIFT 16 +#define RCU_PWR_GATING_CNTL_4 0x4a8 +# define RT(x) ((x) << 0) +# define RT_MASK (0xffff << 0) +# define RT_SHIFT 0 +# define IT(x) ((x) << 16) +# define IT_MASK (0xffff << 16) +# define IT_SHIFT 16 + +/* yes these two have the same address */ +#define RCU_PWR_GATING_CNTL_5 0x504 +#define RCU_GPU_BOOST_DISABLE 0x508 + +#define MCU_M3ARB_INDEX 0x504 +#define MCU_M3ARB_PARAMS 0x508 + +#define RCU_GNB_PWR_REP_TIMER_CNTL 0x50C + +#define RCU_SclkDpmTdpLimit01 0x514 +#define RCU_SclkDpmTdpLimit23 0x518 +#define RCU_SclkDpmTdpLimit47 0x51C +#define RCU_SclkDpmTdpLimitPG 0x520 + +#define GNB_TDP_LIMIT 0x540 +#define RCU_BOOST_MARGIN 0x544 +#define RCU_THROTTLE_MARGIN 0x548 + +#define SMU_PCIE_PG_ARGS 0x58C +#define SMU_PCIE_PG_ARGS_2 0x598 +#define SMU_PCIE_PG_ARGS_3 0x59C + +/* mmio */ +#define RCU_STATUS 0x11c +# define GMC_PWR_GATER_BUSY (1 << 8) +# define GFX_PWR_GATER_BUSY (1 << 9) +# define UVD_PWR_GATER_BUSY (1 << 10) +# define PCIE_PWR_GATER_BUSY (1 << 11) +# define GMC_PWR_GATER_STATE (1 << 12) +# define GFX_PWR_GATER_STATE (1 << 13) +# define UVD_PWR_GATER_STATE (1 << 14) +# define PCIE_PWR_GATER_STATE (1 << 15) +# define GFX1_PWR_GATER_BUSY (1 << 16) +# define GFX2_PWR_GATER_BUSY (1 << 17) +# define GFX1_PWR_GATER_STATE (1 << 18) +# define GFX2_PWR_GATER_STATE (1 << 19) + +#define GFX_INT_REQ 0x120 +# define INT_REQ (1 << 0) +# define SERV_INDEX(x) ((x) << 1) +# define SERV_INDEX_MASK (0xff << 1) +# define SERV_INDEX_SHIFT 1 +#define GFX_INT_STATUS 0x124 +# define INT_ACK (1 << 0) +# define INT_DONE (1 << 1) + +#define CG_SCLK_CNTL 0x600 +# define SCLK_DIVIDER(x) ((x) << 0) +# define SCLK_DIVIDER_MASK (0x7f << 0) +# define SCLK_DIVIDER_SHIFT 0 +#define CG_SCLK_STATUS 0x604 +# define SCLK_OVERCLK_DETECT (1 << 2) + +#define GENERAL_PWRMGT 0x63c +# define STATIC_PM_EN (1 << 1) + +#define SCLK_PWRMGT_CNTL 0x644 +# define SCLK_PWRMGT_OFF (1 << 0) +# define SCLK_LOW_D1 (1 << 1) +# define FIR_RESET (1 << 4) +# define FIR_FORCE_TREND_SEL (1 << 5) +# define FIR_TREND_MODE (1 << 6) +# define DYN_GFX_CLK_OFF_EN (1 << 7) +# define GFX_CLK_FORCE_ON (1 << 8) +# define GFX_CLK_REQUEST_OFF (1 << 9) +# define GFX_CLK_FORCE_OFF (1 << 10) +# define GFX_CLK_OFF_ACPI_D1 (1 << 11) +# define GFX_CLK_OFF_ACPI_D2 (1 << 12) +# define GFX_CLK_OFF_ACPI_D3 (1 << 13) +# define GFX_VOLTAGE_CHANGE_EN (1 << 16) +# define GFX_VOLTAGE_CHANGE_MODE (1 << 17) + +#define TARGET_AND_CURRENT_PROFILE_INDEX 0x66c +# define TARG_SCLK_INDEX(x) ((x) << 6) +# define TARG_SCLK_INDEX_MASK (0x7 << 6) +# define TARG_SCLK_INDEX_SHIFT 6 +# define CURR_SCLK_INDEX(x) ((x) << 9) +# define CURR_SCLK_INDEX_MASK (0x7 << 9) +# define CURR_SCLK_INDEX_SHIFT 9 +# define TARG_INDEX(x) ((x) << 12) +# define TARG_INDEX_MASK (0x7 << 12) +# define TARG_INDEX_SHIFT 12 +# define CURR_INDEX(x) ((x) << 15) +# define CURR_INDEX_MASK (0x7 << 15) +# define CURR_INDEX_SHIFT 15 + +#define CG_SCLK_DPM_CTRL 0x684 +# define SCLK_FSTATE_0_DIV(x) ((x) << 0) +# define SCLK_FSTATE_0_DIV_MASK (0x7f << 0) +# define SCLK_FSTATE_0_DIV_SHIFT 0 +# define SCLK_FSTATE_0_VLD (1 << 7) +# define SCLK_FSTATE_1_DIV(x) ((x) << 8) +# define SCLK_FSTATE_1_DIV_MASK (0x7f << 8) +# define SCLK_FSTATE_1_DIV_SHIFT 8 +# define SCLK_FSTATE_1_VLD (1 << 15) +# define SCLK_FSTATE_2_DIV(x) ((x) << 16) +# define SCLK_FSTATE_2_DIV_MASK (0x7f << 16) +# define SCLK_FSTATE_2_DIV_SHIFT 16 +# define SCLK_FSTATE_2_VLD (1 << 23) +# define SCLK_FSTATE_3_DIV(x) ((x) << 24) +# define SCLK_FSTATE_3_DIV_MASK (0x7f << 24) +# define SCLK_FSTATE_3_DIV_SHIFT 24 +# define SCLK_FSTATE_3_VLD (1 << 31) +#define CG_SCLK_DPM_CTRL_2 0x688 +#define CG_GCOOR 0x68c +# define PHC(x) ((x) << 0) +# define PHC_MASK (0x1f << 0) +# define PHC_SHIFT 0 +# define SDC(x) ((x) << 9) +# define SDC_MASK (0x3ff << 9) +# define SDC_SHIFT 9 +# define SU(x) ((x) << 23) +# define SU_MASK (0xf << 23) +# define SU_SHIFT 23 +# define DIV_ID(x) ((x) << 28) +# define DIV_ID_MASK (0x7 << 28) +# define DIV_ID_SHIFT 28 + +#define CG_FTV 0x690 +#define CG_FFCT_0 0x694 +# define UTC_0(x) ((x) << 0) +# define UTC_0_MASK (0x3ff << 0) +# define UTC_0_SHIFT 0 +# define DTC_0(x) ((x) << 10) +# define DTC_0_MASK (0x3ff << 10) +# define DTC_0_SHIFT 10 + +#define CG_GIT 0x6d8 +# define CG_GICST(x) ((x) << 0) +# define CG_GICST_MASK (0xffff << 0) +# define CG_GICST_SHIFT 0 +# define CG_GIPOT(x) ((x) << 16) +# define CG_GIPOT_MASK (0xffff << 16) +# define CG_GIPOT_SHIFT 16 + +#define CG_SCLK_DPM_CTRL_3 0x6e0 +# define FORCE_SCLK_STATE(x) ((x) << 0) +# define FORCE_SCLK_STATE_MASK (0x7 << 0) +# define FORCE_SCLK_STATE_SHIFT 0 +# define FORCE_SCLK_STATE_EN (1 << 3) +# define GNB_TT(x) ((x) << 8) +# define GNB_TT_MASK (0xff << 8) +# define GNB_TT_SHIFT 8 +# define GNB_THERMTHRO_MASK (1 << 16) +# define CNB_THERMTHRO_MASK_SCLK (1 << 17) +# define DPM_SCLK_ENABLE (1 << 18) +# define GNB_SLOW_FSTATE_0_MASK (1 << 23) +# define GNB_SLOW_FSTATE_0_SHIFT 23 +# define FORCE_NB_PSTATE_1 (1 << 31) + +#define CG_SSP 0x6e8 +# define SST(x) ((x) << 0) +# define SST_MASK (0xffff << 0) +# define SST_SHIFT 0 +# define SSTU(x) ((x) << 16) +# define SSTU_MASK (0xffff << 16) +# define SSTU_SHIFT 16 + +#define CG_ACPI_CNTL 0x70c +# define SCLK_ACPI_DIV(x) ((x) << 0) +# define SCLK_ACPI_DIV_MASK (0x7f << 0) +# define SCLK_ACPI_DIV_SHIFT 0 + +#define CG_SCLK_DPM_CTRL_4 0x71c +# define DC_HDC(x) ((x) << 14) +# define DC_HDC_MASK (0x3fff << 14) +# define DC_HDC_SHIFT 14 +# define DC_HU(x) ((x) << 28) +# define DC_HU_MASK (0xf << 28) +# define DC_HU_SHIFT 28 +#define CG_SCLK_DPM_CTRL_5 0x720 +# define SCLK_FSTATE_BOOTUP(x) ((x) << 0) +# define SCLK_FSTATE_BOOTUP_MASK (0x7 << 0) +# define SCLK_FSTATE_BOOTUP_SHIFT 0 +# define TT_TP(x) ((x) << 3) +# define TT_TP_MASK (0xffff << 3) +# define TT_TP_SHIFT 3 +# define TT_TU(x) ((x) << 19) +# define TT_TU_MASK (0xff << 19) +# define TT_TU_SHIFT 19 +#define CG_SCLK_DPM_CTRL_6 0x724 +#define CG_AT_0 0x728 +# define CG_R(x) ((x) << 0) +# define CG_R_MASK (0xffff << 0) +# define CG_R_SHIFT 0 +# define CG_L(x) ((x) << 16) +# define CG_L_MASK (0xffff << 16) +# define CG_L_SHIFT 16 +#define CG_AT_1 0x72c +#define CG_AT_2 0x730 +#define CG_THERMAL_INT 0x734 +#define DIG_THERM_INTH(x) ((x) << 8) +#define DIG_THERM_INTH_MASK 0x0000FF00 +#define DIG_THERM_INTH_SHIFT 8 +#define DIG_THERM_INTL(x) ((x) << 16) +#define DIG_THERM_INTL_MASK 0x00FF0000 +#define DIG_THERM_INTL_SHIFT 16 +#define THERM_INT_MASK_HIGH (1 << 24) +#define THERM_INT_MASK_LOW (1 << 25) +#define CG_AT_3 0x738 +#define CG_AT_4 0x73c +#define CG_AT_5 0x740 +#define CG_AT_6 0x744 +#define CG_AT_7 0x748 + +#define CG_BSP_0 0x750 +# define BSP(x) ((x) << 0) +# define BSP_MASK (0xffff << 0) +# define BSP_SHIFT 0 +# define BSU(x) ((x) << 16) +# define BSU_MASK (0xf << 16) +# define BSU_SHIFT 16 + +#define CG_CG_VOLTAGE_CNTL 0x770 +# define REQ (1 << 0) +# define LEVEL(x) ((x) << 1) +# define LEVEL_MASK (0x3 << 1) +# define LEVEL_SHIFT 1 +# define CG_VOLTAGE_EN (1 << 3) +# define FORCE (1 << 4) +# define PERIOD(x) ((x) << 8) +# define PERIOD_MASK (0xffff << 8) +# define PERIOD_SHIFT 8 +# define UNIT(x) ((x) << 24) +# define UNIT_MASK (0xf << 24) +# define UNIT_SHIFT 24 + +#define CG_ACPI_VOLTAGE_CNTL 0x780 +# define ACPI_VOLTAGE_EN (1 << 8) + +#define CG_DPM_VOLTAGE_CNTL 0x788 +# define DPM_STATE0_LEVEL_MASK (0x3 << 0) +# define DPM_STATE0_LEVEL_SHIFT 0 +# define DPM_VOLTAGE_EN (1 << 16) + +#define CG_PWR_GATING_CNTL 0x7ac +# define DYN_PWR_DOWN_EN (1 << 0) +# define ACPI_PWR_DOWN_EN (1 << 1) +# define GFX_CLK_OFF_PWR_DOWN_EN (1 << 2) +# define IOC_DISGPU_PWR_DOWN_EN (1 << 3) +# define FORCE_POWR_ON (1 << 4) +# define PGP(x) ((x) << 8) +# define PGP_MASK (0xffff << 8) +# define PGP_SHIFT 8 +# define PGU(x) ((x) << 24) +# define PGU_MASK (0xf << 24) +# define PGU_SHIFT 24 + +#define CG_CGTT_LOCAL_0 0x7d0 +#define CG_CGTT_LOCAL_1 0x7d4 + +#define DEEP_SLEEP_CNTL 0x818 +# define R_DIS (1 << 3) +# define HS(x) ((x) << 4) +# define HS_MASK (0xfff << 4) +# define HS_SHIFT 4 +# define ENABLE_DS (1 << 31) +#define DEEP_SLEEP_CNTL2 0x81c +# define LB_UFP_EN (1 << 0) +# define INOUT_C(x) ((x) << 4) +# define INOUT_C_MASK (0xff << 4) +# define INOUT_C_SHIFT 4 + +#define CG_SCRATCH2 0x824 + +#define CG_SCLK_DPM_CTRL_11 0x830 + +#define HW_REV 0x5564 +# define ATI_REV_ID_MASK (0xf << 28) +# define ATI_REV_ID_SHIFT 28 +/* 0 = A0, 1 = A1, 2 = B0, 3 = C0, etc. */ + +#define DOUT_SCRATCH3 0x611c + +#define GB_ADDR_CONFIG 0x98f8 + +#endif
On Wed, Jun 26, 2013 at 09:22:40AM -0400, alexdeucher@gmail.com wrote:
From: Alex Deucher alexander.deucher@amd.com
This adds dpm support for sumo asics. This includes:
- clockgating
- powergating
- dynamic engine clock scaling
- dynamic voltage scaling
set radeon.dpm=1 to enable it.
Signed-off-by: Alex Deucher alexander.deucher@amd.com
Reviewed-by: Jerome Glisse jglisse@redhat.com
drivers/gpu/drm/radeon/Makefile | 2 +- drivers/gpu/drm/radeon/radeon_asic.c | 12 + drivers/gpu/drm/radeon/radeon_asic.h | 11 + drivers/gpu/drm/radeon/radeon_pm.c | 3 + drivers/gpu/drm/radeon/sumo_dpm.c | 1677 ++++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/sumo_dpm.h | 199 ++++ drivers/gpu/drm/radeon/sumo_smc.c | 224 +++++ drivers/gpu/drm/radeon/sumod.h | 362 ++++++++ 8 files changed, 2489 insertions(+), 1 deletions(-) create mode 100644 drivers/gpu/drm/radeon/sumo_dpm.c create mode 100644 drivers/gpu/drm/radeon/sumo_dpm.h create mode 100644 drivers/gpu/drm/radeon/sumo_smc.c create mode 100644 drivers/gpu/drm/radeon/sumod.h
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index af3dd8f..7c77e1d 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile @@ -78,7 +78,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \ atombios_encoders.o radeon_semaphore.o radeon_sa.o atombios_i2c.o si.o \ si_blit_shaders.o radeon_prime.o radeon_uvd.o cik.o cik_blit_shaders.o \ r600_dpm.o rs780_dpm.o rv6xx_dpm.o rv770_dpm.o rv730_dpm.o rv740_dpm.o \
- rv770_smc.o cypress_dpm.o btc_dpm.o
- rv770_smc.o cypress_dpm.o btc_dpm.o sumo_dpm.o sumo_smc.o
radeon-$(CONFIG_COMPAT) += radeon_ioc32.o radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index 20f65ad..8b541b4 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1539,6 +1539,18 @@ static struct radeon_asic sumo_asic = { .set_uvd_clocks = &sumo_set_uvd_clocks, .get_temperature = &sumo_get_temp, },
- .dpm = {
.init = &sumo_dpm_init,
.setup_asic = &sumo_dpm_setup_asic,
.enable = &sumo_dpm_enable,
.disable = &sumo_dpm_disable,
.set_power_state = &sumo_dpm_set_power_state,
.display_configuration_changed = &sumo_dpm_display_configuration_changed,
.fini = &sumo_dpm_fini,
.get_sclk = &sumo_dpm_get_sclk,
.get_mclk = &sumo_dpm_get_mclk,
.print_power_state = &sumo_dpm_print_power_state,
- }, .pflip = { .pre_page_flip = &evergreen_pre_page_flip, .page_flip = &evergreen_page_flip,
diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 214429b..f6c0984 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -536,6 +536,17 @@ int btc_dpm_enable(struct radeon_device *rdev); void btc_dpm_disable(struct radeon_device *rdev); int btc_dpm_set_power_state(struct radeon_device *rdev); void btc_dpm_fini(struct radeon_device *rdev); +int sumo_dpm_init(struct radeon_device *rdev); +int sumo_dpm_enable(struct radeon_device *rdev); +void sumo_dpm_disable(struct radeon_device *rdev); +int sumo_dpm_set_power_state(struct radeon_device *rdev); +void sumo_dpm_setup_asic(struct radeon_device *rdev); +void sumo_dpm_display_configuration_changed(struct radeon_device *rdev); +void sumo_dpm_fini(struct radeon_device *rdev); +u32 sumo_dpm_get_sclk(struct radeon_device *rdev, bool low); +u32 sumo_dpm_get_mclk(struct radeon_device *rdev, bool low); +void sumo_dpm_print_power_state(struct radeon_device *rdev,
struct radeon_ps *ps);
/*
- cayman
diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 0a770b5..b924bcc1 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -1049,6 +1049,9 @@ int radeon_pm_init(struct radeon_device *rdev) case CHIP_JUNIPER: case CHIP_CYPRESS: case CHIP_HEMLOCK:
- case CHIP_PALM:
- case CHIP_SUMO:
- case CHIP_SUMO2: case CHIP_BARTS: case CHIP_TURKS: case CHIP_CAICOS:
diff --git a/drivers/gpu/drm/radeon/sumo_dpm.c b/drivers/gpu/drm/radeon/sumo_dpm.c new file mode 100644 index 0000000..4f49450 --- /dev/null +++ b/drivers/gpu/drm/radeon/sumo_dpm.c @@ -0,0 +1,1677 @@ +/*
- Copyright 2012 Advanced Micro Devices, 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.
- */
+#include "drmP.h" +#include "radeon.h" +#include "sumod.h" +#include "r600_dpm.h" +#include "cypress_dpm.h" +#include "sumo_dpm.h" +#include "atom.h"
+#define SUMO_MAX_DEEPSLEEP_DIVIDER_ID 5 +#define SUMO_MINIMUM_ENGINE_CLOCK 800 +#define BOOST_DPM_LEVEL 7
+static const u32 sumo_utc[SUMO_PM_NUMBER_OF_TC] = +{
- SUMO_UTC_DFLT_00,
- SUMO_UTC_DFLT_01,
- SUMO_UTC_DFLT_02,
- SUMO_UTC_DFLT_03,
- SUMO_UTC_DFLT_04,
- SUMO_UTC_DFLT_05,
- SUMO_UTC_DFLT_06,
- SUMO_UTC_DFLT_07,
- SUMO_UTC_DFLT_08,
- SUMO_UTC_DFLT_09,
- SUMO_UTC_DFLT_10,
- SUMO_UTC_DFLT_11,
- SUMO_UTC_DFLT_12,
- SUMO_UTC_DFLT_13,
- SUMO_UTC_DFLT_14,
+};
+static const u32 sumo_dtc[SUMO_PM_NUMBER_OF_TC] = +{
- SUMO_DTC_DFLT_00,
- SUMO_DTC_DFLT_01,
- SUMO_DTC_DFLT_02,
- SUMO_DTC_DFLT_03,
- SUMO_DTC_DFLT_04,
- SUMO_DTC_DFLT_05,
- SUMO_DTC_DFLT_06,
- SUMO_DTC_DFLT_07,
- SUMO_DTC_DFLT_08,
- SUMO_DTC_DFLT_09,
- SUMO_DTC_DFLT_10,
- SUMO_DTC_DFLT_11,
- SUMO_DTC_DFLT_12,
- SUMO_DTC_DFLT_13,
- SUMO_DTC_DFLT_14,
+};
+struct sumo_ps *sumo_get_ps(struct radeon_ps *rps) +{
- struct sumo_ps *ps = rps->ps_priv;
- return ps;
+}
+struct sumo_power_info *sumo_get_pi(struct radeon_device *rdev) +{
- struct sumo_power_info *pi = rdev->pm.dpm.priv;
- return pi;
+}
+u32 sumo_get_xclk(struct radeon_device *rdev) +{
- return rdev->clock.spll.reference_freq;
+}
+static void sumo_gfx_clockgating_enable(struct radeon_device *rdev, bool enable) +{
- if (enable)
WREG32_P(SCLK_PWRMGT_CNTL, DYN_GFX_CLK_OFF_EN, ~DYN_GFX_CLK_OFF_EN);
- else {
WREG32_P(SCLK_PWRMGT_CNTL, 0, ~DYN_GFX_CLK_OFF_EN);
WREG32_P(SCLK_PWRMGT_CNTL, GFX_CLK_FORCE_ON, ~GFX_CLK_FORCE_ON);
WREG32_P(SCLK_PWRMGT_CNTL, 0, ~GFX_CLK_FORCE_ON);
RREG32(GB_ADDR_CONFIG);
- }
+}
+#define CGCG_CGTT_LOCAL0_MASK 0xE5BFFFFF +#define CGCG_CGTT_LOCAL1_MASK 0xEFFF07FF
+static void sumo_mg_clockgating_enable(struct radeon_device *rdev, bool enable) +{
- u32 local0;
- u32 local1;
- local0 = RREG32(CG_CGTT_LOCAL_0);
- local1 = RREG32(CG_CGTT_LOCAL_1);
- if (enable) {
WREG32(CG_CGTT_LOCAL_0, (0 & CGCG_CGTT_LOCAL0_MASK) | (local0 & ~CGCG_CGTT_LOCAL0_MASK) );
WREG32(CG_CGTT_LOCAL_1, (0 & CGCG_CGTT_LOCAL1_MASK) | (local1 & ~CGCG_CGTT_LOCAL1_MASK) );
- } else {
WREG32(CG_CGTT_LOCAL_0, (0xFFFFFFFF & CGCG_CGTT_LOCAL0_MASK) | (local0 & ~CGCG_CGTT_LOCAL0_MASK) );
WREG32(CG_CGTT_LOCAL_1, (0xFFFFCFFF & CGCG_CGTT_LOCAL1_MASK) | (local1 & ~CGCG_CGTT_LOCAL1_MASK) );
- }
+}
+static void sumo_program_git(struct radeon_device *rdev) +{
- u32 p, u;
- u32 xclk = sumo_get_xclk(rdev);
- r600_calculate_u_and_p(SUMO_GICST_DFLT,
xclk, 16, &p, &u);
- WREG32_P(CG_GIT, CG_GICST(p), ~CG_GICST_MASK);
+}
+static void sumo_program_grsd(struct radeon_device *rdev) +{
- u32 p, u;
- u32 xclk = sumo_get_xclk(rdev);
- u32 grs = 256 * 25 / 100;
- r600_calculate_u_and_p(1, xclk, 14, &p, &u);
- WREG32(CG_GCOOR, PHC(grs) | SDC(p) | SU(u));
+}
+static void sumo_gfx_clockgating_initialize(struct radeon_device *rdev) +{
- sumo_program_git(rdev);
- sumo_program_grsd(rdev);
+}
+static void sumo_gfx_powergating_initialize(struct radeon_device *rdev) +{
- u32 rcu_pwr_gating_cntl;
- u32 p, u;
- u32 p_c, p_p, d_p;
- u32 r_t, i_t;
- u32 xclk = sumo_get_xclk(rdev);
- if (rdev->family == CHIP_PALM) {
p_c = 4;
d_p = 10;
r_t = 10;
i_t = 4;
p_p = 50 + 1000/200 + 6 * 32;
- } else {
p_c = 16;
d_p = 50;
r_t = 50;
i_t = 50;
p_p = 113;
- }
- WREG32(CG_SCRATCH2, 0x01B60A17);
- r600_calculate_u_and_p(SUMO_GFXPOWERGATINGT_DFLT,
xclk, 16, &p, &u);
- WREG32_P(CG_PWR_GATING_CNTL, PGP(p) | PGU(u),
~(PGP_MASK | PGU_MASK));
- r600_calculate_u_and_p(SUMO_VOLTAGEDROPT_DFLT,
xclk, 16, &p, &u);
- WREG32_P(CG_CG_VOLTAGE_CNTL, PGP(p) | PGU(u),
~(PGP_MASK | PGU_MASK));
- if (rdev->family == CHIP_PALM) {
WREG32_RCU(RCU_PWR_GATING_SEQ0, 0x10103210);
WREG32_RCU(RCU_PWR_GATING_SEQ1, 0x10101010);
- } else {
WREG32_RCU(RCU_PWR_GATING_SEQ0, 0x76543210);
WREG32_RCU(RCU_PWR_GATING_SEQ1, 0xFEDCBA98);
- }
- rcu_pwr_gating_cntl = RREG32_RCU(RCU_PWR_GATING_CNTL);
- rcu_pwr_gating_cntl &=
~(RSVD_MASK | PCV_MASK | PGS_MASK);
- rcu_pwr_gating_cntl |= PCV(p_c) | PGS(1) | PWR_GATING_EN;
- if (rdev->family == CHIP_PALM) {
rcu_pwr_gating_cntl &= ~PCP_MASK;
rcu_pwr_gating_cntl |= PCP(0x77);
- }
- WREG32_RCU(RCU_PWR_GATING_CNTL, rcu_pwr_gating_cntl);
- rcu_pwr_gating_cntl = RREG32_RCU(RCU_PWR_GATING_CNTL_2);
- rcu_pwr_gating_cntl &= ~(MPPU_MASK | MPPD_MASK);
- rcu_pwr_gating_cntl |= MPPU(p_p) | MPPD(50);
- WREG32_RCU(RCU_PWR_GATING_CNTL_2, rcu_pwr_gating_cntl);
- rcu_pwr_gating_cntl = RREG32_RCU(RCU_PWR_GATING_CNTL_3);
- rcu_pwr_gating_cntl &= ~(DPPU_MASK | DPPD_MASK);
- rcu_pwr_gating_cntl |= DPPU(d_p) | DPPD(50);
- WREG32_RCU(RCU_PWR_GATING_CNTL_3, rcu_pwr_gating_cntl);
- rcu_pwr_gating_cntl = RREG32_RCU(RCU_PWR_GATING_CNTL_4);
- rcu_pwr_gating_cntl &= ~(RT_MASK | IT_MASK);
- rcu_pwr_gating_cntl |= RT(r_t) | IT(i_t);
- WREG32_RCU(RCU_PWR_GATING_CNTL_4, rcu_pwr_gating_cntl);
- if (rdev->family == CHIP_PALM)
WREG32_RCU(RCU_PWR_GATING_CNTL_5, 0xA02);
- sumo_smu_pg_init(rdev);
- rcu_pwr_gating_cntl = RREG32_RCU(RCU_PWR_GATING_CNTL);
- rcu_pwr_gating_cntl &=
~(RSVD_MASK | PCV_MASK | PGS_MASK);
- rcu_pwr_gating_cntl |= PCV(p_c) | PGS(4) | PWR_GATING_EN;
- if (rdev->family == CHIP_PALM) {
rcu_pwr_gating_cntl &= ~PCP_MASK;
rcu_pwr_gating_cntl |= PCP(0x77);
- }
- WREG32_RCU(RCU_PWR_GATING_CNTL, rcu_pwr_gating_cntl);
- if (rdev->family == CHIP_PALM) {
rcu_pwr_gating_cntl = RREG32_RCU(RCU_PWR_GATING_CNTL_2);
rcu_pwr_gating_cntl &= ~(MPPU_MASK | MPPD_MASK);
rcu_pwr_gating_cntl |= MPPU(113) | MPPD(50);
WREG32_RCU(RCU_PWR_GATING_CNTL_2, rcu_pwr_gating_cntl);
rcu_pwr_gating_cntl = RREG32_RCU(RCU_PWR_GATING_CNTL_3);
rcu_pwr_gating_cntl &= ~(DPPU_MASK | DPPD_MASK);
rcu_pwr_gating_cntl |= DPPU(16) | DPPD(50);
WREG32_RCU(RCU_PWR_GATING_CNTL_3, rcu_pwr_gating_cntl);
- }
- sumo_smu_pg_init(rdev);
- rcu_pwr_gating_cntl = RREG32_RCU(RCU_PWR_GATING_CNTL);
- rcu_pwr_gating_cntl &=
~(RSVD_MASK | PCV_MASK | PGS_MASK);
- rcu_pwr_gating_cntl |= PGS(5) | PWR_GATING_EN;
- if (rdev->family == CHIP_PALM) {
rcu_pwr_gating_cntl |= PCV(4);
rcu_pwr_gating_cntl &= ~PCP_MASK;
rcu_pwr_gating_cntl |= PCP(0x77);
- } else
rcu_pwr_gating_cntl |= PCV(11);
- WREG32_RCU(RCU_PWR_GATING_CNTL, rcu_pwr_gating_cntl);
- if (rdev->family == CHIP_PALM) {
rcu_pwr_gating_cntl = RREG32_RCU(RCU_PWR_GATING_CNTL_2);
rcu_pwr_gating_cntl &= ~(MPPU_MASK | MPPD_MASK);
rcu_pwr_gating_cntl |= MPPU(113) | MPPD(50);
WREG32_RCU(RCU_PWR_GATING_CNTL_2, rcu_pwr_gating_cntl);
rcu_pwr_gating_cntl = RREG32_RCU(RCU_PWR_GATING_CNTL_3);
rcu_pwr_gating_cntl &= ~(DPPU_MASK | DPPD_MASK);
rcu_pwr_gating_cntl |= DPPU(22) | DPPD(50);
WREG32_RCU(RCU_PWR_GATING_CNTL_3, rcu_pwr_gating_cntl);
- }
- sumo_smu_pg_init(rdev);
+}
+static void sumo_gfx_powergating_enable(struct radeon_device *rdev, bool enable) +{
- if (enable)
WREG32_P(CG_PWR_GATING_CNTL, DYN_PWR_DOWN_EN, ~DYN_PWR_DOWN_EN);
- else {
WREG32_P(CG_PWR_GATING_CNTL, 0, ~DYN_PWR_DOWN_EN);
RREG32(GB_ADDR_CONFIG);
- }
+}
+static int sumo_enable_clock_power_gating(struct radeon_device *rdev) +{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- if (pi->enable_gfx_clock_gating)
sumo_gfx_clockgating_initialize(rdev);
- if (pi->enable_gfx_power_gating)
sumo_gfx_powergating_initialize(rdev);
- if (pi->enable_mg_clock_gating)
sumo_mg_clockgating_enable(rdev, true);
- if (pi->enable_gfx_clock_gating)
sumo_gfx_clockgating_enable(rdev, true);
- if (pi->enable_gfx_power_gating)
sumo_gfx_powergating_enable(rdev, true);
- return 0;
+}
+static void sumo_disable_clock_power_gating(struct radeon_device *rdev) +{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- if (pi->enable_gfx_clock_gating)
sumo_gfx_clockgating_enable(rdev, false);
- if (pi->enable_gfx_power_gating)
sumo_gfx_powergating_enable(rdev, false);
- if (pi->enable_mg_clock_gating)
sumo_mg_clockgating_enable(rdev, false);
+}
+static void sumo_calculate_bsp(struct radeon_device *rdev,
u32 high_clk)
+{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- u32 xclk = sumo_get_xclk(rdev);
- pi->pasi = 65535 * 100 / high_clk;
- pi->asi = 65535 * 100 / high_clk;
- r600_calculate_u_and_p(pi->asi,
xclk, 16, &pi->bsp, &pi->bsu);
- r600_calculate_u_and_p(pi->pasi,
xclk, 16, &pi->pbsp, &pi->pbsu);
- pi->dsp = BSP(pi->bsp) | BSU(pi->bsu);
- pi->psp = BSP(pi->pbsp) | BSU(pi->pbsu);
+}
+static void sumo_init_bsp(struct radeon_device *rdev) +{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- WREG32(CG_BSP_0, pi->psp);
+}
+static void sumo_program_bsp(struct radeon_device *rdev) +{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- struct sumo_ps *ps = sumo_get_ps(rdev->pm.dpm.requested_ps);
- u32 i;
- u32 highest_engine_clock = ps->levels[ps->num_levels - 1].sclk;
- if (ps->flags & SUMO_POWERSTATE_FLAGS_BOOST_STATE)
highest_engine_clock = pi->boost_pl.sclk;
- sumo_calculate_bsp(rdev, highest_engine_clock);
- for (i = 0; i < ps->num_levels - 1; i++)
WREG32(CG_BSP_0 + (i * 4), pi->dsp);
- WREG32(CG_BSP_0 + (i * 4), pi->psp);
- if (ps->flags & SUMO_POWERSTATE_FLAGS_BOOST_STATE)
WREG32(CG_BSP_0 + (BOOST_DPM_LEVEL * 4), pi->psp);
+}
+static void sumo_write_at(struct radeon_device *rdev,
u32 index, u32 value)
+{
- if (index == 0)
WREG32(CG_AT_0, value);
- else if (index == 1)
WREG32(CG_AT_1, value);
- else if (index == 2)
WREG32(CG_AT_2, value);
- else if (index == 3)
WREG32(CG_AT_3, value);
- else if (index == 4)
WREG32(CG_AT_4, value);
- else if (index == 5)
WREG32(CG_AT_5, value);
- else if (index == 6)
WREG32(CG_AT_6, value);
- else if (index == 7)
WREG32(CG_AT_7, value);
+}
+static void sumo_program_at(struct radeon_device *rdev) +{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- struct sumo_ps *ps = sumo_get_ps(rdev->pm.dpm.requested_ps);
- u32 asi;
- u32 i;
- u32 m_a;
- u32 a_t;
- u32 r[SUMO_MAX_HARDWARE_POWERLEVELS];
- u32 l[SUMO_MAX_HARDWARE_POWERLEVELS];
- r[0] = SUMO_R_DFLT0;
- r[1] = SUMO_R_DFLT1;
- r[2] = SUMO_R_DFLT2;
- r[3] = SUMO_R_DFLT3;
- r[4] = SUMO_R_DFLT4;
- l[0] = SUMO_L_DFLT0;
- l[1] = SUMO_L_DFLT1;
- l[2] = SUMO_L_DFLT2;
- l[3] = SUMO_L_DFLT3;
- l[4] = SUMO_L_DFLT4;
- for (i = 0; i < ps->num_levels; i++) {
asi = (i == ps->num_levels - 1) ? pi->pasi : pi->asi;
m_a = asi * ps->levels[i].sclk / 100;
a_t = CG_R(m_a * r[i] / 100) | CG_L(m_a * l[i] / 100);
sumo_write_at(rdev, i, a_t);
- }
- if (ps->flags & SUMO_POWERSTATE_FLAGS_BOOST_STATE) {
asi = pi->pasi;
m_a = asi * pi->boost_pl.sclk / 100;
a_t = CG_R(m_a * r[ps->num_levels - 1] / 100) |
CG_L(m_a * l[ps->num_levels - 1] / 100);
sumo_write_at(rdev, BOOST_DPM_LEVEL, a_t);
- }
+}
+static void sumo_program_tp(struct radeon_device *rdev) +{
- int i;
- enum r600_td td = R600_TD_DFLT;
- for (i = 0; i < SUMO_PM_NUMBER_OF_TC; i++) {
WREG32_P(CG_FFCT_0 + (i * 4), UTC_0(sumo_utc[i]), ~UTC_0_MASK);
WREG32_P(CG_FFCT_0 + (i * 4), DTC_0(sumo_dtc[i]), ~DTC_0_MASK);
- }
- if (td == R600_TD_AUTO)
WREG32_P(SCLK_PWRMGT_CNTL, 0, ~FIR_FORCE_TREND_SEL);
- else
WREG32_P(SCLK_PWRMGT_CNTL, FIR_FORCE_TREND_SEL, ~FIR_FORCE_TREND_SEL);
- if (td == R600_TD_UP)
WREG32_P(SCLK_PWRMGT_CNTL, 0, ~FIR_TREND_MODE);
- if (td == R600_TD_DOWN)
WREG32_P(SCLK_PWRMGT_CNTL, FIR_TREND_MODE, ~FIR_TREND_MODE);
+}
+static void sumo_program_vc(struct radeon_device *rdev) +{
- WREG32(CG_FTV, SUMO_VRC_DFLT);
+}
+static void sumo_clear_vc(struct radeon_device *rdev) +{
- WREG32(CG_FTV, 0);
+}
+static void sumo_program_sstp(struct radeon_device *rdev) +{
- u32 p, u;
- u32 xclk = sumo_get_xclk(rdev);
- r600_calculate_u_and_p(SUMO_SST_DFLT,
xclk, 16, &p, &u);
- WREG32(CG_SSP, SSTU(u) | SST(p));
+}
+static void sumo_set_divider_value(struct radeon_device *rdev,
u32 index, u32 divider)
+{
- u32 reg_index = index / 4;
- u32 field_index = index % 4;
- if (field_index == 0)
WREG32_P(CG_SCLK_DPM_CTRL + (reg_index * 4),
SCLK_FSTATE_0_DIV(divider), ~SCLK_FSTATE_0_DIV_MASK);
- else if (field_index == 1)
WREG32_P(CG_SCLK_DPM_CTRL + (reg_index * 4),
SCLK_FSTATE_1_DIV(divider), ~SCLK_FSTATE_1_DIV_MASK);
- else if (field_index == 2)
WREG32_P(CG_SCLK_DPM_CTRL + (reg_index * 4),
SCLK_FSTATE_2_DIV(divider), ~SCLK_FSTATE_2_DIV_MASK);
- else if (field_index == 3)
WREG32_P(CG_SCLK_DPM_CTRL + (reg_index * 4),
SCLK_FSTATE_3_DIV(divider), ~SCLK_FSTATE_3_DIV_MASK);
+}
+static void sumo_set_ds_dividers(struct radeon_device *rdev,
u32 index, u32 divider)
+{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- if (pi->enable_sclk_ds) {
u32 dpm_ctrl = RREG32(CG_SCLK_DPM_CTRL_6);
dpm_ctrl &= ~(0x7 << (index * 3));
dpm_ctrl |= (divider << (index * 3));
WREG32(CG_SCLK_DPM_CTRL_6, dpm_ctrl);
- }
+}
+static void sumo_set_ss_dividers(struct radeon_device *rdev,
u32 index, u32 divider)
+{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- if (pi->enable_sclk_ds) {
u32 dpm_ctrl = RREG32(CG_SCLK_DPM_CTRL_11);
dpm_ctrl &= ~(0x7 << (index * 3));
dpm_ctrl |= (divider << (index * 3));
WREG32(CG_SCLK_DPM_CTRL_11, dpm_ctrl);
- }
+}
+static void sumo_set_vid(struct radeon_device *rdev, u32 index, u32 vid) +{
- u32 voltage_cntl = RREG32(CG_DPM_VOLTAGE_CNTL);
- voltage_cntl &= ~(DPM_STATE0_LEVEL_MASK << (index * 2));
- voltage_cntl |= (vid << (DPM_STATE0_LEVEL_SHIFT + index * 2));
- WREG32(CG_DPM_VOLTAGE_CNTL, voltage_cntl);
+}
+static void sumo_set_allos_gnb_slow(struct radeon_device *rdev, u32 index, u32 gnb_slow) +{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- u32 temp = gnb_slow;
- u32 cg_sclk_dpm_ctrl_3;
- if (pi->driver_nbps_policy_disable)
temp = 1;
- cg_sclk_dpm_ctrl_3 = RREG32(CG_SCLK_DPM_CTRL_3);
- cg_sclk_dpm_ctrl_3 &= ~(GNB_SLOW_FSTATE_0_MASK << index);
- cg_sclk_dpm_ctrl_3 |= (temp << (GNB_SLOW_FSTATE_0_SHIFT + index));
- WREG32(CG_SCLK_DPM_CTRL_3, cg_sclk_dpm_ctrl_3);
+}
+static void sumo_program_power_level(struct radeon_device *rdev,
struct sumo_pl *pl, u32 index)
+{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- int ret;
- struct atom_clock_dividers dividers;
- u32 ds_en = RREG32(DEEP_SLEEP_CNTL) & ENABLE_DS;
ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
pl->sclk, false, ÷rs);
Indentation issue (space instead of tab).
- if (ret)
return;
- sumo_set_divider_value(rdev, index, dividers.post_div);
- sumo_set_vid(rdev, index, pl->vddc_index);
- if (pl->ss_divider_index == 0 || pl->ds_divider_index == 0) {
if (ds_en)
WREG32_P(DEEP_SLEEP_CNTL, 0, ~ENABLE_DS);
- } else {
sumo_set_ss_dividers(rdev, index, pl->ss_divider_index);
sumo_set_ds_dividers(rdev, index, pl->ds_divider_index);
if (!ds_en)
WREG32_P(DEEP_SLEEP_CNTL, ENABLE_DS, ~ENABLE_DS);
- }
- sumo_set_allos_gnb_slow(rdev, index, pl->allow_gnb_slow);
- if (pi->enable_boost)
sumo_set_tdp_limit(rdev, index, pl->sclk_dpm_tdp_limit);
+}
+static void sumo_power_level_enable(struct radeon_device *rdev, u32 index, bool enable) +{
- u32 reg_index = index / 4;
- u32 field_index = index % 4;
- if (field_index == 0)
WREG32_P(CG_SCLK_DPM_CTRL + (reg_index * 4),
enable ? SCLK_FSTATE_0_VLD : 0, ~SCLK_FSTATE_0_VLD);
- else if (field_index == 1)
WREG32_P(CG_SCLK_DPM_CTRL + (reg_index * 4),
enable ? SCLK_FSTATE_1_VLD : 0, ~SCLK_FSTATE_1_VLD);
- else if (field_index == 2)
WREG32_P(CG_SCLK_DPM_CTRL + (reg_index * 4),
enable ? SCLK_FSTATE_2_VLD : 0, ~SCLK_FSTATE_2_VLD);
- else if (field_index == 3)
WREG32_P(CG_SCLK_DPM_CTRL + (reg_index * 4),
enable ? SCLK_FSTATE_3_VLD : 0, ~SCLK_FSTATE_3_VLD);
+}
+static bool sumo_dpm_enabled(struct radeon_device *rdev) +{
- if (RREG32(CG_SCLK_DPM_CTRL_3) & DPM_SCLK_ENABLE)
return true;
- else
return false;
+}
+static void sumo_start_dpm(struct radeon_device *rdev) +{
- WREG32_P(CG_SCLK_DPM_CTRL_3, DPM_SCLK_ENABLE, ~DPM_SCLK_ENABLE);
+}
+static void sumo_stop_dpm(struct radeon_device *rdev) +{
- WREG32_P(CG_SCLK_DPM_CTRL_3, 0, ~DPM_SCLK_ENABLE);
+}
+static void sumo_set_forced_mode(struct radeon_device *rdev, bool enable) +{
- if (enable)
WREG32_P(CG_SCLK_DPM_CTRL_3, FORCE_SCLK_STATE_EN, ~FORCE_SCLK_STATE_EN);
- else
WREG32_P(CG_SCLK_DPM_CTRL_3, 0, ~FORCE_SCLK_STATE_EN);
+}
+static void sumo_set_forced_mode_enabled(struct radeon_device *rdev) +{
- int i;
- sumo_set_forced_mode(rdev, true);
- for (i = 0; i < rdev->usec_timeout; i++) {
if (RREG32(CG_SCLK_STATUS) & SCLK_OVERCLK_DETECT)
break;
udelay(1);
- }
+}
+static void sumo_wait_for_level_0(struct radeon_device *rdev) +{
- int i;
- for (i = 0; i < rdev->usec_timeout; i++) {
if ((RREG32(TARGET_AND_CURRENT_PROFILE_INDEX) & CURR_SCLK_INDEX_MASK) == 0)
break;
udelay(1);
- }
- for (i = 0; i < rdev->usec_timeout; i++) {
if ((RREG32(TARGET_AND_CURRENT_PROFILE_INDEX) & CURR_INDEX_MASK) == 0)
break;
udelay(1);
- }
+}
+static void sumo_set_forced_mode_disabled(struct radeon_device *rdev) +{
- sumo_set_forced_mode(rdev, false);
+}
+static void sumo_enable_power_level_0(struct radeon_device *rdev) +{
- sumo_power_level_enable(rdev, 0, true);
+}
+static void sumo_patch_boost_state(struct radeon_device *rdev) +{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- struct sumo_ps *new_ps = sumo_get_ps(rdev->pm.dpm.requested_ps);
- if (new_ps->flags & SUMO_POWERSTATE_FLAGS_BOOST_STATE) {
pi->boost_pl = new_ps->levels[new_ps->num_levels - 1];
pi->boost_pl.sclk = pi->sys_info.boost_sclk;
pi->boost_pl.vddc_index = pi->sys_info.boost_vid_2bit;
pi->boost_pl.sclk_dpm_tdp_limit = pi->sys_info.sclk_dpm_tdp_limit_boost;
- }
+}
+static void sumo_pre_notify_alt_vddnb_change(struct radeon_device *rdev) +{
- struct sumo_ps *new_ps = sumo_get_ps(rdev->pm.dpm.requested_ps);
- struct sumo_ps *old_ps = sumo_get_ps(rdev->pm.dpm.current_ps);
- u32 nbps1_old = 0;
- u32 nbps1_new = 0;
- if (old_ps != NULL)
nbps1_old = (old_ps->flags & SUMO_POWERSTATE_FLAGS_FORCE_NBPS1_STATE) ? 1 : 0;
- nbps1_new = (new_ps->flags & SUMO_POWERSTATE_FLAGS_FORCE_NBPS1_STATE) ? 1 : 0;
- if (nbps1_old == 1 && nbps1_new == 0)
sumo_smu_notify_alt_vddnb_change(rdev, 0, 0);
+}
+static void sumo_post_notify_alt_vddnb_change(struct radeon_device *rdev) +{
- struct sumo_ps *new_ps = sumo_get_ps(rdev->pm.dpm.requested_ps);
- struct sumo_ps *old_ps = sumo_get_ps(rdev->pm.dpm.current_ps);
- u32 nbps1_old = 0;
- u32 nbps1_new = 0;
- if (old_ps != NULL)
nbps1_old = (old_ps->flags & SUMO_POWERSTATE_FLAGS_FORCE_NBPS1_STATE)? 1 : 0;
- nbps1_new = (new_ps->flags & SUMO_POWERSTATE_FLAGS_FORCE_NBPS1_STATE)? 1 : 0;
- if (nbps1_old == 0 && nbps1_new == 1)
sumo_smu_notify_alt_vddnb_change(rdev, 1, 1);
+}
+static void sumo_enable_boost(struct radeon_device *rdev, bool enable) +{
- struct sumo_ps *new_ps = sumo_get_ps(rdev->pm.dpm.requested_ps);
- if (enable) {
if (new_ps->flags & SUMO_POWERSTATE_FLAGS_BOOST_STATE)
sumo_boost_state_enable(rdev, true);
- } else
sumo_boost_state_enable(rdev, false);
+}
+static void sumo_update_current_power_levels(struct radeon_device *rdev) +{
- struct sumo_ps *new_ps = sumo_get_ps(rdev->pm.dpm.requested_ps);
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- pi->current_ps = *new_ps;
+}
+static void sumo_set_forced_level(struct radeon_device *rdev, u32 index) +{
- WREG32_P(CG_SCLK_DPM_CTRL_3, FORCE_SCLK_STATE(index), ~FORCE_SCLK_STATE_MASK);
+}
+static void sumo_set_forced_level_0(struct radeon_device *rdev) +{
- sumo_set_forced_level(rdev, 0);
+}
+static void sumo_program_wl(struct radeon_device *rdev) +{
- struct sumo_ps *new_ps = sumo_get_ps(rdev->pm.dpm.requested_ps);
- u32 dpm_ctrl4 = RREG32(CG_SCLK_DPM_CTRL_4);
- dpm_ctrl4 &= 0xFFFFFF00;
- dpm_ctrl4 |= (1 << (new_ps->num_levels - 1));
- if (new_ps->flags & SUMO_POWERSTATE_FLAGS_BOOST_STATE)
dpm_ctrl4 |= (1 << BOOST_DPM_LEVEL);
- WREG32(CG_SCLK_DPM_CTRL_4, dpm_ctrl4);
+}
+static void sumo_program_power_levels_0_to_n(struct radeon_device *rdev) +{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- struct sumo_ps *new_ps = sumo_get_ps(rdev->pm.dpm.requested_ps);
- struct sumo_ps *old_ps = sumo_get_ps(rdev->pm.dpm.current_ps);
- u32 i;
- u32 n_current_state_levels = (old_ps == NULL) ? 1 : old_ps->num_levels;
- for (i = 0; i < new_ps->num_levels; i++) {
sumo_program_power_level(rdev, &new_ps->levels[i], i);
sumo_power_level_enable(rdev, i, true);
- }
- for (i = new_ps->num_levels; i < n_current_state_levels; i++)
sumo_power_level_enable(rdev, i, false);
- if (new_ps->flags & SUMO_POWERSTATE_FLAGS_BOOST_STATE)
sumo_program_power_level(rdev, &pi->boost_pl, BOOST_DPM_LEVEL);
+}
+static void sumo_enable_acpi_pm(struct radeon_device *rdev) +{
- WREG32_P(GENERAL_PWRMGT, STATIC_PM_EN, ~STATIC_PM_EN);
+}
+static void sumo_program_power_level_enter_state(struct radeon_device *rdev) +{
- WREG32_P(CG_SCLK_DPM_CTRL_5, SCLK_FSTATE_BOOTUP(0), ~SCLK_FSTATE_BOOTUP_MASK);
+}
+static void sumo_program_acpi_power_level(struct radeon_device *rdev) +{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- struct atom_clock_dividers dividers;
- int ret;
ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
pi->acpi_pl.sclk,
false, ÷rs);
- if (ret)
return;
- WREG32_P(CG_ACPI_CNTL, SCLK_ACPI_DIV(dividers.post_div), ~SCLK_ACPI_DIV_MASK);
- WREG32_P(CG_ACPI_VOLTAGE_CNTL, 0, ~ACPI_VOLTAGE_EN);
+}
+static void sumo_program_bootup_state(struct radeon_device *rdev) +{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- u32 dpm_ctrl4 = RREG32(CG_SCLK_DPM_CTRL_4);
- u32 i;
- sumo_program_power_level(rdev, &pi->boot_pl, 0);
- dpm_ctrl4 &= 0xFFFFFF00;
- WREG32(CG_SCLK_DPM_CTRL_4, dpm_ctrl4);
- for (i = 1; i < 8; i++)
sumo_power_level_enable(rdev, i, false);
+}
+static void sumo_take_smu_control(struct radeon_device *rdev, bool enable) +{
- u32 v = RREG32(DOUT_SCRATCH3);
- if (enable)
v |= 0x4;
- else
v &= 0xFFFFFFFB;
- WREG32(DOUT_SCRATCH3, v);
+}
+static void sumo_enable_sclk_ds(struct radeon_device *rdev, bool enable) +{
- if (enable) {
u32 deep_sleep_cntl = RREG32(DEEP_SLEEP_CNTL);
u32 deep_sleep_cntl2 = RREG32(DEEP_SLEEP_CNTL2);
u32 t = 1;
deep_sleep_cntl &= ~R_DIS;
deep_sleep_cntl &= ~HS_MASK;
deep_sleep_cntl |= HS(t > 4095 ? 4095 : t);
deep_sleep_cntl2 |= LB_UFP_EN;
deep_sleep_cntl2 &= INOUT_C_MASK;
deep_sleep_cntl2 |= INOUT_C(0xf);
WREG32(DEEP_SLEEP_CNTL2, deep_sleep_cntl2);
WREG32(DEEP_SLEEP_CNTL, deep_sleep_cntl);
- } else
WREG32_P(DEEP_SLEEP_CNTL, 0, ~ENABLE_DS);
+}
+static void sumo_program_bootup_at(struct radeon_device *rdev) +{
- WREG32_P(CG_AT_0, CG_R(0xffff), ~CG_R_MASK);
- WREG32_P(CG_AT_0, CG_L(0), ~CG_L_MASK);
+}
+static void sumo_reset_am(struct radeon_device *rdev) +{
- WREG32_P(SCLK_PWRMGT_CNTL, FIR_RESET, ~FIR_RESET);
+}
+static void sumo_start_am(struct radeon_device *rdev) +{
- WREG32_P(SCLK_PWRMGT_CNTL, 0, ~FIR_RESET);
+}
+static void sumo_program_ttp(struct radeon_device *rdev) +{
- u32 xclk = sumo_get_xclk(rdev);
- u32 p, u;
- u32 cg_sclk_dpm_ctrl_5 = RREG32(CG_SCLK_DPM_CTRL_5);
- r600_calculate_u_and_p(1000,
xclk, 16, &p, &u);
- cg_sclk_dpm_ctrl_5 &= ~(TT_TP_MASK | TT_TU_MASK);
- cg_sclk_dpm_ctrl_5 |= TT_TP(p) | TT_TU(u);
- WREG32(CG_SCLK_DPM_CTRL_5, cg_sclk_dpm_ctrl_5);
+}
+static void sumo_program_ttt(struct radeon_device *rdev) +{
- u32 cg_sclk_dpm_ctrl_3 = RREG32(CG_SCLK_DPM_CTRL_3);
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- cg_sclk_dpm_ctrl_3 &= ~(GNB_TT_MASK | GNB_THERMTHRO_MASK);
- cg_sclk_dpm_ctrl_3 |= GNB_TT(pi->thermal_auto_throttling + 49);
- WREG32(CG_SCLK_DPM_CTRL_3, cg_sclk_dpm_ctrl_3);
+}
+static void sumo_enable_voltage_scaling(struct radeon_device *rdev, bool enable) +{
- if (enable) {
WREG32_P(CG_DPM_VOLTAGE_CNTL, DPM_VOLTAGE_EN, ~DPM_VOLTAGE_EN);
WREG32_P(CG_CG_VOLTAGE_CNTL, 0, ~CG_VOLTAGE_EN);
- } else {
WREG32_P(CG_CG_VOLTAGE_CNTL, CG_VOLTAGE_EN, ~CG_VOLTAGE_EN);
WREG32_P(CG_DPM_VOLTAGE_CNTL, 0, ~DPM_VOLTAGE_EN);
- }
+}
+static void sumo_override_cnb_thermal_events(struct radeon_device *rdev) +{
- WREG32_P(CG_SCLK_DPM_CTRL_3, CNB_THERMTHRO_MASK_SCLK,
~CNB_THERMTHRO_MASK_SCLK);
+}
+static void sumo_program_dc_hto(struct radeon_device *rdev) +{
- u32 cg_sclk_dpm_ctrl_4 = RREG32(CG_SCLK_DPM_CTRL_4);
- u32 p, u;
- u32 xclk = sumo_get_xclk(rdev);
- r600_calculate_u_and_p(100000,
xclk, 14, &p, &u);
- cg_sclk_dpm_ctrl_4 &= ~(DC_HDC_MASK | DC_HU_MASK);
- cg_sclk_dpm_ctrl_4 |= DC_HDC(p) | DC_HU(u);
- WREG32(CG_SCLK_DPM_CTRL_4, cg_sclk_dpm_ctrl_4);
+}
+static void sumo_force_nbp_state(struct radeon_device *rdev) +{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- struct sumo_ps *new_ps = sumo_get_ps(rdev->pm.dpm.requested_ps);
- if (!pi->driver_nbps_policy_disable) {
if (new_ps->flags & SUMO_POWERSTATE_FLAGS_FORCE_NBPS1_STATE)
WREG32_P(CG_SCLK_DPM_CTRL_3, FORCE_NB_PSTATE_1, ~FORCE_NB_PSTATE_1);
else
WREG32_P(CG_SCLK_DPM_CTRL_3, 0, ~FORCE_NB_PSTATE_1);
- }
+}
+static u32 sumo_get_sleep_divider_from_id(u32 id) +{
- return 1 << id;
+}
+static u32 sumo_get_sleep_divider_id_from_clock(struct radeon_device *rdev,
u32 sclk,
u32 min_sclk_in_sr)
+{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- u32 i;
- u32 temp;
- u32 min = (min_sclk_in_sr > SUMO_MINIMUM_ENGINE_CLOCK) ?
min_sclk_in_sr : SUMO_MINIMUM_ENGINE_CLOCK;
- if (sclk < min)
return 0;
- if (!pi->enable_sclk_ds)
return 0;
- for (i = SUMO_MAX_DEEPSLEEP_DIVIDER_ID; ; i--) {
temp = sclk / sumo_get_sleep_divider_from_id(i);
if (temp >= min || i == 0)
break;
- }
- return i;
+}
+static u32 sumo_get_valid_engine_clock(struct radeon_device *rdev,
u32 lower_limit)
+{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- u32 i;
- for (i = 0; i < pi->sys_info.sclk_voltage_mapping_table.num_max_dpm_entries; i++) {
if (pi->sys_info.sclk_voltage_mapping_table.entries[i].sclk_frequency >= lower_limit)
return pi->sys_info.sclk_voltage_mapping_table.entries[i].sclk_frequency;
- }
- return pi->sys_info.sclk_voltage_mapping_table.entries[pi->sys_info.sclk_voltage_mapping_table.num_max_dpm_entries - 1].sclk_frequency;
+}
+static void sumo_patch_thermal_state(struct radeon_device *rdev,
struct sumo_ps *ps,
struct sumo_ps *current_ps)
+{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- u32 sclk_in_sr = pi->sys_info.min_sclk; /* ??? */
- u32 current_vddc;
- u32 current_sclk;
- u32 current_index = 0;
- if (current_ps) {
current_vddc = current_ps->levels[current_index].vddc_index;
current_sclk = current_ps->levels[current_index].sclk;
- } else {
current_vddc = pi->boot_pl.vddc_index;
current_sclk = pi->boot_pl.sclk;
- }
- ps->levels[0].vddc_index = current_vddc;
- if (ps->levels[0].sclk > current_sclk)
ps->levels[0].sclk = current_sclk;
- ps->levels[0].ss_divider_index =
sumo_get_sleep_divider_id_from_clock(rdev, ps->levels[0].sclk, sclk_in_sr);
- ps->levels[0].ds_divider_index =
sumo_get_sleep_divider_id_from_clock(rdev, ps->levels[0].sclk, SUMO_MINIMUM_ENGINE_CLOCK);
- if (ps->levels[0].ds_divider_index > ps->levels[0].ss_divider_index + 1)
ps->levels[0].ds_divider_index = ps->levels[0].ss_divider_index + 1;
- if (ps->levels[0].ss_divider_index == ps->levels[0].ds_divider_index) {
if (ps->levels[0].ss_divider_index > 1)
ps->levels[0].ss_divider_index = ps->levels[0].ss_divider_index - 1;
- }
- if (ps->levels[0].ss_divider_index == 0)
ps->levels[0].ds_divider_index = 0;
- if (ps->levels[0].ds_divider_index == 0)
ps->levels[0].ss_divider_index = 0;
+}
+static void sumo_apply_state_adjust_rules(struct radeon_device *rdev) +{
- struct radeon_ps *rps = rdev->pm.dpm.requested_ps;
- struct sumo_ps *ps = sumo_get_ps(rps);
- struct sumo_ps *current_ps = sumo_get_ps(rdev->pm.dpm.current_ps);
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- u32 min_voltage = 0; /* ??? */
- u32 min_sclk = pi->sys_info.min_sclk; /* XXX check against disp reqs */
- u32 sclk_in_sr = pi->sys_info.min_sclk; /* ??? */
- u32 i;
- if (rps->class & ATOM_PPLIB_CLASSIFICATION_THERMAL)
return sumo_patch_thermal_state(rdev, ps, current_ps);
- if (pi->enable_boost) {
if (rps->class & ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE)
ps->flags |= SUMO_POWERSTATE_FLAGS_BOOST_STATE;
- }
- if ((rps->class & ATOM_PPLIB_CLASSIFICATION_UI_BATTERY) ||
(rps->class & ATOM_PPLIB_CLASSIFICATION_SDSTATE) ||
(rps->class & ATOM_PPLIB_CLASSIFICATION_HDSTATE))
ps->flags |= SUMO_POWERSTATE_FLAGS_FORCE_NBPS1_STATE;
- for (i = 0; i < ps->num_levels; i++) {
if (ps->levels[i].vddc_index < min_voltage)
ps->levels[i].vddc_index = min_voltage;
if (ps->levels[i].sclk < min_sclk)
ps->levels[i].sclk =
sumo_get_valid_engine_clock(rdev, min_sclk);
ps->levels[i].ss_divider_index =
sumo_get_sleep_divider_id_from_clock(rdev, ps->levels[i].sclk, sclk_in_sr);
ps->levels[i].ds_divider_index =
sumo_get_sleep_divider_id_from_clock(rdev, ps->levels[i].sclk, SUMO_MINIMUM_ENGINE_CLOCK);
if (ps->levels[i].ds_divider_index > ps->levels[i].ss_divider_index + 1)
ps->levels[i].ds_divider_index = ps->levels[i].ss_divider_index + 1;
if (ps->levels[i].ss_divider_index == ps->levels[i].ds_divider_index) {
if (ps->levels[i].ss_divider_index > 1)
ps->levels[i].ss_divider_index = ps->levels[i].ss_divider_index - 1;
}
if (ps->levels[i].ss_divider_index == 0)
ps->levels[i].ds_divider_index = 0;
if (ps->levels[i].ds_divider_index == 0)
ps->levels[i].ss_divider_index = 0;
if (ps->flags & SUMO_POWERSTATE_FLAGS_FORCE_NBPS1_STATE)
ps->levels[i].allow_gnb_slow = 1;
else if ((rps->class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE) ||
(rps->class2 & ATOM_PPLIB_CLASSIFICATION2_MVC))
ps->levels[i].allow_gnb_slow = 0;
else if (i == ps->num_levels - 1)
ps->levels[i].allow_gnb_slow = 0;
else
ps->levels[i].allow_gnb_slow = 1;
- }
+}
+static void sumo_cleanup_asic(struct radeon_device *rdev) +{
- sumo_take_smu_control(rdev, false);
+}
+static int sumo_set_thermal_temperature_range(struct radeon_device *rdev,
int min_temp, int max_temp)
+{
- int low_temp = 0 * 1000;
- int high_temp = 255 * 1000;
- if (low_temp < min_temp)
low_temp = min_temp;
- if (high_temp > max_temp)
high_temp = max_temp;
- if (high_temp < low_temp) {
DRM_ERROR("invalid thermal range: %d - %d\n", low_temp, high_temp);
return -EINVAL;
- }
- WREG32_P(CG_THERMAL_INT, DIG_THERM_INTH(49 + (high_temp / 1000)), ~DIG_THERM_INTH_MASK);
- WREG32_P(CG_THERMAL_INT, DIG_THERM_INTL(49 + (low_temp / 1000)), ~DIG_THERM_INTL_MASK);
- rdev->pm.dpm.thermal.min_temp = low_temp;
- rdev->pm.dpm.thermal.max_temp = high_temp;
- return 0;
+}
+int sumo_dpm_enable(struct radeon_device *rdev) +{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- if (sumo_dpm_enabled(rdev))
return -EINVAL;
- sumo_enable_clock_power_gating(rdev);
- sumo_program_bootup_state(rdev);
- sumo_init_bsp(rdev);
- sumo_reset_am(rdev);
- sumo_program_tp(rdev);
- sumo_program_bootup_at(rdev);
- sumo_start_am(rdev);
- if (pi->enable_auto_thermal_throttling) {
sumo_program_ttp(rdev);
sumo_program_ttt(rdev);
- }
- sumo_program_dc_hto(rdev);
- sumo_program_power_level_enter_state(rdev);
- sumo_enable_voltage_scaling(rdev, true);
- sumo_program_sstp(rdev);
- sumo_program_vc(rdev);
- sumo_override_cnb_thermal_events(rdev);
- sumo_start_dpm(rdev);
- sumo_wait_for_level_0(rdev);
- if (pi->enable_sclk_ds)
sumo_enable_sclk_ds(rdev, true);
- if (pi->enable_boost)
sumo_enable_boost_timer(rdev);
- if (rdev->irq.installed &&
r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) {
sumo_set_thermal_temperature_range(rdev, R600_TEMP_RANGE_MIN, R600_TEMP_RANGE_MAX);
rdev->irq.dpm_thermal = true;
radeon_irq_set(rdev);
- }
- return 0;
+}
+void sumo_dpm_disable(struct radeon_device *rdev) +{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- if (!sumo_dpm_enabled(rdev))
return;
- sumo_disable_clock_power_gating(rdev);
- if (pi->enable_sclk_ds)
sumo_enable_sclk_ds(rdev, false);
- sumo_clear_vc(rdev);
- sumo_wait_for_level_0(rdev);
- sumo_stop_dpm(rdev);
- sumo_enable_voltage_scaling(rdev, false);
- if (rdev->irq.installed &&
r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) {
rdev->irq.dpm_thermal = false;
radeon_irq_set(rdev);
- }
+}
+int sumo_dpm_set_power_state(struct radeon_device *rdev) +{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- if (pi->enable_dynamic_patch_ps)
sumo_apply_state_adjust_rules(rdev);
- sumo_update_current_power_levels(rdev);
- if (pi->enable_boost) {
sumo_enable_boost(rdev, false);
sumo_patch_boost_state(rdev);
- }
- if (pi->enable_dpm) {
sumo_pre_notify_alt_vddnb_change(rdev);
sumo_enable_power_level_0(rdev);
sumo_set_forced_level_0(rdev);
sumo_set_forced_mode_enabled(rdev);
sumo_wait_for_level_0(rdev);
sumo_program_power_levels_0_to_n(rdev);
sumo_program_wl(rdev);
sumo_program_bsp(rdev);
sumo_program_at(rdev);
sumo_force_nbp_state(rdev);
sumo_set_forced_mode_disabled(rdev);
sumo_set_forced_mode_enabled(rdev);
sumo_set_forced_mode_disabled(rdev);
sumo_post_notify_alt_vddnb_change(rdev);
- }
- if (pi->enable_boost)
sumo_enable_boost(rdev, true);
- return 0;
+}
+void sumo_dpm_reset_asic(struct radeon_device *rdev) +{
- sumo_program_bootup_state(rdev);
- sumo_enable_power_level_0(rdev);
- sumo_set_forced_level_0(rdev);
- sumo_set_forced_mode_enabled(rdev);
- sumo_wait_for_level_0(rdev);
- sumo_set_forced_mode_disabled(rdev);
- sumo_set_forced_mode_enabled(rdev);
- sumo_set_forced_mode_disabled(rdev);
+}
+void sumo_dpm_setup_asic(struct radeon_device *rdev) +{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- sumo_initialize_m3_arb(rdev);
- pi->fw_version = sumo_get_running_fw_version(rdev);
- DRM_INFO("Found smc ucode version: 0x%08x\n", pi->fw_version);
- sumo_program_acpi_power_level(rdev);
- sumo_enable_acpi_pm(rdev);
- sumo_take_smu_control(rdev, true);
+}
+void sumo_dpm_display_configuration_changed(struct radeon_device *rdev) +{
+}
+union power_info {
- struct _ATOM_POWERPLAY_INFO info;
- struct _ATOM_POWERPLAY_INFO_V2 info_2;
- struct _ATOM_POWERPLAY_INFO_V3 info_3;
- struct _ATOM_PPLIB_POWERPLAYTABLE pplib;
- struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2;
- struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3;
+};
+union pplib_clock_info {
- struct _ATOM_PPLIB_R600_CLOCK_INFO r600;
- struct _ATOM_PPLIB_RS780_CLOCK_INFO rs780;
- struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO evergreen;
- struct _ATOM_PPLIB_SUMO_CLOCK_INFO sumo;
+};
+union pplib_power_state {
- struct _ATOM_PPLIB_STATE v1;
- struct _ATOM_PPLIB_STATE_V2 v2;
+};
+static void sumo_patch_boot_state(struct radeon_device *rdev,
struct sumo_ps *ps)
+{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- ps->num_levels = 1;
- ps->flags = 0;
- ps->levels[0] = pi->boot_pl;
+}
+static void sumo_parse_pplib_non_clock_info(struct radeon_device *rdev,
struct radeon_ps *rps,
struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info,
u8 table_rev)
+{
- struct sumo_ps *ps = sumo_get_ps(rps);
- rps->caps = le32_to_cpu(non_clock_info->ulCapsAndSettings);
- rps->class = le16_to_cpu(non_clock_info->usClassification);
- rps->class2 = le16_to_cpu(non_clock_info->usClassification2);
- if (ATOM_PPLIB_NONCLOCKINFO_VER1 < table_rev) {
rps->vclk = le32_to_cpu(non_clock_info->ulVCLK);
rps->dclk = le32_to_cpu(non_clock_info->ulDCLK);
- } else {
rps->vclk = 0;
rps->dclk = 0;
- }
- if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT) {
rdev->pm.dpm.boot_ps = rps;
sumo_patch_boot_state(rdev, ps);
- }
- if (rps->class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE)
rdev->pm.dpm.uvd_ps = rps;
+}
+static void sumo_parse_pplib_clock_info(struct radeon_device *rdev,
struct radeon_ps *rps, int index,
union pplib_clock_info *clock_info)
+{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- struct sumo_ps *ps = sumo_get_ps(rps);
- struct sumo_pl *pl = &ps->levels[index];
- u32 sclk;
- sclk = le16_to_cpu(clock_info->sumo.usEngineClockLow);
- sclk |= clock_info->sumo.ucEngineClockHigh << 16;
- pl->sclk = sclk;
- pl->vddc_index = clock_info->sumo.vddcIndex;
- pl->sclk_dpm_tdp_limit = clock_info->sumo.tdpLimit;
- ps->num_levels = index + 1;
- if (pi->enable_sclk_ds) {
pl->ds_divider_index = 5;
pl->ss_divider_index = 4;
- }
+}
+static int sumo_parse_power_table(struct radeon_device *rdev) +{
- struct radeon_mode_info *mode_info = &rdev->mode_info;
- struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info;
- union pplib_power_state *power_state;
- int i, j, k, non_clock_array_index, clock_array_index;
- union pplib_clock_info *clock_info;
- struct _StateArray *state_array;
- struct _ClockInfoArray *clock_info_array;
- struct _NonClockInfoArray *non_clock_info_array;
- union power_info *power_info;
- int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo);
u16 data_offset;
- u8 frev, crev;
- u8 *power_state_offset;
- struct sumo_ps *ps;
- if (!atom_parse_data_header(mode_info->atom_context, index, NULL,
&frev, &crev, &data_offset))
return -EINVAL;
- power_info = (union power_info *)(mode_info->atom_context->bios + data_offset);
- state_array = (struct _StateArray *)
(mode_info->atom_context->bios + data_offset +
le16_to_cpu(power_info->pplib.usStateArrayOffset));
- clock_info_array = (struct _ClockInfoArray *)
(mode_info->atom_context->bios + data_offset +
le16_to_cpu(power_info->pplib.usClockInfoArrayOffset));
- non_clock_info_array = (struct _NonClockInfoArray *)
(mode_info->atom_context->bios + data_offset +
le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset));
- rdev->pm.dpm.ps = kzalloc(sizeof(struct radeon_ps) *
state_array->ucNumEntries, GFP_KERNEL);
- if (!rdev->pm.dpm.ps)
return -ENOMEM;
- power_state_offset = (u8 *)state_array->states;
- rdev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps);
- rdev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime);
- rdev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime);
- for (i = 0; i < state_array->ucNumEntries; i++) {
power_state = (union pplib_power_state *)power_state_offset;
non_clock_array_index = power_state->v2.nonClockInfoIndex;
non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *)
&non_clock_info_array->nonClockInfo[non_clock_array_index];
if (!rdev->pm.power_state[i].clock_info)
return -EINVAL;
ps = kzalloc(sizeof(struct sumo_ps), GFP_KERNEL);
if (ps == NULL) {
kfree(rdev->pm.dpm.ps);
return -ENOMEM;
}
rdev->pm.dpm.ps[i].ps_priv = ps;
k = 0;
for (j = 0; j < power_state->v2.ucNumDPMLevels; j++) {
clock_array_index = power_state->v2.clockInfoIndex[j];
if (k >= SUMO_MAX_HARDWARE_POWERLEVELS)
break;
clock_info = (union pplib_clock_info *)
&clock_info_array->clockInfo[clock_array_index * clock_info_array->ucEntrySize];
sumo_parse_pplib_clock_info(rdev,
&rdev->pm.dpm.ps[i], k,
clock_info);
k++;
}
sumo_parse_pplib_non_clock_info(rdev, &rdev->pm.dpm.ps[i],
non_clock_info,
non_clock_info_array->ucEntrySize);
power_state_offset += 2 + power_state->v2.ucNumDPMLevels;
- }
- rdev->pm.dpm.num_ps = state_array->ucNumEntries;
- return 0;
+}
+static u32 sumo_convert_vid2_to_vid7(struct radeon_device *rdev, u32 vid_2bit) +{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- u32 i;
- for (i = 0; i < pi->sys_info.vid_mapping_table.num_entries; i++) {
if (pi->sys_info.vid_mapping_table.entries[i].vid_2bit == vid_2bit)
return pi->sys_info.vid_mapping_table.entries[i].vid_7bit;
- }
- return pi->sys_info.vid_mapping_table.entries[pi->sys_info.vid_mapping_table.num_entries - 1].vid_7bit;
+}
+static u16 sumo_convert_voltage_index_to_value(struct radeon_device *rdev,
u32 vid_2bit)
+{
- u32 vid_7bit = sumo_convert_vid2_to_vid7(rdev, vid_2bit);
- if (vid_7bit > 0x7C)
return 0;
- return (15500 - vid_7bit * 125 + 5) / 10;
+}
+static void sumo_construct_display_voltage_mapping_table(struct radeon_device *rdev,
ATOM_CLK_VOLT_CAPABILITY *table)
+{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- u32 i;
- for (i = 0; i < SUMO_MAX_NUMBER_VOLTAGES; i++) {
if (table[i].ulMaximumSupportedCLK == 0)
break;
pi->sys_info.disp_clk_voltage_mapping_table.display_clock_frequency[i] =
table[i].ulMaximumSupportedCLK;
- }
- pi->sys_info.disp_clk_voltage_mapping_table.num_max_voltage_levels = i;
- if (pi->sys_info.disp_clk_voltage_mapping_table.num_max_voltage_levels == 0) {
pi->sys_info.disp_clk_voltage_mapping_table.display_clock_frequency[0] = 80000;
pi->sys_info.disp_clk_voltage_mapping_table.num_max_voltage_levels = 1;
- }
+}
+static void sumo_construct_sclk_voltage_mapping_table(struct radeon_device *rdev,
ATOM_AVAILABLE_SCLK_LIST *table)
+{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- u32 i;
- u32 n = 0;
- u32 prev_sclk = 0;
- for (i = 0; i < SUMO_MAX_HARDWARE_POWERLEVELS; i++) {
if (table[i].ulSupportedSCLK > prev_sclk) {
pi->sys_info.sclk_voltage_mapping_table.entries[n].sclk_frequency =
table[i].ulSupportedSCLK;
pi->sys_info.sclk_voltage_mapping_table.entries[n].vid_2bit =
table[i].usVoltageIndex;
prev_sclk = table[i].ulSupportedSCLK;
n++;
}
- }
- pi->sys_info.sclk_voltage_mapping_table.num_max_dpm_entries = n;
+}
+static void sumo_construct_vid_mapping_table(struct radeon_device *rdev,
ATOM_AVAILABLE_SCLK_LIST *table)
+{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- u32 i, j;
- for (i = 0; i < SUMO_MAX_HARDWARE_POWERLEVELS; i++) {
if (table[i].ulSupportedSCLK != 0) {
pi->sys_info.vid_mapping_table.entries[table[i].usVoltageIndex].vid_7bit =
table[i].usVoltageID;
pi->sys_info.vid_mapping_table.entries[table[i].usVoltageIndex].vid_2bit =
table[i].usVoltageIndex;
}
- }
- for (i = 0; i < SUMO_MAX_NUMBER_VOLTAGES; i++) {
if (pi->sys_info.vid_mapping_table.entries[i].vid_7bit == 0) {
for (j = i + 1; j < SUMO_MAX_NUMBER_VOLTAGES; j++) {
if (pi->sys_info.vid_mapping_table.entries[j].vid_7bit != 0) {
pi->sys_info.vid_mapping_table.entries[i] =
pi->sys_info.vid_mapping_table.entries[j];
pi->sys_info.vid_mapping_table.entries[j].vid_7bit = 0;
break;
}
}
if (j == SUMO_MAX_NUMBER_VOLTAGES)
break;
}
- }
- pi->sys_info.vid_mapping_table.num_entries = i;
+}
+union igp_info {
- struct _ATOM_INTEGRATED_SYSTEM_INFO info;
- struct _ATOM_INTEGRATED_SYSTEM_INFO_V2 info_2;
- struct _ATOM_INTEGRATED_SYSTEM_INFO_V5 info_5;
- struct _ATOM_INTEGRATED_SYSTEM_INFO_V6 info_6;
+};
+static int sumo_parse_sys_info_table(struct radeon_device *rdev) +{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- struct radeon_mode_info *mode_info = &rdev->mode_info;
- int index = GetIndexIntoMasterTable(DATA, IntegratedSystemInfo);
- union igp_info *igp_info;
- u8 frev, crev;
- u16 data_offset;
- int i;
- if (atom_parse_data_header(mode_info->atom_context, index, NULL,
&frev, &crev, &data_offset)) {
igp_info = (union igp_info *)(mode_info->atom_context->bios +
data_offset);
if (crev != 6) {
DRM_ERROR("Unsupported IGP table: %d %d\n", frev, crev);
return -EINVAL;
}
pi->sys_info.bootup_sclk = le32_to_cpu(igp_info->info_6.ulBootUpEngineClock);
pi->sys_info.min_sclk = le32_to_cpu(igp_info->info_6.ulMinEngineClock);
pi->sys_info.bootup_uma_clk = le32_to_cpu(igp_info->info_6.ulBootUpUMAClock);
pi->sys_info.bootup_nb_voltage_index =
le16_to_cpu(igp_info->info_6.usBootUpNBVoltage);
if (igp_info->info_6.ucHtcTmpLmt == 0)
pi->sys_info.htc_tmp_lmt = 203;
else
pi->sys_info.htc_tmp_lmt = igp_info->info_6.ucHtcTmpLmt;
if (igp_info->info_6.ucHtcHystLmt == 0)
pi->sys_info.htc_hyst_lmt = 5;
else
pi->sys_info.htc_hyst_lmt = igp_info->info_6.ucHtcHystLmt;
if (pi->sys_info.htc_tmp_lmt <= pi->sys_info.htc_hyst_lmt) {
DRM_ERROR("The htcTmpLmt should be larger than htcHystLmt.\n");
}
for (i = 0; i < NUMBER_OF_M3ARB_PARAM_SETS; i++) {
pi->sys_info.csr_m3_arb_cntl_default[i] =
le32_to_cpu(igp_info->info_6.ulCSR_M3_ARB_CNTL_DEFAULT[i]);
pi->sys_info.csr_m3_arb_cntl_uvd[i] =
le32_to_cpu(igp_info->info_6.ulCSR_M3_ARB_CNTL_UVD[i]);
pi->sys_info.csr_m3_arb_cntl_fs3d[i] =
le32_to_cpu(igp_info->info_6.ulCSR_M3_ARB_CNTL_FS3D[i]);
}
pi->sys_info.sclk_dpm_boost_margin =
le32_to_cpu(igp_info->info_6.SclkDpmBoostMargin);
pi->sys_info.sclk_dpm_throttle_margin =
le32_to_cpu(igp_info->info_6.SclkDpmThrottleMargin);
pi->sys_info.sclk_dpm_tdp_limit_pg =
le16_to_cpu(igp_info->info_6.SclkDpmTdpLimitPG);
pi->sys_info.gnb_tdp_limit = le16_to_cpu(igp_info->info_6.GnbTdpLimit);
pi->sys_info.sclk_dpm_tdp_limit_boost =
le16_to_cpu(igp_info->info_6.SclkDpmTdpLimitBoost);
pi->sys_info.boost_sclk = le32_to_cpu(igp_info->info_6.ulBoostEngineCLock);
pi->sys_info.boost_vid_2bit = igp_info->info_6.ulBoostVid_2bit;
if (igp_info->info_6.EnableBoost)
pi->sys_info.enable_boost = true;
else
pi->sys_info.enable_boost = false;
sumo_construct_display_voltage_mapping_table(rdev,
igp_info->info_6.sDISPCLK_Voltage);
sumo_construct_sclk_voltage_mapping_table(rdev,
igp_info->info_6.sAvail_SCLK);
sumo_construct_vid_mapping_table(rdev, igp_info->info_6.sAvail_SCLK);
- }
- return 0;
+}
+static void sumo_construct_boot_and_acpi_state(struct radeon_device *rdev) +{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- pi->boot_pl.sclk = pi->sys_info.bootup_sclk;
- pi->boot_pl.vddc_index = pi->sys_info.bootup_nb_voltage_index;
- pi->boot_pl.ds_divider_index = 0;
- pi->boot_pl.ss_divider_index = 0;
- pi->boot_pl.allow_gnb_slow = 1;
- pi->acpi_pl = pi->boot_pl;
- pi->current_ps.num_levels = 1;
- pi->current_ps.levels[0] = pi->boot_pl;
+}
+int sumo_dpm_init(struct radeon_device *rdev) +{
- struct sumo_power_info *pi;
- u32 hw_rev = (RREG32(HW_REV) & ATI_REV_ID_MASK) >> ATI_REV_ID_SHIFT;
- int ret;
- pi = kzalloc(sizeof(struct sumo_power_info), GFP_KERNEL);
- if (pi == NULL)
return -ENOMEM;
- rdev->pm.dpm.priv = pi;
- pi->driver_nbps_policy_disable = false;
- if ((rdev->family == CHIP_PALM) && (hw_rev < 3))
pi->disable_gfx_power_gating_in_uvd = true;
- else
pi->disable_gfx_power_gating_in_uvd = false;
- pi->enable_alt_vddnb = true;
- pi->enable_sclk_ds = true;
- pi->enable_dynamic_m3_arbiter = false;
- pi->enable_dynamic_patch_ps = true;
- pi->enable_gfx_power_gating = true;
- pi->enable_gfx_clock_gating = true;
- pi->enable_mg_clock_gating = true;
- pi->enable_auto_thermal_throttling = true;
- ret = sumo_parse_sys_info_table(rdev);
- if (ret)
return ret;
- sumo_construct_boot_and_acpi_state(rdev);
- ret = sumo_parse_power_table(rdev);
- if (ret)
return ret;
- pi->pasi = CYPRESS_HASI_DFLT;
- pi->asi = RV770_ASI_DFLT;
- pi->thermal_auto_throttling = pi->sys_info.htc_tmp_lmt;
- pi->enable_boost = pi->sys_info.enable_boost;
- pi->enable_dpm = true;
- return 0;
+}
+void sumo_dpm_print_power_state(struct radeon_device *rdev,
struct radeon_ps *rps)
+{
- int i;
- struct sumo_ps *ps = sumo_get_ps(rps);
- r600_dpm_print_class_info(rps->class, rps->class2);
- r600_dpm_print_cap_info(rps->caps);
- printk("\tuvd vclk: %d dclk: %d\n", rps->vclk, rps->dclk);
- for (i = 0; i < ps->num_levels; i++) {
struct sumo_pl *pl = &ps->levels[i];
printk("\t\tpower level %d sclk: %u vddc: %u\n",
i, pl->sclk,
sumo_convert_voltage_index_to_value(rdev, pl->vddc_index));
- }
- r600_dpm_print_ps_status(rdev, rps);
+}
+void sumo_dpm_fini(struct radeon_device *rdev) +{
- int i;
- sumo_cleanup_asic(rdev); /* ??? */
- for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
kfree(rdev->pm.dpm.ps[i].ps_priv);
- }
- kfree(rdev->pm.dpm.ps);
- kfree(rdev->pm.dpm.priv);
+}
+u32 sumo_dpm_get_sclk(struct radeon_device *rdev, bool low) +{
- struct sumo_ps *requested_state = sumo_get_ps(rdev->pm.dpm.requested_ps);
- if (low)
return requested_state->levels[0].sclk;
- else
return requested_state->levels[requested_state->num_levels - 1].sclk;
+}
+u32 sumo_dpm_get_mclk(struct radeon_device *rdev, bool low) +{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- return pi->sys_info.bootup_uma_clk;
+} diff --git a/drivers/gpu/drm/radeon/sumo_dpm.h b/drivers/gpu/drm/radeon/sumo_dpm.h new file mode 100644 index 0000000..561bee1 --- /dev/null +++ b/drivers/gpu/drm/radeon/sumo_dpm.h @@ -0,0 +1,199 @@ +/*
- Copyright 2012 Advanced Micro Devices, Inc.
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- OTHER DEALINGS IN THE SOFTWARE.
- */
+#ifndef __SUMO_DPM_H__ +#define __SUMO_DPM_H__
+#define SUMO_MAX_HARDWARE_POWERLEVELS 5 +#define SUMO_PM_NUMBER_OF_TC 15
+struct sumo_pl {
- u32 sclk;
- u32 vddc_index;
- u32 ds_divider_index;
- u32 ss_divider_index;
- u32 allow_gnb_slow;
- u32 sclk_dpm_tdp_limit;
+};
+/* used for the flags field */ +#define SUMO_POWERSTATE_FLAGS_FORCE_NBPS1_STATE (1 << 0) +#define SUMO_POWERSTATE_FLAGS_BOOST_STATE (1 << 1)
+struct sumo_ps {
- struct sumo_pl levels[SUMO_MAX_HARDWARE_POWERLEVELS];
- u32 num_levels;
- /* flags */
- u32 flags;
+};
+#define NUMBER_OF_M3ARB_PARAM_SETS 10 +#define SUMO_MAX_NUMBER_VOLTAGES 4
+struct sumo_disp_clock_voltage_mapping_table {
- u32 num_max_voltage_levels;
- u32 display_clock_frequency[SUMO_MAX_NUMBER_VOLTAGES];
+};
+struct sumo_vid_mapping_entry {
- u16 vid_2bit;
- u16 vid_7bit;
+};
+struct sumo_vid_mapping_table {
- u32 num_entries;
- struct sumo_vid_mapping_entry entries[SUMO_MAX_NUMBER_VOLTAGES];
+};
+struct sumo_sclk_voltage_mapping_entry {
- u32 sclk_frequency;
- u16 vid_2bit;
- u16 rsv;
+};
+struct sumo_sclk_voltage_mapping_table {
- u32 num_max_dpm_entries;
- struct sumo_sclk_voltage_mapping_entry entries[SUMO_MAX_HARDWARE_POWERLEVELS];
+};
+struct sumo_sys_info {
- u32 bootup_sclk;
- u32 min_sclk;
- u32 bootup_uma_clk;
- u16 bootup_nb_voltage_index;
- u8 htc_tmp_lmt;
- u8 htc_hyst_lmt;
- struct sumo_sclk_voltage_mapping_table sclk_voltage_mapping_table;
- struct sumo_disp_clock_voltage_mapping_table disp_clk_voltage_mapping_table;
- struct sumo_vid_mapping_table vid_mapping_table;
- u32 csr_m3_arb_cntl_default[NUMBER_OF_M3ARB_PARAM_SETS];
- u32 csr_m3_arb_cntl_uvd[NUMBER_OF_M3ARB_PARAM_SETS];
- u32 csr_m3_arb_cntl_fs3d[NUMBER_OF_M3ARB_PARAM_SETS];
- u32 sclk_dpm_boost_margin;
- u32 sclk_dpm_throttle_margin;
- u32 sclk_dpm_tdp_limit_pg;
- u32 gnb_tdp_limit;
- u32 sclk_dpm_tdp_limit_boost;
- u32 boost_sclk;
- u32 boost_vid_2bit;
- bool enable_boost;
+};
+struct sumo_power_info {
- u32 asi;
- u32 pasi;
- u32 bsp;
- u32 bsu;
- u32 pbsp;
- u32 pbsu;
- u32 dsp;
- u32 psp;
- u32 thermal_auto_throttling;
- u32 uvd_m3_arbiter;
- u32 fw_version;
- struct sumo_sys_info sys_info;
- struct sumo_pl acpi_pl;
- struct sumo_pl boot_pl;
- struct sumo_pl boost_pl;
- struct sumo_ps current_ps;
- bool disable_gfx_power_gating_in_uvd;
- bool driver_nbps_policy_disable;
- bool enable_alt_vddnb;
- bool enable_dynamic_m3_arbiter;
- bool enable_gfx_clock_gating;
- bool enable_gfx_power_gating;
- bool enable_mg_clock_gating;
- bool enable_sclk_ds;
- bool enable_auto_thermal_throttling;
- bool enable_dynamic_patch_ps;
- bool enable_dpm;
- bool enable_boost;
+};
+#define SUMO_UTC_DFLT_00 0x48 +#define SUMO_UTC_DFLT_01 0x44 +#define SUMO_UTC_DFLT_02 0x44 +#define SUMO_UTC_DFLT_03 0x44 +#define SUMO_UTC_DFLT_04 0x44 +#define SUMO_UTC_DFLT_05 0x44 +#define SUMO_UTC_DFLT_06 0x44 +#define SUMO_UTC_DFLT_07 0x44 +#define SUMO_UTC_DFLT_08 0x44 +#define SUMO_UTC_DFLT_09 0x44 +#define SUMO_UTC_DFLT_10 0x44 +#define SUMO_UTC_DFLT_11 0x44 +#define SUMO_UTC_DFLT_12 0x44 +#define SUMO_UTC_DFLT_13 0x44 +#define SUMO_UTC_DFLT_14 0x44
+#define SUMO_DTC_DFLT_00 0x48 +#define SUMO_DTC_DFLT_01 0x44 +#define SUMO_DTC_DFLT_02 0x44 +#define SUMO_DTC_DFLT_03 0x44 +#define SUMO_DTC_DFLT_04 0x44 +#define SUMO_DTC_DFLT_05 0x44 +#define SUMO_DTC_DFLT_06 0x44 +#define SUMO_DTC_DFLT_07 0x44 +#define SUMO_DTC_DFLT_08 0x44 +#define SUMO_DTC_DFLT_09 0x44 +#define SUMO_DTC_DFLT_10 0x44 +#define SUMO_DTC_DFLT_11 0x44 +#define SUMO_DTC_DFLT_12 0x44 +#define SUMO_DTC_DFLT_13 0x44 +#define SUMO_DTC_DFLT_14 0x44
+#define SUMO_AH_DFLT 5
+#define SUMO_R_DFLT0 70 +#define SUMO_R_DFLT1 70 +#define SUMO_R_DFLT2 70 +#define SUMO_R_DFLT3 70 +#define SUMO_R_DFLT4 100
+#define SUMO_L_DFLT0 0 +#define SUMO_L_DFLT1 20 +#define SUMO_L_DFLT2 20 +#define SUMO_L_DFLT3 20 +#define SUMO_L_DFLT4 20 +#define SUMO_VRC_DFLT 0x30033 +#define SUMO_MGCGTTLOCAL0_DFLT 0 +#define SUMO_MGCGTTLOCAL1_DFLT 0 +#define SUMO_GICST_DFLT 19 +#define SUMO_SST_DFLT 8 +#define SUMO_VOLTAGEDROPT_DFLT 1 +#define SUMO_GFXPOWERGATINGT_DFLT 100
+/* sumo_dpm.c */ +u32 sumo_get_xclk(struct radeon_device *rdev);
+/* sumo_smc.c */ +void sumo_initialize_m3_arb(struct radeon_device *rdev); +void sumo_smu_pg_init(struct radeon_device *rdev); +void sumo_set_tdp_limit(struct radeon_device *rdev, u32 index, u32 tdp_limit); +void sumo_smu_notify_alt_vddnb_change(struct radeon_device *rdev,
bool powersaving, bool force_nbps1);
+void sumo_boost_state_enable(struct radeon_device *rdev, bool enable); +void sumo_enable_boost_timer(struct radeon_device *rdev); +u32 sumo_get_running_fw_version(struct radeon_device *rdev);
+#endif diff --git a/drivers/gpu/drm/radeon/sumo_smc.c b/drivers/gpu/drm/radeon/sumo_smc.c new file mode 100644 index 0000000..9d0ae9b --- /dev/null +++ b/drivers/gpu/drm/radeon/sumo_smc.c @@ -0,0 +1,224 @@ +/*
- Copyright 2012 Advanced Micro Devices, 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.
- */
+#include <linux/firmware.h> +#include "drmP.h" +#include "radeon.h" +#include "sumod.h" +#include "sumo_dpm.h" +#include "ppsmc.h" +#include "radeon_ucode.h"
+#define SUMO_SMU_SERVICE_ROUTINE_PG_INIT 1 +#define SUMO_SMU_SERVICE_ROUTINE_ALTVDDNB_NOTIFY 27 +#define SUMO_SMU_SERVICE_ROUTINE_GFX_SRV_ID_20 20
+struct sumo_ps *sumo_get_ps(struct radeon_ps *rps); +struct sumo_power_info *sumo_get_pi(struct radeon_device *rdev);
+static void sumo_send_msg_to_smu(struct radeon_device *rdev, u32 id) +{
- u32 gfx_int_req;
- int i;
- for (i = 0; i < rdev->usec_timeout; i++) {
if (RREG32(GFX_INT_STATUS) & INT_DONE)
break;
udelay(1);
- }
- gfx_int_req = SERV_INDEX(id) | INT_REQ;
- WREG32(GFX_INT_REQ, gfx_int_req);
- for (i = 0; i < rdev->usec_timeout; i++) {
if (RREG32(GFX_INT_REQ) & INT_REQ)
break;
udelay(1);
- }
- for (i = 0; i < rdev->usec_timeout; i++) {
if (RREG32(GFX_INT_STATUS) & INT_ACK)
break;
udelay(1);
- }
- for (i = 0; i < rdev->usec_timeout; i++) {
if (RREG32(GFX_INT_STATUS) & INT_DONE)
break;
udelay(1);
- }
- gfx_int_req &= ~INT_REQ;
- WREG32(GFX_INT_REQ, gfx_int_req);
+}
+void sumo_initialize_m3_arb(struct radeon_device *rdev) +{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- u32 i;
- if (!pi->enable_dynamic_m3_arbiter)
return;
- for (i = 0; i < NUMBER_OF_M3ARB_PARAM_SETS; i++)
WREG32_RCU(MCU_M3ARB_PARAMS + (i * 4),
pi->sys_info.csr_m3_arb_cntl_default[i]);
- for (; i < NUMBER_OF_M3ARB_PARAM_SETS * 2; i++)
WREG32_RCU(MCU_M3ARB_PARAMS + (i * 4),
pi->sys_info.csr_m3_arb_cntl_uvd[i % NUMBER_OF_M3ARB_PARAM_SETS]);
- for (; i < NUMBER_OF_M3ARB_PARAM_SETS * 3; i++)
WREG32_RCU(MCU_M3ARB_PARAMS + (i * 4),
pi->sys_info.csr_m3_arb_cntl_fs3d[i % NUMBER_OF_M3ARB_PARAM_SETS]);
+}
+static bool sumo_is_alt_vddnb_supported(struct radeon_device *rdev) +{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- bool return_code = false;
- if (!pi->enable_alt_vddnb)
return return_code;
- if ((rdev->family == CHIP_SUMO) || (rdev->family == CHIP_SUMO2)) {
if (pi->fw_version >= 0x00010C00)
return_code = true;
- }
- return return_code;
+}
+void sumo_smu_notify_alt_vddnb_change(struct radeon_device *rdev,
bool powersaving, bool force_nbps1)
+{
- u32 param = 0;
- if (!sumo_is_alt_vddnb_supported(rdev))
return;
- if (powersaving)
param |= 1;
- if (force_nbps1)
param |= 2;
- WREG32_RCU(RCU_ALTVDDNB_NOTIFY, param);
- sumo_send_msg_to_smu(rdev, SUMO_SMU_SERVICE_ROUTINE_ALTVDDNB_NOTIFY);
+}
+void sumo_smu_pg_init(struct radeon_device *rdev) +{
- sumo_send_msg_to_smu(rdev, SUMO_SMU_SERVICE_ROUTINE_PG_INIT);
+}
+static u32 sumo_power_of_4(u32 unit) +{
- u32 ret = 1;
- u32 i;
- for (i = 0; i < unit; i++)
ret *= 4;
- return ret;
+}
+void sumo_enable_boost_timer(struct radeon_device *rdev) +{
- struct sumo_power_info *pi = sumo_get_pi(rdev);
- u32 period, unit, timer_value;
- u32 xclk = sumo_get_xclk(rdev);
- unit = (RREG32_RCU(RCU_LCLK_SCALING_CNTL) & LCLK_SCALING_TIMER_PRESCALER_MASK)
>> LCLK_SCALING_TIMER_PRESCALER_SHIFT;
- period = 100 * (xclk / 100 / sumo_power_of_4(unit));
- timer_value = (period << 16) | (unit << 4);
- WREG32_RCU(RCU_GNB_PWR_REP_TIMER_CNTL, timer_value);
- WREG32_RCU(RCU_BOOST_MARGIN, pi->sys_info.sclk_dpm_boost_margin);
- WREG32_RCU(RCU_THROTTLE_MARGIN, pi->sys_info.sclk_dpm_throttle_margin);
- WREG32_RCU(GNB_TDP_LIMIT, pi->sys_info.gnb_tdp_limit);
- WREG32_RCU(RCU_SclkDpmTdpLimitPG, pi->sys_info.sclk_dpm_tdp_limit_pg);
- sumo_send_msg_to_smu(rdev, SUMO_SMU_SERVICE_ROUTINE_GFX_SRV_ID_20);
+}
+void sumo_set_tdp_limit(struct radeon_device *rdev, u32 index, u32 tdp_limit) +{
- u32 regoffset = 0;
- u32 shift = 0;
- u32 mask = 0xFFF;
- u32 sclk_dpm_tdp_limit;
- switch (index) {
case 0:
Indentation issue (space instead of tab) same issue for all case below.
regoffset = RCU_SclkDpmTdpLimit01;
shift = 16;
break;
case 1:
regoffset = RCU_SclkDpmTdpLimit01;
shift = 0;
break;
case 2:
regoffset = RCU_SclkDpmTdpLimit23;
shift = 16;
break;
case 3:
regoffset = RCU_SclkDpmTdpLimit23;
shift = 0;
break;
case 4:
regoffset = RCU_SclkDpmTdpLimit47;
shift = 16;
break;
case 7:
regoffset = RCU_SclkDpmTdpLimit47;
shift = 0;
break;
default:
break;
- }
- sclk_dpm_tdp_limit = RREG32_RCU(regoffset);
- sclk_dpm_tdp_limit &= ~(mask << shift);
- sclk_dpm_tdp_limit |= (tdp_limit << shift);
- WREG32_RCU(regoffset, sclk_dpm_tdp_limit);
+}
+void sumo_boost_state_enable(struct radeon_device *rdev, bool enable) +{
- u32 boost_disable = RREG32_RCU(RCU_GPU_BOOST_DISABLE);
- boost_disable &= 0xFFFFFFFE;
- boost_disable |= (enable ? 0 : 1);
- WREG32_RCU(RCU_GPU_BOOST_DISABLE, boost_disable);
+}
+u32 sumo_get_running_fw_version(struct radeon_device *rdev) +{
- return RREG32_RCU(RCU_FW_VERSION);
+}
diff --git a/drivers/gpu/drm/radeon/sumod.h b/drivers/gpu/drm/radeon/sumod.h new file mode 100644 index 0000000..a5deba6 --- /dev/null +++ b/drivers/gpu/drm/radeon/sumod.h @@ -0,0 +1,362 @@ +/*
- Copyright 2012 Advanced Micro Devices, 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: Alex Deucher
- */
+#ifndef _SUMOD_H_ +#define _SUMOD_H_
+/* pm registers */
+/* rcu */ +#define RCU_FW_VERSION 0x30c
+#define RCU_PWR_GATING_SEQ0 0x408 +#define RCU_PWR_GATING_SEQ1 0x40c +#define RCU_PWR_GATING_CNTL 0x410 +# define PWR_GATING_EN (1 << 0) +# define RSVD_MASK (0x3 << 1) +# define PCV(x) ((x) << 3) +# define PCV_MASK (0x1f << 3) +# define PCV_SHIFT 3 +# define PCP(x) ((x) << 8) +# define PCP_MASK (0xf << 8) +# define PCP_SHIFT 8 +# define RPW(x) ((x) << 16) +# define RPW_MASK (0xf << 16) +# define RPW_SHIFT 16 +# define ID(x) ((x) << 24) +# define ID_MASK (0xf << 24) +# define ID_SHIFT 24 +# define PGS(x) ((x) << 28) +# define PGS_MASK (0xf << 28) +# define PGS_SHIFT 28
+#define RCU_ALTVDDNB_NOTIFY 0x430 +#define RCU_LCLK_SCALING_CNTL 0x434 +# define LCLK_SCALING_EN (1 << 0) +# define LCLK_SCALING_TYPE (1 << 1) +# define LCLK_SCALING_TIMER_PRESCALER(x) ((x) << 4) +# define LCLK_SCALING_TIMER_PRESCALER_MASK (0xf << 4) +# define LCLK_SCALING_TIMER_PRESCALER_SHIFT 4 +# define LCLK_SCALING_TIMER_PERIOD(x) ((x) << 16) +# define LCLK_SCALING_TIMER_PERIOD_MASK (0xf << 16) +# define LCLK_SCALING_TIMER_PERIOD_SHIFT 16
+#define RCU_PWR_GATING_CNTL_2 0x4a0 +# define MPPU(x) ((x) << 0) +# define MPPU_MASK (0xffff << 0) +# define MPPU_SHIFT 0 +# define MPPD(x) ((x) << 16) +# define MPPD_MASK (0xffff << 16) +# define MPPD_SHIFT 16 +#define RCU_PWR_GATING_CNTL_3 0x4a4 +# define DPPU(x) ((x) << 0) +# define DPPU_MASK (0xffff << 0) +# define DPPU_SHIFT 0 +# define DPPD(x) ((x) << 16) +# define DPPD_MASK (0xffff << 16) +# define DPPD_SHIFT 16 +#define RCU_PWR_GATING_CNTL_4 0x4a8 +# define RT(x) ((x) << 0) +# define RT_MASK (0xffff << 0) +# define RT_SHIFT 0 +# define IT(x) ((x) << 16) +# define IT_MASK (0xffff << 16) +# define IT_SHIFT 16
+/* yes these two have the same address */ +#define RCU_PWR_GATING_CNTL_5 0x504 +#define RCU_GPU_BOOST_DISABLE 0x508
+#define MCU_M3ARB_INDEX 0x504 +#define MCU_M3ARB_PARAMS 0x508
+#define RCU_GNB_PWR_REP_TIMER_CNTL 0x50C
+#define RCU_SclkDpmTdpLimit01 0x514 +#define RCU_SclkDpmTdpLimit23 0x518 +#define RCU_SclkDpmTdpLimit47 0x51C +#define RCU_SclkDpmTdpLimitPG 0x520
+#define GNB_TDP_LIMIT 0x540 +#define RCU_BOOST_MARGIN 0x544 +#define RCU_THROTTLE_MARGIN 0x548
+#define SMU_PCIE_PG_ARGS 0x58C +#define SMU_PCIE_PG_ARGS_2 0x598 +#define SMU_PCIE_PG_ARGS_3 0x59C
+/* mmio */ +#define RCU_STATUS 0x11c +# define GMC_PWR_GATER_BUSY (1 << 8) +# define GFX_PWR_GATER_BUSY (1 << 9) +# define UVD_PWR_GATER_BUSY (1 << 10) +# define PCIE_PWR_GATER_BUSY (1 << 11) +# define GMC_PWR_GATER_STATE (1 << 12) +# define GFX_PWR_GATER_STATE (1 << 13) +# define UVD_PWR_GATER_STATE (1 << 14) +# define PCIE_PWR_GATER_STATE (1 << 15) +# define GFX1_PWR_GATER_BUSY (1 << 16) +# define GFX2_PWR_GATER_BUSY (1 << 17) +# define GFX1_PWR_GATER_STATE (1 << 18) +# define GFX2_PWR_GATER_STATE (1 << 19)
+#define GFX_INT_REQ 0x120 +# define INT_REQ (1 << 0) +# define SERV_INDEX(x) ((x) << 1) +# define SERV_INDEX_MASK (0xff << 1) +# define SERV_INDEX_SHIFT 1 +#define GFX_INT_STATUS 0x124 +# define INT_ACK (1 << 0) +# define INT_DONE (1 << 1)
+#define CG_SCLK_CNTL 0x600 +# define SCLK_DIVIDER(x) ((x) << 0) +# define SCLK_DIVIDER_MASK (0x7f << 0) +# define SCLK_DIVIDER_SHIFT 0 +#define CG_SCLK_STATUS 0x604 +# define SCLK_OVERCLK_DETECT (1 << 2)
+#define GENERAL_PWRMGT 0x63c +# define STATIC_PM_EN (1 << 1)
+#define SCLK_PWRMGT_CNTL 0x644 +# define SCLK_PWRMGT_OFF (1 << 0) +# define SCLK_LOW_D1 (1 << 1) +# define FIR_RESET (1 << 4) +# define FIR_FORCE_TREND_SEL (1 << 5) +# define FIR_TREND_MODE (1 << 6) +# define DYN_GFX_CLK_OFF_EN (1 << 7) +# define GFX_CLK_FORCE_ON (1 << 8) +# define GFX_CLK_REQUEST_OFF (1 << 9) +# define GFX_CLK_FORCE_OFF (1 << 10) +# define GFX_CLK_OFF_ACPI_D1 (1 << 11) +# define GFX_CLK_OFF_ACPI_D2 (1 << 12) +# define GFX_CLK_OFF_ACPI_D3 (1 << 13) +# define GFX_VOLTAGE_CHANGE_EN (1 << 16) +# define GFX_VOLTAGE_CHANGE_MODE (1 << 17)
+#define TARGET_AND_CURRENT_PROFILE_INDEX 0x66c +# define TARG_SCLK_INDEX(x) ((x) << 6) +# define TARG_SCLK_INDEX_MASK (0x7 << 6) +# define TARG_SCLK_INDEX_SHIFT 6 +# define CURR_SCLK_INDEX(x) ((x) << 9) +# define CURR_SCLK_INDEX_MASK (0x7 << 9) +# define CURR_SCLK_INDEX_SHIFT 9 +# define TARG_INDEX(x) ((x) << 12) +# define TARG_INDEX_MASK (0x7 << 12) +# define TARG_INDEX_SHIFT 12 +# define CURR_INDEX(x) ((x) << 15) +# define CURR_INDEX_MASK (0x7 << 15) +# define CURR_INDEX_SHIFT 15
+#define CG_SCLK_DPM_CTRL 0x684 +# define SCLK_FSTATE_0_DIV(x) ((x) << 0) +# define SCLK_FSTATE_0_DIV_MASK (0x7f << 0) +# define SCLK_FSTATE_0_DIV_SHIFT 0 +# define SCLK_FSTATE_0_VLD (1 << 7) +# define SCLK_FSTATE_1_DIV(x) ((x) << 8) +# define SCLK_FSTATE_1_DIV_MASK (0x7f << 8) +# define SCLK_FSTATE_1_DIV_SHIFT 8 +# define SCLK_FSTATE_1_VLD (1 << 15) +# define SCLK_FSTATE_2_DIV(x) ((x) << 16) +# define SCLK_FSTATE_2_DIV_MASK (0x7f << 16) +# define SCLK_FSTATE_2_DIV_SHIFT 16 +# define SCLK_FSTATE_2_VLD (1 << 23) +# define SCLK_FSTATE_3_DIV(x) ((x) << 24) +# define SCLK_FSTATE_3_DIV_MASK (0x7f << 24) +# define SCLK_FSTATE_3_DIV_SHIFT 24 +# define SCLK_FSTATE_3_VLD (1 << 31) +#define CG_SCLK_DPM_CTRL_2 0x688 +#define CG_GCOOR 0x68c +# define PHC(x) ((x) << 0) +# define PHC_MASK (0x1f << 0) +# define PHC_SHIFT 0 +# define SDC(x) ((x) << 9) +# define SDC_MASK (0x3ff << 9) +# define SDC_SHIFT 9 +# define SU(x) ((x) << 23) +# define SU_MASK (0xf << 23) +# define SU_SHIFT 23 +# define DIV_ID(x) ((x) << 28) +# define DIV_ID_MASK (0x7 << 28) +# define DIV_ID_SHIFT 28
+#define CG_FTV 0x690 +#define CG_FFCT_0 0x694 +# define UTC_0(x) ((x) << 0) +# define UTC_0_MASK (0x3ff << 0) +# define UTC_0_SHIFT 0 +# define DTC_0(x) ((x) << 10) +# define DTC_0_MASK (0x3ff << 10) +# define DTC_0_SHIFT 10
+#define CG_GIT 0x6d8 +# define CG_GICST(x) ((x) << 0) +# define CG_GICST_MASK (0xffff << 0) +# define CG_GICST_SHIFT 0 +# define CG_GIPOT(x) ((x) << 16) +# define CG_GIPOT_MASK (0xffff << 16) +# define CG_GIPOT_SHIFT 16
+#define CG_SCLK_DPM_CTRL_3 0x6e0 +# define FORCE_SCLK_STATE(x) ((x) << 0) +# define FORCE_SCLK_STATE_MASK (0x7 << 0) +# define FORCE_SCLK_STATE_SHIFT 0 +# define FORCE_SCLK_STATE_EN (1 << 3) +# define GNB_TT(x) ((x) << 8) +# define GNB_TT_MASK (0xff << 8) +# define GNB_TT_SHIFT 8 +# define GNB_THERMTHRO_MASK (1 << 16) +# define CNB_THERMTHRO_MASK_SCLK (1 << 17) +# define DPM_SCLK_ENABLE (1 << 18) +# define GNB_SLOW_FSTATE_0_MASK (1 << 23) +# define GNB_SLOW_FSTATE_0_SHIFT 23 +# define FORCE_NB_PSTATE_1 (1 << 31)
+#define CG_SSP 0x6e8 +# define SST(x) ((x) << 0) +# define SST_MASK (0xffff << 0) +# define SST_SHIFT 0 +# define SSTU(x) ((x) << 16) +# define SSTU_MASK (0xffff << 16) +# define SSTU_SHIFT 16
+#define CG_ACPI_CNTL 0x70c +# define SCLK_ACPI_DIV(x) ((x) << 0) +# define SCLK_ACPI_DIV_MASK (0x7f << 0) +# define SCLK_ACPI_DIV_SHIFT 0
+#define CG_SCLK_DPM_CTRL_4 0x71c +# define DC_HDC(x) ((x) << 14) +# define DC_HDC_MASK (0x3fff << 14) +# define DC_HDC_SHIFT 14 +# define DC_HU(x) ((x) << 28) +# define DC_HU_MASK (0xf << 28) +# define DC_HU_SHIFT 28 +#define CG_SCLK_DPM_CTRL_5 0x720 +# define SCLK_FSTATE_BOOTUP(x) ((x) << 0) +# define SCLK_FSTATE_BOOTUP_MASK (0x7 << 0) +# define SCLK_FSTATE_BOOTUP_SHIFT 0 +# define TT_TP(x) ((x) << 3) +# define TT_TP_MASK (0xffff << 3) +# define TT_TP_SHIFT 3 +# define TT_TU(x) ((x) << 19) +# define TT_TU_MASK (0xff << 19) +# define TT_TU_SHIFT 19 +#define CG_SCLK_DPM_CTRL_6 0x724 +#define CG_AT_0 0x728 +# define CG_R(x) ((x) << 0) +# define CG_R_MASK (0xffff << 0) +# define CG_R_SHIFT 0 +# define CG_L(x) ((x) << 16) +# define CG_L_MASK (0xffff << 16) +# define CG_L_SHIFT 16 +#define CG_AT_1 0x72c +#define CG_AT_2 0x730 +#define CG_THERMAL_INT 0x734 +#define DIG_THERM_INTH(x) ((x) << 8) +#define DIG_THERM_INTH_MASK 0x0000FF00 +#define DIG_THERM_INTH_SHIFT 8 +#define DIG_THERM_INTL(x) ((x) << 16) +#define DIG_THERM_INTL_MASK 0x00FF0000 +#define DIG_THERM_INTL_SHIFT 16 +#define THERM_INT_MASK_HIGH (1 << 24) +#define THERM_INT_MASK_LOW (1 << 25) +#define CG_AT_3 0x738 +#define CG_AT_4 0x73c +#define CG_AT_5 0x740 +#define CG_AT_6 0x744 +#define CG_AT_7 0x748
+#define CG_BSP_0 0x750 +# define BSP(x) ((x) << 0) +# define BSP_MASK (0xffff << 0) +# define BSP_SHIFT 0 +# define BSU(x) ((x) << 16) +# define BSU_MASK (0xf << 16) +# define BSU_SHIFT 16
+#define CG_CG_VOLTAGE_CNTL 0x770 +# define REQ (1 << 0) +# define LEVEL(x) ((x) << 1) +# define LEVEL_MASK (0x3 << 1) +# define LEVEL_SHIFT 1 +# define CG_VOLTAGE_EN (1 << 3) +# define FORCE (1 << 4) +# define PERIOD(x) ((x) << 8) +# define PERIOD_MASK (0xffff << 8) +# define PERIOD_SHIFT 8 +# define UNIT(x) ((x) << 24) +# define UNIT_MASK (0xf << 24) +# define UNIT_SHIFT 24
+#define CG_ACPI_VOLTAGE_CNTL 0x780 +# define ACPI_VOLTAGE_EN (1 << 8)
+#define CG_DPM_VOLTAGE_CNTL 0x788 +# define DPM_STATE0_LEVEL_MASK (0x3 << 0) +# define DPM_STATE0_LEVEL_SHIFT 0 +# define DPM_VOLTAGE_EN (1 << 16)
+#define CG_PWR_GATING_CNTL 0x7ac +# define DYN_PWR_DOWN_EN (1 << 0) +# define ACPI_PWR_DOWN_EN (1 << 1) +# define GFX_CLK_OFF_PWR_DOWN_EN (1 << 2) +# define IOC_DISGPU_PWR_DOWN_EN (1 << 3) +# define FORCE_POWR_ON (1 << 4) +# define PGP(x) ((x) << 8) +# define PGP_MASK (0xffff << 8) +# define PGP_SHIFT 8 +# define PGU(x) ((x) << 24) +# define PGU_MASK (0xf << 24) +# define PGU_SHIFT 24
+#define CG_CGTT_LOCAL_0 0x7d0 +#define CG_CGTT_LOCAL_1 0x7d4
+#define DEEP_SLEEP_CNTL 0x818 +# define R_DIS (1 << 3) +# define HS(x) ((x) << 4) +# define HS_MASK (0xfff << 4) +# define HS_SHIFT 4 +# define ENABLE_DS (1 << 31) +#define DEEP_SLEEP_CNTL2 0x81c +# define LB_UFP_EN (1 << 0) +# define INOUT_C(x) ((x) << 4) +# define INOUT_C_MASK (0xff << 4) +# define INOUT_C_SHIFT 4
+#define CG_SCRATCH2 0x824
+#define CG_SCLK_DPM_CTRL_11 0x830
+#define HW_REV 0x5564 +# define ATI_REV_ID_MASK (0xf << 28) +# define ATI_REV_ID_SHIFT 28 +/* 0 = A0, 1 = A1, 2 = B0, 3 = C0, etc. */
+#define DOUT_SCRATCH3 0x611c
+#define GB_ADDR_CONFIG 0x98f8
+#endif
1.7.7.5
dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
From: Alex Deucher alexander.deucher@amd.com
This adds dpm support for trinity asics. This includes: - clockgating - powergating - dynamic engine clock scaling - dynamic voltage scaling
set radeon.dpm=1 to enable it.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/Makefile | 3 +- drivers/gpu/drm/radeon/evergreen.c | 13 +- drivers/gpu/drm/radeon/evergreend.h | 10 + drivers/gpu/drm/radeon/ppsmc.h | 10 +- drivers/gpu/drm/radeon/radeon_asic.c | 12 + drivers/gpu/drm/radeon/radeon_asic.h | 12 + drivers/gpu/drm/radeon/radeon_pm.c | 1 + drivers/gpu/drm/radeon/sumo_dpm.c | 90 +- drivers/gpu/drm/radeon/sumo_dpm.h | 21 +- drivers/gpu/drm/radeon/sumo_smc.c | 2 - drivers/gpu/drm/radeon/trinity_dpm.c | 1613 ++++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/trinity_dpm.h | 110 +++ drivers/gpu/drm/radeon/trinity_smc.c | 110 +++ drivers/gpu/drm/radeon/trinityd.h | 223 +++++ 14 files changed, 2179 insertions(+), 51 deletions(-) create mode 100644 drivers/gpu/drm/radeon/trinity_dpm.c create mode 100644 drivers/gpu/drm/radeon/trinity_dpm.h create mode 100644 drivers/gpu/drm/radeon/trinity_smc.c create mode 100644 drivers/gpu/drm/radeon/trinityd.h
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index 7c77e1d..2239ec2 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile @@ -78,7 +78,8 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \ atombios_encoders.o radeon_semaphore.o radeon_sa.o atombios_i2c.o si.o \ si_blit_shaders.o radeon_prime.o radeon_uvd.o cik.o cik_blit_shaders.o \ r600_dpm.o rs780_dpm.o rv6xx_dpm.o rv770_dpm.o rv730_dpm.o rv740_dpm.o \ - rv770_smc.o cypress_dpm.o btc_dpm.o sumo_dpm.o sumo_smc.o + rv770_smc.o cypress_dpm.o btc_dpm.o sumo_dpm.o sumo_smc.o trinity_dpm.o \ + trinity_smc.o
radeon-$(CONFIG_COMPAT) += radeon_ioc32.o radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index c91b1e5..6449953 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -4187,8 +4187,12 @@ int evergreen_irq_set(struct radeon_device *rdev) hpd4 = RREG32(DC_HPD4_INT_CONTROL) & ~DC_HPDx_INT_EN; hpd5 = RREG32(DC_HPD5_INT_CONTROL) & ~DC_HPDx_INT_EN; hpd6 = RREG32(DC_HPD6_INT_CONTROL) & ~DC_HPDx_INT_EN; - thermal_int = RREG32(CG_THERMAL_INT) & - ~(THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW); + if (rdev->family == CHIP_ARUBA) + thermal_int = RREG32(TN_CG_THERMAL_INT_CTRL) & + ~(THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW); + else + thermal_int = RREG32(CG_THERMAL_INT) & + ~(THERM_INT_MASK_HIGH | THERM_INT_MASK_LOW);
afmt1 = RREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET) & ~AFMT_AZ_FORMAT_WTRIG_MASK; afmt2 = RREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET) & ~AFMT_AZ_FORMAT_WTRIG_MASK; @@ -4360,7 +4364,10 @@ int evergreen_irq_set(struct radeon_device *rdev) WREG32(DC_HPD4_INT_CONTROL, hpd4); WREG32(DC_HPD5_INT_CONTROL, hpd5); WREG32(DC_HPD6_INT_CONTROL, hpd6); - WREG32(CG_THERMAL_INT, thermal_int); + if (rdev->family == CHIP_ARUBA) + WREG32(TN_CG_THERMAL_INT_CTRL, thermal_int); + else + WREG32(CG_THERMAL_INT, thermal_int);
WREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, afmt1); WREG32(AFMT_AUDIO_PACKET_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, afmt2); diff --git a/drivers/gpu/drm/radeon/evergreend.h b/drivers/gpu/drm/radeon/evergreend.h index 93b91a3..35e6153 100644 --- a/drivers/gpu/drm/radeon/evergreend.h +++ b/drivers/gpu/drm/radeon/evergreend.h @@ -823,6 +823,16 @@ #define THERM_INT_MASK_HIGH (1 << 24) #define THERM_INT_MASK_LOW (1 << 25)
+#define TN_CG_THERMAL_INT_CTRL 0x738 +#define TN_DIG_THERM_INTH(x) ((x) << 0) +#define TN_DIG_THERM_INTH_MASK 0x000000FF +#define TN_DIG_THERM_INTH_SHIFT 0 +#define TN_DIG_THERM_INTL(x) ((x) << 8) +#define TN_DIG_THERM_INTL_MASK 0x0000FF00 +#define TN_DIG_THERM_INTL_SHIFT 8 +#define TN_THERM_INT_MASK_HIGH (1 << 24) +#define TN_THERM_INT_MASK_LOW (1 << 25) + #define CG_MULT_THERMAL_STATUS 0x740 #define ASIC_T(x) ((x) << 16) #define ASIC_T_MASK 0x07FF0000 diff --git a/drivers/gpu/drm/radeon/ppsmc.h b/drivers/gpu/drm/radeon/ppsmc.h index 638de17..8ef479a 100644 --- a/drivers/gpu/drm/radeon/ppsmc.h +++ b/drivers/gpu/drm/radeon/ppsmc.h @@ -69,7 +69,15 @@ typedef uint8_t PPSMC_Result; #define PPSMC_MSG_ExitULV ((uint8_t)0x65) #define PPSMC_MSG_ResetToDefaults ((uint8_t)0x84)
-typedef uint8_t PPSMC_Msg; +/* TN */ +#define PPSMC_MSG_DPM_Config ((uint32_t) 0x102) +#define PPSMC_MSG_DPM_ForceState ((uint32_t) 0x104) +#define PPSMC_MSG_PG_SIMD_Config ((uint32_t) 0x108) +#define PPSMC_MSG_DCE_RemoveVoltageAdjustment ((uint32_t) 0x11d) +#define PPSMC_MSG_DCE_AllowVoltageAdjustment ((uint32_t) 0x11e) + + +typedef uint16_t PPSMC_Msg;
#pragma pack(pop)
diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index 8b541b4..5dcbb28 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1944,6 +1944,18 @@ static struct radeon_asic trinity_asic = { .set_uvd_clocks = &sumo_set_uvd_clocks, .get_temperature = &tn_get_temp, }, + .dpm = { + .init = &trinity_dpm_init, + .setup_asic = &trinity_dpm_setup_asic, + .enable = &trinity_dpm_enable, + .disable = &trinity_dpm_disable, + .set_power_state = &trinity_dpm_set_power_state, + .display_configuration_changed = &trinity_dpm_display_configuration_changed, + .fini = &trinity_dpm_fini, + .get_sclk = &trinity_dpm_get_sclk, + .get_mclk = &trinity_dpm_get_mclk, + .print_power_state = &trinity_dpm_print_power_state, + }, .pflip = { .pre_page_flip = &evergreen_pre_page_flip, .page_flip = &evergreen_page_flip, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index f6c0984..4d3f93d 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -581,6 +581,18 @@ bool cayman_gfx_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring); bool cayman_dma_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring); void cayman_dma_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm);
+int trinity_dpm_init(struct radeon_device *rdev); +int trinity_dpm_enable(struct radeon_device *rdev); +void trinity_dpm_disable(struct radeon_device *rdev); +int trinity_dpm_set_power_state(struct radeon_device *rdev); +void trinity_dpm_setup_asic(struct radeon_device *rdev); +void trinity_dpm_display_configuration_changed(struct radeon_device *rdev); +void trinity_dpm_fini(struct radeon_device *rdev); +u32 trinity_dpm_get_sclk(struct radeon_device *rdev, bool low); +u32 trinity_dpm_get_mclk(struct radeon_device *rdev, bool low); +void trinity_dpm_print_power_state(struct radeon_device *rdev, + struct radeon_ps *ps); + /* DCE6 - SI */ void dce6_bandwidth_update(struct radeon_device *rdev);
diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index b924bcc1..e6fce0b 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -1055,6 +1055,7 @@ int radeon_pm_init(struct radeon_device *rdev) case CHIP_BARTS: case CHIP_TURKS: case CHIP_CAICOS: + case CHIP_ARUBA: if (radeon_dpm == 1) rdev->pm.pm_method = PM_METHOD_DPM; else diff --git a/drivers/gpu/drm/radeon/sumo_dpm.c b/drivers/gpu/drm/radeon/sumo_dpm.c index 4f49450..67c85c7 100644 --- a/drivers/gpu/drm/radeon/sumo_dpm.c +++ b/drivers/gpu/drm/radeon/sumo_dpm.c @@ -27,7 +27,6 @@ #include "r600_dpm.h" #include "cypress_dpm.h" #include "sumo_dpm.h" -#include "atom.h"
#define SUMO_MAX_DEEPSLEEP_DIVIDER_ID 5 #define SUMO_MINIMUM_ENGINE_CLOCK 800 @@ -144,7 +143,7 @@ static void sumo_program_grsd(struct radeon_device *rdev) WREG32(CG_GCOOR, PHC(grs) | SDC(p) | SU(u)); }
-static void sumo_gfx_clockgating_initialize(struct radeon_device *rdev) +void sumo_gfx_clockgating_initialize(struct radeon_device *rdev) { sumo_program_git(rdev); sumo_program_grsd(rdev); @@ -452,17 +451,17 @@ static void sumo_program_tp(struct radeon_device *rdev) WREG32_P(SCLK_PWRMGT_CNTL, FIR_TREND_MODE, ~FIR_TREND_MODE); }
-static void sumo_program_vc(struct radeon_device *rdev) +void sumo_program_vc(struct radeon_device *rdev, u32 vrc) { - WREG32(CG_FTV, SUMO_VRC_DFLT); + WREG32(CG_FTV, vrc); }
-static void sumo_clear_vc(struct radeon_device *rdev) +void sumo_clear_vc(struct radeon_device *rdev) { WREG32(CG_FTV, 0); }
-static void sumo_program_sstp(struct radeon_device *rdev) +void sumo_program_sstp(struct radeon_device *rdev) { u32 p, u; u32 xclk = sumo_get_xclk(rdev); @@ -812,7 +811,7 @@ static void sumo_program_bootup_state(struct radeon_device *rdev) sumo_power_level_enable(rdev, i, false); }
-static void sumo_take_smu_control(struct radeon_device *rdev, bool enable) +void sumo_take_smu_control(struct radeon_device *rdev, bool enable) { u32 v = RREG32(DOUT_SCRATCH3);
@@ -933,14 +932,14 @@ static void sumo_force_nbp_state(struct radeon_device *rdev) } }
-static u32 sumo_get_sleep_divider_from_id(u32 id) +u32 sumo_get_sleep_divider_from_id(u32 id) { return 1 << id; }
-static u32 sumo_get_sleep_divider_id_from_clock(struct radeon_device *rdev, - u32 sclk, - u32 min_sclk_in_sr) +u32 sumo_get_sleep_divider_id_from_clock(struct radeon_device *rdev, + u32 sclk, + u32 min_sclk_in_sr) { struct sumo_power_info *pi = sumo_get_pi(rdev); u32 i; @@ -1136,7 +1135,7 @@ int sumo_dpm_enable(struct radeon_device *rdev) sumo_program_power_level_enter_state(rdev); sumo_enable_voltage_scaling(rdev, true); sumo_program_sstp(rdev); - sumo_program_vc(rdev); + sumo_program_vc(rdev, SUMO_VRC_DFLT); sumo_override_cnb_thermal_events(rdev); sumo_start_dpm(rdev); sumo_wait_for_level_0(rdev); @@ -1393,23 +1392,25 @@ static int sumo_parse_power_table(struct radeon_device *rdev) return 0; }
-static u32 sumo_convert_vid2_to_vid7(struct radeon_device *rdev, u32 vid_2bit) +u32 sumo_convert_vid2_to_vid7(struct radeon_device *rdev, + struct sumo_vid_mapping_table *vid_mapping_table, + u32 vid_2bit) { - struct sumo_power_info *pi = sumo_get_pi(rdev); u32 i;
- for (i = 0; i < pi->sys_info.vid_mapping_table.num_entries; i++) { - if (pi->sys_info.vid_mapping_table.entries[i].vid_2bit == vid_2bit) - return pi->sys_info.vid_mapping_table.entries[i].vid_7bit; + for (i = 0; i < vid_mapping_table->num_entries; i++) { + if (vid_mapping_table->entries[i].vid_2bit == vid_2bit) + return vid_mapping_table->entries[i].vid_7bit; }
- return pi->sys_info.vid_mapping_table.entries[pi->sys_info.vid_mapping_table.num_entries - 1].vid_7bit; + return vid_mapping_table->entries[vid_mapping_table->num_entries - 1].vid_7bit; }
static u16 sumo_convert_voltage_index_to_value(struct radeon_device *rdev, u32 vid_2bit) { - u32 vid_7bit = sumo_convert_vid2_to_vid7(rdev, vid_2bit); + struct sumo_power_info *pi = sumo_get_pi(rdev); + u32 vid_7bit = sumo_convert_vid2_to_vid7(rdev, &pi->sys_info.vid_mapping_table, vid_2bit);
if (vid_7bit > 0x7C) return 0; @@ -1418,71 +1419,71 @@ static u16 sumo_convert_voltage_index_to_value(struct radeon_device *rdev, }
static void sumo_construct_display_voltage_mapping_table(struct radeon_device *rdev, + struct sumo_disp_clock_voltage_mapping_table *disp_clk_voltage_mapping_table, ATOM_CLK_VOLT_CAPABILITY *table) { - struct sumo_power_info *pi = sumo_get_pi(rdev); u32 i;
for (i = 0; i < SUMO_MAX_NUMBER_VOLTAGES; i++) { if (table[i].ulMaximumSupportedCLK == 0) break;
- pi->sys_info.disp_clk_voltage_mapping_table.display_clock_frequency[i] = + disp_clk_voltage_mapping_table->display_clock_frequency[i] = table[i].ulMaximumSupportedCLK; }
- pi->sys_info.disp_clk_voltage_mapping_table.num_max_voltage_levels = i; + disp_clk_voltage_mapping_table->num_max_voltage_levels = i;
- if (pi->sys_info.disp_clk_voltage_mapping_table.num_max_voltage_levels == 0) { - pi->sys_info.disp_clk_voltage_mapping_table.display_clock_frequency[0] = 80000; - pi->sys_info.disp_clk_voltage_mapping_table.num_max_voltage_levels = 1; + if (disp_clk_voltage_mapping_table->num_max_voltage_levels == 0) { + disp_clk_voltage_mapping_table->display_clock_frequency[0] = 80000; + disp_clk_voltage_mapping_table->num_max_voltage_levels = 1; } }
-static void sumo_construct_sclk_voltage_mapping_table(struct radeon_device *rdev, - ATOM_AVAILABLE_SCLK_LIST *table) +void sumo_construct_sclk_voltage_mapping_table(struct radeon_device *rdev, + struct sumo_sclk_voltage_mapping_table *sclk_voltage_mapping_table, + ATOM_AVAILABLE_SCLK_LIST *table) { - struct sumo_power_info *pi = sumo_get_pi(rdev); u32 i; u32 n = 0; u32 prev_sclk = 0;
for (i = 0; i < SUMO_MAX_HARDWARE_POWERLEVELS; i++) { if (table[i].ulSupportedSCLK > prev_sclk) { - pi->sys_info.sclk_voltage_mapping_table.entries[n].sclk_frequency = + sclk_voltage_mapping_table->entries[n].sclk_frequency = table[i].ulSupportedSCLK; - pi->sys_info.sclk_voltage_mapping_table.entries[n].vid_2bit = + sclk_voltage_mapping_table->entries[n].vid_2bit = table[i].usVoltageIndex; prev_sclk = table[i].ulSupportedSCLK; n++; } }
- pi->sys_info.sclk_voltage_mapping_table.num_max_dpm_entries = n; + sclk_voltage_mapping_table->num_max_dpm_entries = n; }
-static void sumo_construct_vid_mapping_table(struct radeon_device *rdev, - ATOM_AVAILABLE_SCLK_LIST *table) +void sumo_construct_vid_mapping_table(struct radeon_device *rdev, + struct sumo_vid_mapping_table *vid_mapping_table, + ATOM_AVAILABLE_SCLK_LIST *table) { - struct sumo_power_info *pi = sumo_get_pi(rdev); u32 i, j;
for (i = 0; i < SUMO_MAX_HARDWARE_POWERLEVELS; i++) { if (table[i].ulSupportedSCLK != 0) { - pi->sys_info.vid_mapping_table.entries[table[i].usVoltageIndex].vid_7bit = + vid_mapping_table->entries[table[i].usVoltageIndex].vid_7bit = table[i].usVoltageID; - pi->sys_info.vid_mapping_table.entries[table[i].usVoltageIndex].vid_2bit = + vid_mapping_table->entries[table[i].usVoltageIndex].vid_2bit = table[i].usVoltageIndex; } }
for (i = 0; i < SUMO_MAX_NUMBER_VOLTAGES; i++) { - if (pi->sys_info.vid_mapping_table.entries[i].vid_7bit == 0) { + if (vid_mapping_table->entries[i].vid_7bit == 0) { for (j = i + 1; j < SUMO_MAX_NUMBER_VOLTAGES; j++) { - if (pi->sys_info.vid_mapping_table.entries[j].vid_7bit != 0) { - pi->sys_info.vid_mapping_table.entries[i] = - pi->sys_info.vid_mapping_table.entries[j]; - pi->sys_info.vid_mapping_table.entries[j].vid_7bit = 0; + if (vid_mapping_table->entries[j].vid_7bit != 0) { + vid_mapping_table->entries[i] = + vid_mapping_table->entries[j]; + vid_mapping_table->entries[j].vid_7bit = 0; break; } } @@ -1492,7 +1493,7 @@ static void sumo_construct_vid_mapping_table(struct radeon_device *rdev, } }
- pi->sys_info.vid_mapping_table.num_entries = i; + vid_mapping_table->num_entries = i; }
union igp_info { @@ -1561,10 +1562,13 @@ static int sumo_parse_sys_info_table(struct radeon_device *rdev) else pi->sys_info.enable_boost = false; sumo_construct_display_voltage_mapping_table(rdev, + &pi->sys_info.disp_clk_voltage_mapping_table, igp_info->info_6.sDISPCLK_Voltage); sumo_construct_sclk_voltage_mapping_table(rdev, + &pi->sys_info.sclk_voltage_mapping_table, igp_info->info_6.sAvail_SCLK); - sumo_construct_vid_mapping_table(rdev, igp_info->info_6.sAvail_SCLK); + sumo_construct_vid_mapping_table(rdev, &pi->sys_info.vid_mapping_table, + igp_info->info_6.sAvail_SCLK);
} return 0; diff --git a/drivers/gpu/drm/radeon/sumo_dpm.h b/drivers/gpu/drm/radeon/sumo_dpm.h index 561bee1..d041a6c 100644 --- a/drivers/gpu/drm/radeon/sumo_dpm.h +++ b/drivers/gpu/drm/radeon/sumo_dpm.h @@ -23,6 +23,8 @@ #ifndef __SUMO_DPM_H__ #define __SUMO_DPM_H__
+#include "atom.h" + #define SUMO_MAX_HARDWARE_POWERLEVELS 5 #define SUMO_PM_NUMBER_OF_TC 15
@@ -184,7 +186,24 @@ struct sumo_power_info {
/* sumo_dpm.c */ u32 sumo_get_xclk(struct radeon_device *rdev); - +void sumo_gfx_clockgating_initialize(struct radeon_device *rdev); +void sumo_program_vc(struct radeon_device *rdev, u32 vrc); +void sumo_clear_vc(struct radeon_device *rdev); +void sumo_program_sstp(struct radeon_device *rdev); +void sumo_take_smu_control(struct radeon_device *rdev, bool enable); +void sumo_construct_sclk_voltage_mapping_table(struct radeon_device *rdev, + struct sumo_sclk_voltage_mapping_table *sclk_voltage_mapping_table, + ATOM_AVAILABLE_SCLK_LIST *table); +void sumo_construct_vid_mapping_table(struct radeon_device *rdev, + struct sumo_vid_mapping_table *vid_mapping_table, + ATOM_AVAILABLE_SCLK_LIST *table); +u32 sumo_convert_vid2_to_vid7(struct radeon_device *rdev, + struct sumo_vid_mapping_table *vid_mapping_table, + u32 vid_2bit); +u32 sumo_get_sleep_divider_from_id(u32 id); +u32 sumo_get_sleep_divider_id_from_clock(struct radeon_device *rdev, + u32 sclk, + u32 min_sclk_in_sr);
/* sumo_smc.c */ void sumo_initialize_m3_arb(struct radeon_device *rdev); diff --git a/drivers/gpu/drm/radeon/sumo_smc.c b/drivers/gpu/drm/radeon/sumo_smc.c index 9d0ae9b..bc1a510 100644 --- a/drivers/gpu/drm/radeon/sumo_smc.c +++ b/drivers/gpu/drm/radeon/sumo_smc.c @@ -21,13 +21,11 @@ * */
-#include <linux/firmware.h> #include "drmP.h" #include "radeon.h" #include "sumod.h" #include "sumo_dpm.h" #include "ppsmc.h" -#include "radeon_ucode.h"
#define SUMO_SMU_SERVICE_ROUTINE_PG_INIT 1 #define SUMO_SMU_SERVICE_ROUTINE_ALTVDDNB_NOTIFY 27 diff --git a/drivers/gpu/drm/radeon/trinity_dpm.c b/drivers/gpu/drm/radeon/trinity_dpm.c new file mode 100644 index 0000000..c4779a6 --- /dev/null +++ b/drivers/gpu/drm/radeon/trinity_dpm.c @@ -0,0 +1,1613 @@ +/* + * Copyright 2012 Advanced Micro Devices, 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. + * + */ + +#include "drmP.h" +#include "radeon.h" +#include "trinityd.h" +#include "r600_dpm.h" +#include "trinity_dpm.h" + +#define TRINITY_MAX_DEEPSLEEP_DIVIDER_ID 5 +#define TRINITY_MINIMUM_ENGINE_CLOCK 800 +#define SCLK_MIN_DIV_INTV_SHIFT 12 +#define TRINITY_DISPCLK_BYPASS_THRESHOLD 10000 + +#ifndef TRINITY_MGCG_SEQUENCE +#define TRINITY_MGCG_SEQUENCE 100 + +static const u32 trinity_mgcg_shls_default[] = +{ + /* Register, Value, Mask */ + 0x0000802c, 0xc0000000, 0xffffffff, + 0x00003fc4, 0xc0000000, 0xffffffff, + 0x00005448, 0x00000100, 0xffffffff, + 0x000055e4, 0x00000100, 0xffffffff, + 0x0000160c, 0x00000100, 0xffffffff, + 0x00008984, 0x06000100, 0xffffffff, + 0x0000c164, 0x00000100, 0xffffffff, + 0x00008a18, 0x00000100, 0xffffffff, + 0x0000897c, 0x06000100, 0xffffffff, + 0x00008b28, 0x00000100, 0xffffffff, + 0x00009144, 0x00800200, 0xffffffff, + 0x00009a60, 0x00000100, 0xffffffff, + 0x00009868, 0x00000100, 0xffffffff, + 0x00008d58, 0x00000100, 0xffffffff, + 0x00009510, 0x00000100, 0xffffffff, + 0x0000949c, 0x00000100, 0xffffffff, + 0x00009654, 0x00000100, 0xffffffff, + 0x00009030, 0x00000100, 0xffffffff, + 0x00009034, 0x00000100, 0xffffffff, + 0x00009038, 0x00000100, 0xffffffff, + 0x0000903c, 0x00000100, 0xffffffff, + 0x00009040, 0x00000100, 0xffffffff, + 0x0000a200, 0x00000100, 0xffffffff, + 0x0000a204, 0x00000100, 0xffffffff, + 0x0000a208, 0x00000100, 0xffffffff, + 0x0000a20c, 0x00000100, 0xffffffff, + 0x00009744, 0x00000100, 0xffffffff, + 0x00003f80, 0x00000100, 0xffffffff, + 0x0000a210, 0x00000100, 0xffffffff, + 0x0000a214, 0x00000100, 0xffffffff, + 0x000004d8, 0x00000100, 0xffffffff, + 0x00009664, 0x00000100, 0xffffffff, + 0x00009698, 0x00000100, 0xffffffff, + 0x000004d4, 0x00000200, 0xffffffff, + 0x000004d0, 0x00000000, 0xffffffff, + 0x000030cc, 0x00000104, 0xffffffff, + 0x0000d0c0, 0x00000100, 0xffffffff, + 0x0000d8c0, 0x00000100, 0xffffffff, + 0x0000951c, 0x00010000, 0xffffffff, + 0x00009160, 0x00030002, 0xffffffff, + 0x00009164, 0x00050004, 0xffffffff, + 0x00009168, 0x00070006, 0xffffffff, + 0x00009178, 0x00070000, 0xffffffff, + 0x0000917c, 0x00030002, 0xffffffff, + 0x00009180, 0x00050004, 0xffffffff, + 0x0000918c, 0x00010006, 0xffffffff, + 0x00009190, 0x00090008, 0xffffffff, + 0x00009194, 0x00070000, 0xffffffff, + 0x00009198, 0x00030002, 0xffffffff, + 0x0000919c, 0x00050004, 0xffffffff, + 0x000091a8, 0x00010006, 0xffffffff, + 0x000091ac, 0x00090008, 0xffffffff, + 0x000091b0, 0x00070000, 0xffffffff, + 0x000091b4, 0x00030002, 0xffffffff, + 0x000091b8, 0x00050004, 0xffffffff, + 0x000091c4, 0x00010006, 0xffffffff, + 0x000091c8, 0x00090008, 0xffffffff, + 0x000091cc, 0x00070000, 0xffffffff, + 0x000091d0, 0x00030002, 0xffffffff, + 0x000091d4, 0x00050004, 0xffffffff, + 0x000091e0, 0x00010006, 0xffffffff, + 0x000091e4, 0x00090008, 0xffffffff, + 0x000091e8, 0x00000000, 0xffffffff, + 0x000091ec, 0x00070000, 0xffffffff, + 0x000091f0, 0x00030002, 0xffffffff, + 0x000091f4, 0x00050004, 0xffffffff, + 0x00009200, 0x00010006, 0xffffffff, + 0x00009204, 0x00090008, 0xffffffff, + 0x00009208, 0x00070000, 0xffffffff, + 0x0000920c, 0x00030002, 0xffffffff, + 0x00009210, 0x00050004, 0xffffffff, + 0x0000921c, 0x00010006, 0xffffffff, + 0x00009220, 0x00090008, 0xffffffff, + 0x00009294, 0x00000000, 0xffffffff +}; + +static const u32 trinity_mgcg_shls_enable[] = +{ + /* Register, Value, Mask */ + 0x0000802c, 0xc0000000, 0xffffffff, + 0x000008f8, 0x00000000, 0xffffffff, + 0x000008fc, 0x00000000, 0x000133FF, + 0x000008f8, 0x00000001, 0xffffffff, + 0x000008fc, 0x00000000, 0xE00B03FC, + 0x00009150, 0x96944200, 0xffffffff +}; + +static const u32 trinity_mgcg_shls_disable[] = +{ + /* Register, Value, Mask */ + 0x0000802c, 0xc0000000, 0xffffffff, + 0x00009150, 0x00600000, 0xffffffff, + 0x000008f8, 0x00000000, 0xffffffff, + 0x000008fc, 0xffffffff, 0x000133FF, + 0x000008f8, 0x00000001, 0xffffffff, + 0x000008fc, 0xffffffff, 0xE00B03FC +}; +#endif + +#ifndef TRINITY_SYSLS_SEQUENCE +#define TRINITY_SYSLS_SEQUENCE 100 + +static const u32 trinity_sysls_default[] = +{ + /* Register, Value, Mask */ + 0x000055e8, 0x00000000, 0xffffffff, + 0x0000d0bc, 0x00000000, 0xffffffff, + 0x0000d8bc, 0x00000000, 0xffffffff, + 0x000015c0, 0x000c1401, 0xffffffff, + 0x0000264c, 0x000c0400, 0xffffffff, + 0x00002648, 0x000c0400, 0xffffffff, + 0x00002650, 0x000c0400, 0xffffffff, + 0x000020b8, 0x000c0400, 0xffffffff, + 0x000020bc, 0x000c0400, 0xffffffff, + 0x000020c0, 0x000c0c80, 0xffffffff, + 0x0000f4a0, 0x000000c0, 0xffffffff, + 0x0000f4a4, 0x00680fff, 0xffffffff, + 0x00002f50, 0x00000404, 0xffffffff, + 0x000004c8, 0x00000001, 0xffffffff, + 0x0000641c, 0x00000000, 0xffffffff, + 0x00000c7c, 0x00000000, 0xffffffff, + 0x00006dfc, 0x00000000, 0xffffffff +}; + +static const u32 trinity_sysls_disable[] = +{ + /* Register, Value, Mask */ + 0x0000d0c0, 0x00000000, 0xffffffff, + 0x0000d8c0, 0x00000000, 0xffffffff, + 0x000055e8, 0x00000000, 0xffffffff, + 0x0000d0bc, 0x00000000, 0xffffffff, + 0x0000d8bc, 0x00000000, 0xffffffff, + 0x000015c0, 0x00041401, 0xffffffff, + 0x0000264c, 0x00040400, 0xffffffff, + 0x00002648, 0x00040400, 0xffffffff, + 0x00002650, 0x00040400, 0xffffffff, + 0x000020b8, 0x00040400, 0xffffffff, + 0x000020bc, 0x00040400, 0xffffffff, + 0x000020c0, 0x00040c80, 0xffffffff, + 0x0000f4a0, 0x000000c0, 0xffffffff, + 0x0000f4a4, 0x00680000, 0xffffffff, + 0x00002f50, 0x00000404, 0xffffffff, + 0x000004c8, 0x00000001, 0xffffffff, + 0x0000641c, 0x00007ffd, 0xffffffff, + 0x00000c7c, 0x0000ff00, 0xffffffff, + 0x00006dfc, 0x0000007f, 0xffffffff +}; + +static const u32 trinity_sysls_enable[] = +{ + /* Register, Value, Mask */ + 0x000055e8, 0x00000001, 0xffffffff, + 0x0000d0bc, 0x00000100, 0xffffffff, + 0x0000d8bc, 0x00000100, 0xffffffff, + 0x000015c0, 0x000c1401, 0xffffffff, + 0x0000264c, 0x000c0400, 0xffffffff, + 0x00002648, 0x000c0400, 0xffffffff, + 0x00002650, 0x000c0400, 0xffffffff, + 0x000020b8, 0x000c0400, 0xffffffff, + 0x000020bc, 0x000c0400, 0xffffffff, + 0x000020c0, 0x000c0c80, 0xffffffff, + 0x0000f4a0, 0x000000c0, 0xffffffff, + 0x0000f4a4, 0x00680fff, 0xffffffff, + 0x00002f50, 0x00000903, 0xffffffff, + 0x000004c8, 0x00000000, 0xffffffff, + 0x0000641c, 0x00000000, 0xffffffff, + 0x00000c7c, 0x00000000, 0xffffffff, + 0x00006dfc, 0x00000000, 0xffffffff +}; +#endif + +static const u32 trinity_override_mgpg_sequences[] = +{ + /* Register, Value */ + 0x00000200, 0xE030032C, + 0x00000204, 0x00000FFF, + 0x00000200, 0xE0300058, + 0x00000204, 0x00030301, + 0x00000200, 0xE0300054, + 0x00000204, 0x500010FF, + 0x00000200, 0xE0300074, + 0x00000204, 0x00030301, + 0x00000200, 0xE0300070, + 0x00000204, 0x500010FF, + 0x00000200, 0xE0300090, + 0x00000204, 0x00030301, + 0x00000200, 0xE030008C, + 0x00000204, 0x500010FF, + 0x00000200, 0xE03000AC, + 0x00000204, 0x00030301, + 0x00000200, 0xE03000A8, + 0x00000204, 0x500010FF, + 0x00000200, 0xE03000C8, + 0x00000204, 0x00030301, + 0x00000200, 0xE03000C4, + 0x00000204, 0x500010FF, + 0x00000200, 0xE03000E4, + 0x00000204, 0x00030301, + 0x00000200, 0xE03000E0, + 0x00000204, 0x500010FF, + 0x00000200, 0xE0300100, + 0x00000204, 0x00030301, + 0x00000200, 0xE03000FC, + 0x00000204, 0x500010FF, + 0x00000200, 0xE0300058, + 0x00000204, 0x00030303, + 0x00000200, 0xE0300054, + 0x00000204, 0x600010FF, + 0x00000200, 0xE0300074, + 0x00000204, 0x00030303, + 0x00000200, 0xE0300070, + 0x00000204, 0x600010FF, + 0x00000200, 0xE0300090, + 0x00000204, 0x00030303, + 0x00000200, 0xE030008C, + 0x00000204, 0x600010FF, + 0x00000200, 0xE03000AC, + 0x00000204, 0x00030303, + 0x00000200, 0xE03000A8, + 0x00000204, 0x600010FF, + 0x00000200, 0xE03000C8, + 0x00000204, 0x00030303, + 0x00000200, 0xE03000C4, + 0x00000204, 0x600010FF, + 0x00000200, 0xE03000E4, + 0x00000204, 0x00030303, + 0x00000200, 0xE03000E0, + 0x00000204, 0x600010FF, + 0x00000200, 0xE0300100, + 0x00000204, 0x00030303, + 0x00000200, 0xE03000FC, + 0x00000204, 0x600010FF, + 0x00000200, 0xE0300058, + 0x00000204, 0x00030303, + 0x00000200, 0xE0300054, + 0x00000204, 0x700010FF, + 0x00000200, 0xE0300074, + 0x00000204, 0x00030303, + 0x00000200, 0xE0300070, + 0x00000204, 0x700010FF, + 0x00000200, 0xE0300090, + 0x00000204, 0x00030303, + 0x00000200, 0xE030008C, + 0x00000204, 0x700010FF, + 0x00000200, 0xE03000AC, + 0x00000204, 0x00030303, + 0x00000200, 0xE03000A8, + 0x00000204, 0x700010FF, + 0x00000200, 0xE03000C8, + 0x00000204, 0x00030303, + 0x00000200, 0xE03000C4, + 0x00000204, 0x700010FF, + 0x00000200, 0xE03000E4, + 0x00000204, 0x00030303, + 0x00000200, 0xE03000E0, + 0x00000204, 0x700010FF, + 0x00000200, 0xE0300100, + 0x00000204, 0x00030303, + 0x00000200, 0xE03000FC, + 0x00000204, 0x700010FF, + 0x00000200, 0xE0300058, + 0x00000204, 0x00010303, + 0x00000200, 0xE0300054, + 0x00000204, 0x800010FF, + 0x00000200, 0xE0300074, + 0x00000204, 0x00010303, + 0x00000200, 0xE0300070, + 0x00000204, 0x800010FF, + 0x00000200, 0xE0300090, + 0x00000204, 0x00010303, + 0x00000200, 0xE030008C, + 0x00000204, 0x800010FF, + 0x00000200, 0xE03000AC, + 0x00000204, 0x00010303, + 0x00000200, 0xE03000A8, + 0x00000204, 0x800010FF, + 0x00000200, 0xE03000C4, + 0x00000204, 0x800010FF, + 0x00000200, 0xE03000C8, + 0x00000204, 0x00010303, + 0x00000200, 0xE03000E4, + 0x00000204, 0x00010303, + 0x00000200, 0xE03000E0, + 0x00000204, 0x800010FF, + 0x00000200, 0xE0300100, + 0x00000204, 0x00010303, + 0x00000200, 0xE03000FC, + 0x00000204, 0x800010FF, + 0x00000200, 0x0001f198, + 0x00000204, 0x0003ffff, + 0x00000200, 0x0001f19C, + 0x00000204, 0x3fffffff, + 0x00000200, 0xE030032C, + 0x00000204, 0x00000000, +}; + +static void trinity_program_clk_gating_hw_sequence(struct radeon_device *rdev, + const u32 *seq, u32 count); +static void trinity_override_dynamic_mg_powergating(struct radeon_device *rdev); +static void trinity_apply_state_adjust_rules(struct radeon_device *rdev); + +struct trinity_ps *trinity_get_ps(struct radeon_ps *rps) +{ + struct trinity_ps *ps = rps->ps_priv; + + return ps; +} + +struct trinity_power_info *trinity_get_pi(struct radeon_device *rdev) +{ + struct trinity_power_info *pi = rdev->pm.dpm.priv; + + return pi; +} + +static void trinity_gfx_powergating_initialize(struct radeon_device *rdev) +{ + struct trinity_power_info *pi = trinity_get_pi(rdev); + u32 p, u; + u32 value; + struct atom_clock_dividers dividers; + u32 xclk = sumo_get_xclk(rdev); + u32 sssd = 1; + int ret; + u32 hw_rev = (RREG32(HW_REV) & ATI_REV_ID_MASK) >> ATI_REV_ID_SHIFT; + + ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM, + 25000, false, ÷rs); + if (ret) + return; + + value = RREG32_SMC(GFX_POWER_GATING_CNTL); + value &= ~(SSSD_MASK | PDS_DIV_MASK); + if (sssd) + value |= SSSD(1); + value |= PDS_DIV(dividers.post_div); + WREG32_SMC(GFX_POWER_GATING_CNTL, value); + + r600_calculate_u_and_p(500, xclk, 16, &p, &u); + + WREG32(CG_PG_CTRL, SP(p) | SU(u)); + + WREG32_P(CG_GIPOTS, CG_GIPOT(p), ~CG_GIPOT_MASK); + + /* XXX double check hw_rev */ + if (pi->override_dynamic_mgpg && (hw_rev == 0)) + trinity_override_dynamic_mg_powergating(rdev); + +} + +#define CGCG_CGTT_LOCAL0_MASK 0xFFFF33FF +#define CGCG_CGTT_LOCAL1_MASK 0xFFFB0FFE +#define CGTS_SM_CTRL_REG_DISABLE 0x00600000 +#define CGTS_SM_CTRL_REG_ENABLE 0x96944200 + +static void trinity_mg_clockgating_enable(struct radeon_device *rdev, + bool enable) +{ + u32 local0; + u32 local1; + + if (enable) { + local0 = RREG32_CG(CG_CGTT_LOCAL_0); + local1 = RREG32_CG(CG_CGTT_LOCAL_1); + + WREG32_CG(CG_CGTT_LOCAL_0, + (0x00380000 & CGCG_CGTT_LOCAL0_MASK) | (local0 & ~CGCG_CGTT_LOCAL0_MASK) ); + WREG32_CG(CG_CGTT_LOCAL_1, + (0x0E000000 & CGCG_CGTT_LOCAL1_MASK) | (local1 & ~CGCG_CGTT_LOCAL1_MASK) ); + + WREG32(CGTS_SM_CTRL_REG, CGTS_SM_CTRL_REG_ENABLE); + } else { + WREG32(CGTS_SM_CTRL_REG, CGTS_SM_CTRL_REG_DISABLE); + + local0 = RREG32_CG(CG_CGTT_LOCAL_0); + local1 = RREG32_CG(CG_CGTT_LOCAL_1); + + WREG32_CG(CG_CGTT_LOCAL_0, + CGCG_CGTT_LOCAL0_MASK | (local0 & ~CGCG_CGTT_LOCAL0_MASK) ); + WREG32_CG(CG_CGTT_LOCAL_1, + CGCG_CGTT_LOCAL1_MASK | (local1 & ~CGCG_CGTT_LOCAL1_MASK) ); + } +} + +static void trinity_mg_clockgating_initialize(struct radeon_device *rdev) +{ + u32 count; + const u32 *seq = NULL; + + seq = &trinity_mgcg_shls_default[0]; + count = sizeof(trinity_mgcg_shls_default) / (3 * sizeof(u32)); + + trinity_program_clk_gating_hw_sequence(rdev, seq, count); +} + +static void trinity_gfx_clockgating_enable(struct radeon_device *rdev, + bool enable) +{ + if (enable) { + WREG32_P(SCLK_PWRMGT_CNTL, DYN_GFX_CLK_OFF_EN, ~DYN_GFX_CLK_OFF_EN); + } else { + WREG32_P(SCLK_PWRMGT_CNTL, 0, ~DYN_GFX_CLK_OFF_EN); + WREG32_P(SCLK_PWRMGT_CNTL, GFX_CLK_FORCE_ON, ~GFX_CLK_FORCE_ON); + WREG32_P(SCLK_PWRMGT_CNTL, 0, ~GFX_CLK_FORCE_ON); + RREG32(GB_ADDR_CONFIG); + } +} + +static void trinity_program_clk_gating_hw_sequence(struct radeon_device *rdev, + const u32 *seq, u32 count) +{ + u32 i, length = count * 3; + + for (i = 0; i < length; i += 3) + WREG32_P(seq[i], seq[i+1], ~seq[i+2]); +} + +static void trinity_program_override_mgpg_sequences(struct radeon_device *rdev, + const u32 *seq, u32 count) +{ + u32 i, length = count * 2; + + for (i = 0; i < length; i += 2) + WREG32(seq[i], seq[i+1]); + +} + +static void trinity_override_dynamic_mg_powergating(struct radeon_device *rdev) +{ + u32 count; + const u32 *seq = NULL; + + seq = &trinity_override_mgpg_sequences[0]; + count = sizeof(trinity_override_mgpg_sequences) / (2 * sizeof(u32)); + + trinity_program_override_mgpg_sequences(rdev, seq, count); +} + +static void trinity_ls_clockgating_enable(struct radeon_device *rdev, + bool enable) +{ + u32 count; + const u32 *seq = NULL; + + if (enable) { + seq = &trinity_sysls_enable[0]; + count = sizeof(trinity_sysls_enable) / (3 * sizeof(u32)); + } else { + seq = &trinity_sysls_disable[0]; + count = sizeof(trinity_sysls_disable) / (3 * sizeof(u32)); + } + + trinity_program_clk_gating_hw_sequence(rdev, seq, count); +} + +static void trinity_gfx_powergating_enable(struct radeon_device *rdev, + bool enable) +{ + if (enable) { + if (RREG32_SMC(CC_SMU_TST_EFUSE1_MISC) & RB_BACKEND_DISABLE_MASK) + WREG32_SMC(SMU_SCRATCH_A, (RREG32_SMC(SMU_SCRATCH_A) | 0x01)); + + WREG32_P(SCLK_PWRMGT_CNTL, DYN_PWR_DOWN_EN, ~DYN_PWR_DOWN_EN); + } else { + WREG32_P(SCLK_PWRMGT_CNTL, 0, ~DYN_PWR_DOWN_EN); + RREG32(GB_ADDR_CONFIG); + } +} + +static void trinity_gfx_dynamic_mgpg_enable(struct radeon_device *rdev, + bool enable) +{ + u32 value; + + if (enable) { + value = RREG32_SMC(PM_I_CNTL_1); + value &= ~DS_PG_CNTL_MASK; + value |= DS_PG_CNTL(1); + WREG32_SMC(PM_I_CNTL_1, value); + + value = RREG32_SMC(SMU_S_PG_CNTL); + value &= ~DS_PG_EN_MASK; + value |= DS_PG_EN(1); + WREG32_SMC(SMU_S_PG_CNTL, value); + } else { + value = RREG32_SMC(SMU_S_PG_CNTL); + value &= ~DS_PG_EN_MASK; + WREG32_SMC(SMU_S_PG_CNTL, value); + + value = RREG32_SMC(PM_I_CNTL_1); + value &= ~DS_PG_CNTL_MASK; + WREG32_SMC(PM_I_CNTL_1, value); + } + + trinity_gfx_dynamic_mgpg_config(rdev); + +} + +static void trinity_enable_clock_power_gating(struct radeon_device *rdev) +{ + struct trinity_power_info *pi = trinity_get_pi(rdev); + + if (pi->enable_gfx_clock_gating) + sumo_gfx_clockgating_initialize(rdev); + if (pi->enable_mg_clock_gating) + trinity_mg_clockgating_initialize(rdev); + if (pi->enable_gfx_power_gating) + trinity_gfx_powergating_initialize(rdev); + if (pi->enable_mg_clock_gating) { + trinity_ls_clockgating_enable(rdev, true); + trinity_mg_clockgating_enable(rdev, true); + } + if (pi->enable_gfx_clock_gating) + trinity_gfx_clockgating_enable(rdev, true); + if (pi->enable_gfx_dynamic_mgpg) + trinity_gfx_dynamic_mgpg_enable(rdev, true); + if (pi->enable_gfx_power_gating) + trinity_gfx_powergating_enable(rdev, true); +} + +static void trinity_disable_clock_power_gating(struct radeon_device *rdev) +{ + struct trinity_power_info *pi = trinity_get_pi(rdev); + + if (pi->enable_gfx_power_gating) + trinity_gfx_powergating_enable(rdev, false); + if (pi->enable_gfx_dynamic_mgpg) + trinity_gfx_dynamic_mgpg_enable(rdev, false); + if (pi->enable_gfx_clock_gating) + trinity_gfx_clockgating_enable(rdev, false); + if (pi->enable_mg_clock_gating) { + trinity_mg_clockgating_enable(rdev, false); + trinity_ls_clockgating_enable(rdev, false); + } +} + +static void trinity_set_divider_value(struct radeon_device *rdev, + u32 index, u32 sclk) +{ + struct atom_clock_dividers dividers; + int ret; + u32 value; + u32 ix = index * TRINITY_SIZEOF_DPM_STATE_TABLE; + + ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM, + sclk, false, ÷rs); + if (ret) + return; + + value = RREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_0 + ix); + value &= ~CLK_DIVIDER_MASK; + value |= CLK_DIVIDER(dividers.post_div); + WREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_0 + ix, value); + + ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM, + sclk/2, false, ÷rs); + if (ret) + return; + + value = RREG32_SMC(SMU_SCLK_DPM_STATE_0_PG_CNTL + ix); + value &= ~PD_SCLK_DIVIDER_MASK; + value |= PD_SCLK_DIVIDER(dividers.post_div); + WREG32_SMC(SMU_SCLK_DPM_STATE_0_PG_CNTL + ix, value); +} + +static void trinity_set_ds_dividers(struct radeon_device *rdev, + u32 index, u32 divider) +{ + u32 value; + u32 ix = index * TRINITY_SIZEOF_DPM_STATE_TABLE; + + value = RREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_1 + ix); + value &= ~DS_DIV_MASK; + value |= DS_DIV(divider); + WREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_1 + ix, value); +} + +static void trinity_set_ss_dividers(struct radeon_device *rdev, + u32 index, u32 divider) +{ + u32 value; + u32 ix = index * TRINITY_SIZEOF_DPM_STATE_TABLE; + + value = RREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_1 + ix); + value &= ~DS_SH_DIV_MASK; + value |= DS_SH_DIV(divider); + WREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_1 + ix, value); +} + +static void trinity_set_vid(struct radeon_device *rdev, u32 index, u32 vid) +{ + struct trinity_power_info *pi = trinity_get_pi(rdev); + u32 vid_7bit = sumo_convert_vid2_to_vid7(rdev, &pi->sys_info.vid_mapping_table, vid); + u32 value; + u32 ix = index * TRINITY_SIZEOF_DPM_STATE_TABLE; + + value = RREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_0 + ix); + value &= ~VID_MASK; + value |= VID(vid_7bit); + WREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_0 + ix, value); + + value = RREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_0 + ix); + value &= ~LVRT_MASK; + value |= LVRT(0); + WREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_0 + ix, value); +} + +static void trinity_set_allos_gnb_slow(struct radeon_device *rdev, + u32 index, u32 gnb_slow) +{ + u32 value; + u32 ix = index * TRINITY_SIZEOF_DPM_STATE_TABLE; + + value = RREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_3 + ix); + value &= ~GNB_SLOW_MASK; + value |= GNB_SLOW(gnb_slow); + WREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_3 + ix, value); +} + +static void trinity_set_force_nbp_state(struct radeon_device *rdev, + u32 index, u32 force_nbp_state) +{ + u32 value; + u32 ix = index * TRINITY_SIZEOF_DPM_STATE_TABLE; + + value = RREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_3 + ix); + value &= ~FORCE_NBPS1_MASK; + value |= FORCE_NBPS1(force_nbp_state); + WREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_3 + ix, value); +} + +static void trinity_set_display_wm(struct radeon_device *rdev, + u32 index, u32 wm) +{ + u32 value; + u32 ix = index * TRINITY_SIZEOF_DPM_STATE_TABLE; + + value = RREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_1 + ix); + value &= ~DISPLAY_WM_MASK; + value |= DISPLAY_WM(wm); + WREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_1 + ix, value); +} + +static void trinity_set_vce_wm(struct radeon_device *rdev, + u32 index, u32 wm) +{ + u32 value; + u32 ix = index * TRINITY_SIZEOF_DPM_STATE_TABLE; + + value = RREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_1 + ix); + value &= ~VCE_WM_MASK; + value |= VCE_WM(wm); + WREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_1 + ix, value); +} + +static void trinity_set_at(struct radeon_device *rdev, + u32 index, u32 at) +{ + u32 value; + u32 ix = index * TRINITY_SIZEOF_DPM_STATE_TABLE; + + value = RREG32_SMC(SMU_SCLK_DPM_STATE_0_AT + ix); + value &= ~AT_MASK; + value |= AT(at); + WREG32_SMC(SMU_SCLK_DPM_STATE_0_AT + ix, value); +} + +static void trinity_program_power_level(struct radeon_device *rdev, + struct trinity_pl *pl, u32 index) +{ + struct trinity_power_info *pi = trinity_get_pi(rdev); + + if (index >= SUMO_MAX_HARDWARE_POWERLEVELS) + return; + + trinity_set_divider_value(rdev, index, pl->sclk); + trinity_set_vid(rdev, index, pl->vddc_index); + trinity_set_ss_dividers(rdev, index, pl->ss_divider_index); + trinity_set_ds_dividers(rdev, index, pl->ds_divider_index); + trinity_set_allos_gnb_slow(rdev, index, pl->allow_gnb_slow); + trinity_set_force_nbp_state(rdev, index, pl->force_nbp_state); + trinity_set_display_wm(rdev, index, pl->display_wm); + trinity_set_vce_wm(rdev, index, pl->vce_wm); + trinity_set_at(rdev, index, pi->at[index]); +} + +static void trinity_power_level_enable_disable(struct radeon_device *rdev, + u32 index, bool enable) +{ + u32 value; + u32 ix = index * TRINITY_SIZEOF_DPM_STATE_TABLE; + + value = RREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_0 + ix); + value &= ~STATE_VALID_MASK; + if (enable) + value |= STATE_VALID(1); + WREG32_SMC(SMU_SCLK_DPM_STATE_0_CNTL_0 + ix, value); +} + +static bool trinity_dpm_enabled(struct radeon_device *rdev) +{ + if (RREG32_SMC(SMU_SCLK_DPM_CNTL) & SCLK_DPM_EN(1)) + return true; + else + return false; +} + +static void trinity_start_dpm(struct radeon_device *rdev) +{ + u32 value = RREG32_SMC(SMU_SCLK_DPM_CNTL); + + value &= ~(SCLK_DPM_EN_MASK | SCLK_DPM_BOOT_STATE_MASK | VOLTAGE_CHG_EN_MASK); + value |= SCLK_DPM_EN(1) | SCLK_DPM_BOOT_STATE(0) | VOLTAGE_CHG_EN(1); + WREG32_SMC(SMU_SCLK_DPM_CNTL, value); + + WREG32_P(GENERAL_PWRMGT, GLOBAL_PWRMGT_EN, ~GLOBAL_PWRMGT_EN); + WREG32_P(CG_CG_VOLTAGE_CNTL, 0, ~EN); + + trinity_dpm_config(rdev, true); +} + +static void trinity_wait_for_dpm_enabled(struct radeon_device *rdev) +{ + int i; + + for (i = 0; i < rdev->usec_timeout; i++) { + if (RREG32(SCLK_PWRMGT_CNTL) & DYNAMIC_PM_EN) + break; + udelay(1); + } + for (i = 0; i < rdev->usec_timeout; i++) { + if ((RREG32(TARGET_AND_CURRENT_PROFILE_INDEX) & TARGET_STATE_MASK) == 0) + break; + udelay(1); + } + for (i = 0; i < rdev->usec_timeout; i++) { + if ((RREG32(TARGET_AND_CURRENT_PROFILE_INDEX) & CURRENT_STATE_MASK) == 0) + break; + udelay(1); + } +} + +static void trinity_stop_dpm(struct radeon_device *rdev) +{ + u32 sclk_dpm_cntl; + + WREG32_P(CG_CG_VOLTAGE_CNTL, EN, ~EN); + + sclk_dpm_cntl = RREG32_SMC(SMU_SCLK_DPM_CNTL); + sclk_dpm_cntl &= ~(SCLK_DPM_EN_MASK | VOLTAGE_CHG_EN_MASK); + WREG32_SMC(SMU_SCLK_DPM_CNTL, sclk_dpm_cntl); + + trinity_dpm_config(rdev, false); +} + +static void trinity_start_am(struct radeon_device *rdev) +{ + WREG32_P(SCLK_PWRMGT_CNTL, 0, ~(RESET_SCLK_CNT | RESET_BUSY_CNT)); +} + +static void trinity_reset_am(struct radeon_device *rdev) +{ + WREG32_P(SCLK_PWRMGT_CNTL, RESET_SCLK_CNT | RESET_BUSY_CNT, + ~(RESET_SCLK_CNT | RESET_BUSY_CNT)); +} + +static void trinity_wait_for_level_0(struct radeon_device *rdev) +{ + int i; + + for (i = 0; i < rdev->usec_timeout; i++) { + if ((RREG32(TARGET_AND_CURRENT_PROFILE_INDEX) & CURRENT_STATE_MASK) == 0) + break; + udelay(1); + } +} + +static void trinity_enable_power_level_0(struct radeon_device *rdev) +{ + trinity_power_level_enable_disable(rdev, 0, true); +} + +static void trinity_force_level_0(struct radeon_device *rdev) +{ + trinity_dpm_force_state(rdev, 0); +} + +static void trinity_unforce_levels(struct radeon_device *rdev) +{ + trinity_dpm_no_forced_level(rdev); +} + +static void trinity_update_current_power_levels(struct radeon_device *rdev) +{ + struct trinity_ps *new_ps = trinity_get_ps(rdev->pm.dpm.requested_ps); + struct trinity_power_info *pi = trinity_get_pi(rdev); + + pi->current_ps = *new_ps; +} + +static void trinity_program_power_levels_0_to_n(struct radeon_device *rdev) +{ + struct trinity_ps *new_ps = trinity_get_ps(rdev->pm.dpm.requested_ps); + struct trinity_ps *old_ps = trinity_get_ps(rdev->pm.dpm.current_ps); + u32 i; + u32 n_current_state_levels = (old_ps == NULL) ? 1 : old_ps->num_levels; + + for (i = 0; i < new_ps->num_levels; i++) { + trinity_program_power_level(rdev, &new_ps->levels[i], i); + trinity_power_level_enable_disable(rdev, i, true); + } + + for (i = new_ps->num_levels; i < n_current_state_levels; i++) + trinity_power_level_enable_disable(rdev, i, false); +} + +static void trinity_program_bootup_state(struct radeon_device *rdev) +{ + struct trinity_power_info *pi = trinity_get_pi(rdev); + u32 i; + + trinity_program_power_level(rdev, &pi->boot_pl, 0); + trinity_power_level_enable_disable(rdev, 0, true); + + for (i = 1; i < 8; i++) + trinity_power_level_enable_disable(rdev, i, false); +} + +static void trinity_program_ttt(struct radeon_device *rdev) +{ + struct trinity_power_info *pi = trinity_get_pi(rdev); + u32 value = RREG32_SMC(SMU_SCLK_DPM_TTT); + + value &= ~(HT_MASK | LT_MASK); + value |= HT((pi->thermal_auto_throttling + 49) * 8); + value |= LT((pi->thermal_auto_throttling + 49 - pi->sys_info.htc_hyst_lmt) * 8); + WREG32_SMC(SMU_SCLK_DPM_TTT, value); +} + +static void trinity_enable_att(struct radeon_device *rdev) +{ + u32 value = RREG32_SMC(SMU_SCLK_DPM_TT_CNTL); + + value &= ~SCLK_TT_EN_MASK; + value |= SCLK_TT_EN(1); + WREG32_SMC(SMU_SCLK_DPM_TT_CNTL, value); +} + +static void trinity_program_sclk_dpm(struct radeon_device *rdev) +{ + u32 p, u; + u32 tp = RREG32_SMC(PM_TP); + u32 ni; + u32 xclk = sumo_get_xclk(rdev); + u32 value; + + r600_calculate_u_and_p(400, xclk, 16, &p, &u); + + ni = (p + tp - 1) / tp; + + value = RREG32_SMC(PM_I_CNTL_1); + value &= ~SCLK_DPM_MASK; + value |= SCLK_DPM(ni); + WREG32_SMC(PM_I_CNTL_1, value); +} + +static int trinity_set_thermal_temperature_range(struct radeon_device *rdev, + int min_temp, int max_temp) +{ + int low_temp = 0 * 1000; + int high_temp = 255 * 1000; + + if (low_temp < min_temp) + low_temp = min_temp; + if (high_temp > max_temp) + high_temp = max_temp; + if (high_temp < low_temp) { + DRM_ERROR("invalid thermal range: %d - %d\n", low_temp, high_temp); + return -EINVAL; + } + + WREG32_P(CG_THERMAL_INT_CTRL, DIG_THERM_INTH(49 + (high_temp / 1000)), ~DIG_THERM_INTH_MASK); + WREG32_P(CG_THERMAL_INT_CTRL, DIG_THERM_INTL(49 + (low_temp / 1000)), ~DIG_THERM_INTL_MASK); + + rdev->pm.dpm.thermal.min_temp = low_temp; + rdev->pm.dpm.thermal.max_temp = high_temp; + + return 0; +} + +int trinity_dpm_enable(struct radeon_device *rdev) +{ + struct trinity_power_info *pi = trinity_get_pi(rdev); + + trinity_acquire_mutex(rdev); + + if (trinity_dpm_enabled(rdev)) { + trinity_release_mutex(rdev); + return -EINVAL; + } + + trinity_enable_clock_power_gating(rdev); + trinity_program_bootup_state(rdev); + sumo_program_vc(rdev, 0x00C00033); + trinity_start_am(rdev); + if (pi->enable_auto_thermal_throttling) { + trinity_program_ttt(rdev); + trinity_enable_att(rdev); + } + trinity_program_sclk_dpm(rdev); + trinity_start_dpm(rdev); + trinity_wait_for_dpm_enabled(rdev); + trinity_release_mutex(rdev); + + if (rdev->irq.installed && + r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) { + trinity_set_thermal_temperature_range(rdev, R600_TEMP_RANGE_MIN, R600_TEMP_RANGE_MAX); + rdev->irq.dpm_thermal = true; + radeon_irq_set(rdev); + } + + return 0; +} + +void trinity_dpm_disable(struct radeon_device *rdev) +{ + trinity_acquire_mutex(rdev); + if (!trinity_dpm_enabled(rdev)) { + trinity_release_mutex(rdev); + return; + } + trinity_disable_clock_power_gating(rdev); + sumo_clear_vc(rdev); + trinity_wait_for_level_0(rdev); + trinity_stop_dpm(rdev); + trinity_reset_am(rdev); + trinity_release_mutex(rdev); + + if (rdev->irq.installed && + r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) { + rdev->irq.dpm_thermal = false; + radeon_irq_set(rdev); + } +} + +static void trinity_get_min_sclk_divider(struct radeon_device *rdev) +{ + struct trinity_power_info *pi = trinity_get_pi(rdev); + + pi->min_sclk_did = + (RREG32_SMC(CC_SMU_MISC_FUSES) & MinSClkDid_MASK) >> MinSClkDid_SHIFT; +} + +static void trinity_setup_nbp_sim(struct radeon_device *rdev) +{ + struct trinity_power_info *pi = trinity_get_pi(rdev); + struct trinity_ps *new_ps = trinity_get_ps(rdev->pm.dpm.requested_ps); + u32 nbpsconfig; + + if (pi->sys_info.nb_dpm_enable) { + nbpsconfig = RREG32_SMC(NB_PSTATE_CONFIG); + nbpsconfig &= ~(Dpm0PgNbPsLo_MASK | Dpm0PgNbPsHi_MASK | DpmXNbPsLo_MASK | DpmXNbPsHi_MASK); + nbpsconfig |= (Dpm0PgNbPsLo(new_ps->Dpm0PgNbPsLo) | + Dpm0PgNbPsHi(new_ps->Dpm0PgNbPsHi) | + DpmXNbPsLo(new_ps->DpmXNbPsLo) | + DpmXNbPsHi(new_ps->DpmXNbPsHi)); + WREG32_SMC(NB_PSTATE_CONFIG, nbpsconfig); + } +} + +int trinity_dpm_set_power_state(struct radeon_device *rdev) +{ + struct trinity_power_info *pi = trinity_get_pi(rdev); + + trinity_apply_state_adjust_rules(rdev); + trinity_update_current_power_levels(rdev); + + trinity_acquire_mutex(rdev); + if (pi->enable_dpm) { + trinity_enable_power_level_0(rdev); + trinity_force_level_0(rdev); + trinity_wait_for_level_0(rdev); + trinity_setup_nbp_sim(rdev); + trinity_program_power_levels_0_to_n(rdev); + trinity_force_level_0(rdev); + trinity_unforce_levels(rdev); + } + trinity_release_mutex(rdev); + + return 0; +} + +void trinity_dpm_setup_asic(struct radeon_device *rdev) +{ + trinity_acquire_mutex(rdev); + sumo_program_sstp(rdev); + sumo_take_smu_control(rdev, true); + trinity_get_min_sclk_divider(rdev); + trinity_release_mutex(rdev); +} + +void trinity_dpm_reset_asic(struct radeon_device *rdev) +{ + struct trinity_power_info *pi = trinity_get_pi(rdev); + + trinity_acquire_mutex(rdev); + if (pi->enable_dpm) { + trinity_enable_power_level_0(rdev); + trinity_force_level_0(rdev); + trinity_wait_for_level_0(rdev); + trinity_program_bootup_state(rdev); + trinity_force_level_0(rdev); + trinity_unforce_levels(rdev); + } + trinity_release_mutex(rdev); +} + +static u16 trinity_convert_voltage_index_to_value(struct radeon_device *rdev, + u32 vid_2bit) +{ + struct trinity_power_info *pi = trinity_get_pi(rdev); + u32 vid_7bit = sumo_convert_vid2_to_vid7(rdev, &pi->sys_info.vid_mapping_table, vid_2bit); + u32 svi_mode = (RREG32_SMC(PM_CONFIG) & SVI_Mode) ? 1 : 0; + u32 step = (svi_mode == 0) ? 1250 : 625; + u32 delta = vid_7bit * step + 50; + + if (delta > 155000) + return 0; + + return (155000 - delta) / 100; +} + +static void trinity_patch_boot_state(struct radeon_device *rdev, + struct trinity_ps *ps) +{ + struct trinity_power_info *pi = trinity_get_pi(rdev); + + ps->num_levels = 1; + ps->nbps_flags = 0; + ps->bapm_flags = 0; + ps->levels[0] = pi->boot_pl; +} + +static u8 trinity_calculate_vce_wm(struct radeon_device *rdev, u32 sclk) +{ + if (sclk < 20000) + return 1; + return 0; +} + +static void trinity_construct_boot_state(struct radeon_device *rdev) +{ + struct trinity_power_info *pi = trinity_get_pi(rdev); + + pi->boot_pl.sclk = pi->sys_info.bootup_sclk; + pi->boot_pl.vddc_index = pi->sys_info.bootup_nb_voltage_index; + pi->boot_pl.ds_divider_index = 0; + pi->boot_pl.ss_divider_index = 0; + pi->boot_pl.allow_gnb_slow = 1; + pi->boot_pl.force_nbp_state = 0; + pi->boot_pl.display_wm = 0; + pi->boot_pl.vce_wm = 0; + pi->current_ps.num_levels = 1; + pi->current_ps.levels[0] = pi->boot_pl; +} + +static u8 trinity_get_sleep_divider_id_from_clock(struct radeon_device *rdev, + u32 sclk, u32 min_sclk_in_sr) +{ + struct trinity_power_info *pi = trinity_get_pi(rdev); + u32 i; + u32 temp; + u32 min = (min_sclk_in_sr > TRINITY_MINIMUM_ENGINE_CLOCK) ? + min_sclk_in_sr : TRINITY_MINIMUM_ENGINE_CLOCK; + + if (sclk < min) + return 0; + + if (!pi->enable_sclk_ds) + return 0; + + for (i = TRINITY_MAX_DEEPSLEEP_DIVIDER_ID; ; i--) { + temp = sclk / sumo_get_sleep_divider_from_id(i); + if (temp >= min || i == 0) + break; + } + + return (u8)i; +} + +static u32 trinity_get_valid_engine_clock(struct radeon_device *rdev, + u32 lower_limit) +{ + struct trinity_power_info *pi = trinity_get_pi(rdev); + u32 i; + + for (i = 0; i < pi->sys_info.sclk_voltage_mapping_table.num_max_dpm_entries; i++) { + if (pi->sys_info.sclk_voltage_mapping_table.entries[i].sclk_frequency >= lower_limit) + return pi->sys_info.sclk_voltage_mapping_table.entries[i].sclk_frequency; + } + + if (i == pi->sys_info.sclk_voltage_mapping_table.num_max_dpm_entries) + DRM_ERROR("engine clock out of range!"); + + return 0; +} + +static void trinity_patch_thermal_state(struct radeon_device *rdev, + struct trinity_ps *ps, + struct trinity_ps *current_ps) +{ + struct trinity_power_info *pi = trinity_get_pi(rdev); + u32 sclk_in_sr = pi->sys_info.min_sclk; /* ??? */ + u32 current_vddc; + u32 current_sclk; + u32 current_index = 0; + + if (current_ps) { + current_vddc = current_ps->levels[current_index].vddc_index; + current_sclk = current_ps->levels[current_index].sclk; + } else { + current_vddc = pi->boot_pl.vddc_index; + current_sclk = pi->boot_pl.sclk; + } + + ps->levels[0].vddc_index = current_vddc; + + if (ps->levels[0].sclk > current_sclk) + ps->levels[0].sclk = current_sclk; + + ps->levels[0].ds_divider_index = + trinity_get_sleep_divider_id_from_clock(rdev, ps->levels[0].sclk, sclk_in_sr); + ps->levels[0].ss_divider_index = ps->levels[0].ds_divider_index; + ps->levels[0].allow_gnb_slow = 1; + ps->levels[0].force_nbp_state = 0; + ps->levels[0].display_wm = 0; + ps->levels[0].vce_wm = + trinity_calculate_vce_wm(rdev, ps->levels[0].sclk); +} + +static u8 trinity_calculate_display_wm(struct radeon_device *rdev, + struct trinity_ps *ps, u32 index) +{ + if (ps == NULL || ps->num_levels <= 1) + return 0; + else if (ps->num_levels == 2) { + if (index == 0) + return 0; + else + return 1; + } else { + if (index == 0) + return 0; + else if (ps->levels[index].sclk < 30000) + return 0; + else + return 1; + } +} + +static void trinity_apply_state_adjust_rules(struct radeon_device *rdev) +{ + struct radeon_ps *rps = rdev->pm.dpm.requested_ps; + struct trinity_ps *ps = trinity_get_ps(rps); + struct trinity_ps *current_ps = trinity_get_ps(rdev->pm.dpm.current_ps); + struct trinity_power_info *pi = trinity_get_pi(rdev); + u32 min_voltage = 0; /* ??? */ + u32 min_sclk = pi->sys_info.min_sclk; /* XXX check against disp reqs */ + u32 sclk_in_sr = pi->sys_info.min_sclk; /* ??? */ + u32 i; + bool force_high; + u32 num_active_displays = rdev->pm.dpm.new_active_crtc_count; + + if (rps->class & ATOM_PPLIB_CLASSIFICATION_THERMAL) + return trinity_patch_thermal_state(rdev, ps, current_ps); + + for (i = 0; i < ps->num_levels; i++) { + if (ps->levels[i].vddc_index < min_voltage) + ps->levels[i].vddc_index = min_voltage; + + if (ps->levels[i].sclk < min_sclk) + ps->levels[i].sclk = + trinity_get_valid_engine_clock(rdev, min_sclk); + + ps->levels[i].ds_divider_index = + sumo_get_sleep_divider_id_from_clock(rdev, ps->levels[i].sclk, sclk_in_sr); + + ps->levels[i].ss_divider_index = ps->levels[i].ds_divider_index; + + ps->levels[i].allow_gnb_slow = 1; + ps->levels[i].force_nbp_state = 0; + ps->levels[i].display_wm = + trinity_calculate_display_wm(rdev, ps, i); + ps->levels[i].vce_wm = + trinity_calculate_vce_wm(rdev, ps->levels[0].sclk); + } + + if ((rps->class & (ATOM_PPLIB_CLASSIFICATION_HDSTATE | ATOM_PPLIB_CLASSIFICATION_SDSTATE)) || + ((rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) == ATOM_PPLIB_CLASSIFICATION_UI_BATTERY)) + ps->bapm_flags |= TRINITY_POWERSTATE_FLAGS_BAPM_DISABLE; + + if (pi->sys_info.nb_dpm_enable) { + ps->Dpm0PgNbPsLo = 0x1; + ps->Dpm0PgNbPsHi = 0x0; + ps->DpmXNbPsLo = 0x2; + ps->DpmXNbPsHi = 0x1; + + if ((rps->class & (ATOM_PPLIB_CLASSIFICATION_HDSTATE | ATOM_PPLIB_CLASSIFICATION_SDSTATE)) || + ((rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) == ATOM_PPLIB_CLASSIFICATION_UI_BATTERY)) { + force_high = ((rps->class & ATOM_PPLIB_CLASSIFICATION_HDSTATE) || + ((rps->class & ATOM_PPLIB_CLASSIFICATION_SDSTATE) && + (pi->sys_info.uma_channel_number == 1))); + force_high = (num_active_displays >= 3) || force_high; + ps->Dpm0PgNbPsLo = force_high ? 0x2 : 0x3; + ps->Dpm0PgNbPsHi = 0x1; + ps->DpmXNbPsLo = force_high ? 0x2 : 0x3; + ps->DpmXNbPsHi = 0x2; + ps->levels[ps->num_levels - 1].allow_gnb_slow = 0; + } + } +} + +static void trinity_cleanup_asic(struct radeon_device *rdev) +{ + sumo_take_smu_control(rdev, false); +} + +#if 0 +static void trinity_pre_display_configuration_change(struct radeon_device *rdev) +{ + struct trinity_power_info *pi = trinity_get_pi(rdev); + + if (pi->voltage_drop_in_dce) + trinity_dce_enable_voltage_adjustment(rdev, false); +} +#endif + +static void trinity_add_dccac_value(struct radeon_device *rdev) +{ + u32 gpu_cac_avrg_cntl_window_size; + u32 num_active_displays = rdev->pm.dpm.new_active_crtc_count; + u64 disp_clk = rdev->clock.default_dispclk / 100; + u32 dc_cac_value; + + gpu_cac_avrg_cntl_window_size = + (RREG32_SMC(GPU_CAC_AVRG_CNTL) & WINDOW_SIZE_MASK) >> WINDOW_SIZE_SHIFT; + + dc_cac_value = (u32)((14213 * disp_clk * disp_clk * (u64)num_active_displays) >> + (32 - gpu_cac_avrg_cntl_window_size)); + + WREG32_SMC(DC_CAC_VALUE, dc_cac_value); +} + +void trinity_dpm_display_configuration_changed(struct radeon_device *rdev) +{ + struct trinity_power_info *pi = trinity_get_pi(rdev); + + if (pi->voltage_drop_in_dce) + trinity_dce_enable_voltage_adjustment(rdev, true); + trinity_add_dccac_value(rdev); +} + +union power_info { + struct _ATOM_POWERPLAY_INFO info; + struct _ATOM_POWERPLAY_INFO_V2 info_2; + struct _ATOM_POWERPLAY_INFO_V3 info_3; + struct _ATOM_PPLIB_POWERPLAYTABLE pplib; + struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2; + struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3; +}; + +union pplib_clock_info { + struct _ATOM_PPLIB_R600_CLOCK_INFO r600; + struct _ATOM_PPLIB_RS780_CLOCK_INFO rs780; + struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO evergreen; + struct _ATOM_PPLIB_SUMO_CLOCK_INFO sumo; +}; + +union pplib_power_state { + struct _ATOM_PPLIB_STATE v1; + struct _ATOM_PPLIB_STATE_V2 v2; +}; + +static void trinity_parse_pplib_non_clock_info(struct radeon_device *rdev, + struct radeon_ps *rps, + struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info, + u8 table_rev) +{ + struct trinity_ps *ps = trinity_get_ps(rps); + + rps->caps = le32_to_cpu(non_clock_info->ulCapsAndSettings); + rps->class = le16_to_cpu(non_clock_info->usClassification); + rps->class2 = le16_to_cpu(non_clock_info->usClassification2); + + if (ATOM_PPLIB_NONCLOCKINFO_VER1 < table_rev) { + rps->vclk = le32_to_cpu(non_clock_info->ulVCLK); + rps->dclk = le32_to_cpu(non_clock_info->ulDCLK); + } else { + rps->vclk = 0; + rps->dclk = 0; + } + + if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT) { + rdev->pm.dpm.boot_ps = rps; + trinity_patch_boot_state(rdev, ps); + } + if (rps->class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE) + rdev->pm.dpm.uvd_ps = rps; +} + +static void trinity_parse_pplib_clock_info(struct radeon_device *rdev, + struct radeon_ps *rps, int index, + union pplib_clock_info *clock_info) +{ + struct trinity_power_info *pi = trinity_get_pi(rdev); + struct trinity_ps *ps = trinity_get_ps(rps); + struct trinity_pl *pl = &ps->levels[index]; + u32 sclk; + + sclk = le16_to_cpu(clock_info->sumo.usEngineClockLow); + sclk |= clock_info->sumo.ucEngineClockHigh << 16; + pl->sclk = sclk; + pl->vddc_index = clock_info->sumo.vddcIndex; + + ps->num_levels = index + 1; + + if (pi->enable_sclk_ds) { + pl->ds_divider_index = 5; + pl->ss_divider_index = 5; + } +} + +static int trinity_parse_power_table(struct radeon_device *rdev) +{ + struct radeon_mode_info *mode_info = &rdev->mode_info; + struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info; + union pplib_power_state *power_state; + int i, j, k, non_clock_array_index, clock_array_index; + union pplib_clock_info *clock_info; + struct _StateArray *state_array; + struct _ClockInfoArray *clock_info_array; + struct _NonClockInfoArray *non_clock_info_array; + union power_info *power_info; + int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo); + u16 data_offset; + u8 frev, crev; + u8 *power_state_offset; + struct sumo_ps *ps; + + if (!atom_parse_data_header(mode_info->atom_context, index, NULL, + &frev, &crev, &data_offset)) + return -EINVAL; + power_info = (union power_info *)(mode_info->atom_context->bios + data_offset); + + state_array = (struct _StateArray *) + (mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib.usStateArrayOffset)); + clock_info_array = (struct _ClockInfoArray *) + (mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib.usClockInfoArrayOffset)); + non_clock_info_array = (struct _NonClockInfoArray *) + (mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset)); + + rdev->pm.dpm.ps = kzalloc(sizeof(struct radeon_ps) * + state_array->ucNumEntries, GFP_KERNEL); + if (!rdev->pm.dpm.ps) + return -ENOMEM; + power_state_offset = (u8 *)state_array->states; + rdev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps); + rdev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime); + rdev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime); + for (i = 0; i < state_array->ucNumEntries; i++) { + power_state = (union pplib_power_state *)power_state_offset; + non_clock_array_index = power_state->v2.nonClockInfoIndex; + non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *) + &non_clock_info_array->nonClockInfo[non_clock_array_index]; + if (!rdev->pm.power_state[i].clock_info) + return -EINVAL; + ps = kzalloc(sizeof(struct sumo_ps), GFP_KERNEL); + if (ps == NULL) { + kfree(rdev->pm.dpm.ps); + return -ENOMEM; + } + rdev->pm.dpm.ps[i].ps_priv = ps; + k = 0; + for (j = 0; j < power_state->v2.ucNumDPMLevels; j++) { + clock_array_index = power_state->v2.clockInfoIndex[j]; + if (clock_array_index >= clock_info_array->ucNumEntries) + continue; + if (k >= SUMO_MAX_HARDWARE_POWERLEVELS) + break; + clock_info = (union pplib_clock_info *) + &clock_info_array->clockInfo[clock_array_index * clock_info_array->ucEntrySize]; + trinity_parse_pplib_clock_info(rdev, + &rdev->pm.dpm.ps[i], k, + clock_info); + k++; + } + trinity_parse_pplib_non_clock_info(rdev, &rdev->pm.dpm.ps[i], + non_clock_info, + non_clock_info_array->ucEntrySize); + power_state_offset += 2 + power_state->v2.ucNumDPMLevels; + } + rdev->pm.dpm.num_ps = state_array->ucNumEntries; + return 0; +} + +union igp_info { + struct _ATOM_INTEGRATED_SYSTEM_INFO info; + struct _ATOM_INTEGRATED_SYSTEM_INFO_V2 info_2; + struct _ATOM_INTEGRATED_SYSTEM_INFO_V5 info_5; + struct _ATOM_INTEGRATED_SYSTEM_INFO_V6 info_6; + struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_7 info_7; +}; + +static int trinity_parse_sys_info_table(struct radeon_device *rdev) +{ + struct trinity_power_info *pi = trinity_get_pi(rdev); + struct radeon_mode_info *mode_info = &rdev->mode_info; + int index = GetIndexIntoMasterTable(DATA, IntegratedSystemInfo); + union igp_info *igp_info; + u8 frev, crev; + u16 data_offset; + int i; + + if (atom_parse_data_header(mode_info->atom_context, index, NULL, + &frev, &crev, &data_offset)) { + igp_info = (union igp_info *)(mode_info->atom_context->bios + + data_offset); + + if (crev != 7) { + DRM_ERROR("Unsupported IGP table: %d %d\n", frev, crev); + return -EINVAL; + } + pi->sys_info.bootup_sclk = le32_to_cpu(igp_info->info_7.ulBootUpEngineClock); + pi->sys_info.min_sclk = le32_to_cpu(igp_info->info_7.ulMinEngineClock); + pi->sys_info.bootup_uma_clk = le32_to_cpu(igp_info->info_7.ulBootUpUMAClock); + pi->sys_info.bootup_nb_voltage_index = + le16_to_cpu(igp_info->info_7.usBootUpNBVoltage); + if (igp_info->info_7.ucHtcTmpLmt == 0) + pi->sys_info.htc_tmp_lmt = 203; + else + pi->sys_info.htc_tmp_lmt = igp_info->info_7.ucHtcTmpLmt; + if (igp_info->info_7.ucHtcHystLmt == 0) + pi->sys_info.htc_hyst_lmt = 5; + else + pi->sys_info.htc_hyst_lmt = igp_info->info_7.ucHtcHystLmt; + if (pi->sys_info.htc_tmp_lmt <= pi->sys_info.htc_hyst_lmt) { + DRM_ERROR("The htcTmpLmt should be larger than htcHystLmt.\n"); + } + + if (pi->enable_nbps_policy) + pi->sys_info.nb_dpm_enable = igp_info->info_7.ucNBDPMEnable; + else + pi->sys_info.nb_dpm_enable = 0; + + for (i = 0; i < TRINITY_NUM_NBPSTATES; i++) { + pi->sys_info.nbp_mclk[i] = le32_to_cpu(igp_info->info_7.ulNbpStateMemclkFreq[i]); + pi->sys_info.nbp_nclk[i] = le32_to_cpu(igp_info->info_7.ulNbpStateNClkFreq[i]); + } + + pi->sys_info.nbp_voltage_index[0] = le16_to_cpu(igp_info->info_7.usNBP0Voltage); + pi->sys_info.nbp_voltage_index[1] = le16_to_cpu(igp_info->info_7.usNBP1Voltage); + pi->sys_info.nbp_voltage_index[2] = le16_to_cpu(igp_info->info_7.usNBP2Voltage); + pi->sys_info.nbp_voltage_index[3] = le16_to_cpu(igp_info->info_7.usNBP3Voltage); + + if (!pi->sys_info.nb_dpm_enable) { + for (i = 1; i < TRINITY_NUM_NBPSTATES; i++) { + pi->sys_info.nbp_mclk[i] = pi->sys_info.nbp_mclk[0]; + pi->sys_info.nbp_nclk[i] = pi->sys_info.nbp_nclk[0]; + pi->sys_info.nbp_voltage_index[i] = pi->sys_info.nbp_voltage_index[0]; + } + } + + pi->sys_info.uma_channel_number = igp_info->info_7.ucUMAChannelNumber; + + sumo_construct_sclk_voltage_mapping_table(rdev, + &pi->sys_info.sclk_voltage_mapping_table, + igp_info->info_7.sAvail_SCLK); + sumo_construct_vid_mapping_table(rdev, &pi->sys_info.vid_mapping_table, + igp_info->info_7.sAvail_SCLK); + + } + return 0; +} + +int trinity_dpm_init(struct radeon_device *rdev) +{ + struct trinity_power_info *pi; + int ret, i; + + pi = kzalloc(sizeof(struct trinity_power_info), GFP_KERNEL); + if (pi == NULL) + return -ENOMEM; + rdev->pm.dpm.priv = pi; + + for (i = 0; i < SUMO_MAX_HARDWARE_POWERLEVELS; i++) + pi->at[i] = TRINITY_AT_DFLT; + + pi->enable_nbps_policy = true; + pi->enable_sclk_ds = true; + pi->enable_gfx_power_gating = true; + pi->enable_gfx_clock_gating = true; + pi->enable_mg_clock_gating = true; + pi->enable_gfx_dynamic_mgpg = true; /* ??? */ + pi->override_dynamic_mgpg = true; + pi->enable_auto_thermal_throttling = true; + pi->voltage_drop_in_dce = false; /* need to restructure dpm/modeset interaction */ + + ret = trinity_parse_sys_info_table(rdev); + if (ret) + return ret; + + trinity_construct_boot_state(rdev); + + ret = trinity_parse_power_table(rdev); + if (ret) + return ret; + + pi->thermal_auto_throttling = pi->sys_info.htc_tmp_lmt; + pi->enable_dpm = true; + + return 0; +} + +void trinity_dpm_print_power_state(struct radeon_device *rdev, + struct radeon_ps *rps) +{ + int i; + struct trinity_ps *ps = trinity_get_ps(rps); + + r600_dpm_print_class_info(rps->class, rps->class2); + r600_dpm_print_cap_info(rps->caps); + printk("\tuvd vclk: %d dclk: %d\n", rps->vclk, rps->dclk); + for (i = 0; i < ps->num_levels; i++) { + struct trinity_pl *pl = &ps->levels[i]; + printk("\t\tpower level %d sclk: %u vddc: %u\n", + i, pl->sclk, + trinity_convert_voltage_index_to_value(rdev, pl->vddc_index)); + } + r600_dpm_print_ps_status(rdev, rps); +} + +void trinity_dpm_fini(struct radeon_device *rdev) +{ + int i; + + trinity_cleanup_asic(rdev); /* ??? */ + + for (i = 0; i < rdev->pm.dpm.num_ps; i++) { + kfree(rdev->pm.dpm.ps[i].ps_priv); + } + kfree(rdev->pm.dpm.ps); + kfree(rdev->pm.dpm.priv); +} + +u32 trinity_dpm_get_sclk(struct radeon_device *rdev, bool low) +{ + struct trinity_ps *requested_state = trinity_get_ps(rdev->pm.dpm.requested_ps); + + if (low) + return requested_state->levels[0].sclk; + else + return requested_state->levels[requested_state->num_levels - 1].sclk; +} + +u32 trinity_dpm_get_mclk(struct radeon_device *rdev, bool low) +{ + struct trinity_power_info *pi = trinity_get_pi(rdev); + + return pi->sys_info.bootup_uma_clk; +} diff --git a/drivers/gpu/drm/radeon/trinity_dpm.h b/drivers/gpu/drm/radeon/trinity_dpm.h new file mode 100644 index 0000000..15e050f --- /dev/null +++ b/drivers/gpu/drm/radeon/trinity_dpm.h @@ -0,0 +1,110 @@ +/* + * Copyright 2012 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ +#ifndef __TRINITY_DPM_H__ +#define __TRINITY_DPM_H__ + +#include "sumo_dpm.h" + +#define TRINITY_SIZEOF_DPM_STATE_TABLE (SMU_SCLK_DPM_STATE_1_CNTL_0 - SMU_SCLK_DPM_STATE_0_CNTL_0) + +struct trinity_pl { + u32 sclk; + u8 vddc_index; + u8 ds_divider_index; + u8 ss_divider_index; + u8 allow_gnb_slow; + u8 force_nbp_state; + u8 display_wm; + u8 vce_wm; +}; + +#define TRINITY_POWERSTATE_FLAGS_NBPS_FORCEHIGH (1 << 0) +#define TRINITY_POWERSTATE_FLAGS_NBPS_LOCKTOHIGH (1 << 1) +#define TRINITY_POWERSTATE_FLAGS_NBPS_LOCKTOLOW (1 << 2) + +#define TRINITY_POWERSTATE_FLAGS_BAPM_DISABLE (1 << 0) + +struct trinity_ps { + u32 num_levels; + struct trinity_pl levels[SUMO_MAX_HARDWARE_POWERLEVELS]; + + u32 nbps_flags; + u32 bapm_flags; + + u8 Dpm0PgNbPsLo; + u8 Dpm0PgNbPsHi; + u8 DpmXNbPsLo; + u8 DpmXNbPsHi; +}; + +#define TRINITY_NUM_NBPSTATES 4 + +struct trinity_sys_info { + u32 bootup_uma_clk; + u32 bootup_sclk; + u32 min_sclk; + u32 nb_dpm_enable; + u32 nbp_mclk[TRINITY_NUM_NBPSTATES]; + u32 nbp_nclk[TRINITY_NUM_NBPSTATES]; + u16 nbp_voltage_index[TRINITY_NUM_NBPSTATES]; + u16 bootup_nb_voltage_index; + u8 htc_tmp_lmt; + u8 htc_hyst_lmt; + struct sumo_sclk_voltage_mapping_table sclk_voltage_mapping_table; + struct sumo_vid_mapping_table vid_mapping_table; + u32 uma_channel_number; +}; + +struct trinity_power_info { + u32 at[SUMO_MAX_HARDWARE_POWERLEVELS]; + u32 dpm_interval; + u32 thermal_auto_throttling; + struct trinity_sys_info sys_info; + struct trinity_pl boot_pl; + struct trinity_ps current_ps; + u32 min_sclk_did; + bool enable_nbps_policy; + bool voltage_drop_in_dce; + bool override_dynamic_mgpg; + bool enable_gfx_clock_gating; + bool enable_gfx_power_gating; + bool enable_mg_clock_gating; + bool enable_gfx_dynamic_mgpg; + bool enable_auto_thermal_throttling; + bool enable_dpm; + bool enable_sclk_ds; +}; + +#define TRINITY_AT_DFLT 30 + +/* trinity_smc.c */ +int trinity_dpm_config(struct radeon_device *rdev, bool enable); +int trinity_dpm_force_state(struct radeon_device *rdev, u32 n); +int trinity_dpm_no_forced_level(struct radeon_device *rdev); +int trinity_dce_enable_voltage_adjustment(struct radeon_device *rdev, + bool enable); +int trinity_gfx_dynamic_mgpg_config(struct radeon_device *rdev); +void trinity_acquire_mutex(struct radeon_device *rdev); +void trinity_release_mutex(struct radeon_device *rdev); + +#endif diff --git a/drivers/gpu/drm/radeon/trinity_smc.c b/drivers/gpu/drm/radeon/trinity_smc.c new file mode 100644 index 0000000..60ffc1e --- /dev/null +++ b/drivers/gpu/drm/radeon/trinity_smc.c @@ -0,0 +1,110 @@ +/* + * Copyright 2012 Advanced Micro Devices, 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. + * + */ + +#include "drmP.h" +#include "radeon.h" +#include "trinityd.h" +#include "trinity_dpm.h" +#include "ppsmc.h" + +struct trinity_ps *trinity_get_ps(struct radeon_ps *rps); +struct trinity_power_info *trinity_get_pi(struct radeon_device *rdev); + +static int trinity_notify_message_to_smu(struct radeon_device *rdev, u32 id) +{ + int i; + u32 v = 0; + + WREG32(SMC_MESSAGE_0, id); + for (i = 0; i < rdev->usec_timeout; i++) { + if (RREG32(SMC_RESP_0) != 0) + break; + udelay(1); + } + v = RREG32(SMC_RESP_0); + + if (v != 1) { + if (v == 0xFF) { + DRM_ERROR("SMC failed to handle the message!\n"); + return -EINVAL; + } else if (v == 0xFE) { + DRM_ERROR("Unknown SMC message!\n"); + return -EINVAL; + } + } + + return 0; +} + +int trinity_dpm_config(struct radeon_device *rdev, bool enable) +{ + if (enable) + WREG32_SMC(SMU_SCRATCH0, 1); + else + WREG32_SMC(SMU_SCRATCH0, 0); + + return trinity_notify_message_to_smu(rdev, PPSMC_MSG_DPM_Config); +} + +int trinity_dpm_force_state(struct radeon_device *rdev, u32 n) +{ + WREG32_SMC(SMU_SCRATCH0, n); + + return trinity_notify_message_to_smu(rdev, PPSMC_MSG_DPM_ForceState); +} + +int trinity_dpm_no_forced_level(struct radeon_device *rdev) +{ + return trinity_notify_message_to_smu(rdev, PPSMC_MSG_NoForcedLevel); +} + +int trinity_dce_enable_voltage_adjustment(struct radeon_device *rdev, + bool enable) +{ + if (enable) + return trinity_notify_message_to_smu(rdev, PPSMC_MSG_DCE_AllowVoltageAdjustment); + else + return trinity_notify_message_to_smu(rdev, PPSMC_MSG_DCE_RemoveVoltageAdjustment); +} + +int trinity_gfx_dynamic_mgpg_config(struct radeon_device *rdev) +{ + return trinity_notify_message_to_smu(rdev, PPSMC_MSG_PG_SIMD_Config); +} + +void trinity_acquire_mutex(struct radeon_device *rdev) +{ + int i; + + WREG32(SMC_INT_REQ, 1); + for (i = 0; i < rdev->usec_timeout; i++) { + if ((RREG32(SMC_INT_REQ) & 0xffff) == 1) + break; + udelay(1); + } +} + +void trinity_release_mutex(struct radeon_device *rdev) +{ + WREG32(SMC_INT_REQ, 0); +} diff --git a/drivers/gpu/drm/radeon/trinityd.h b/drivers/gpu/drm/radeon/trinityd.h new file mode 100644 index 0000000..b234d36 --- /dev/null +++ b/drivers/gpu/drm/radeon/trinityd.h @@ -0,0 +1,223 @@ +/* + * Copyright 2012 Advanced Micro Devices, 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: Alex Deucher + */ +#ifndef _TRINITYD_H_ +#define _TRINITYD_H_ + +/* pm registers */ + +/* cg */ +#define CG_CGTT_LOCAL_0 0x0 +#define CG_CGTT_LOCAL_1 0x1 + +/* smc */ +#define SMU_SCLK_DPM_STATE_0_CNTL_0 0x1f000 +# define STATE_VALID(x) ((x) << 0) +# define STATE_VALID_MASK (0xff << 0) +# define STATE_VALID_SHIFT 0 +# define CLK_DIVIDER(x) ((x) << 8) +# define CLK_DIVIDER_MASK (0xff << 8) +# define CLK_DIVIDER_SHIFT 8 +# define VID(x) ((x) << 16) +# define VID_MASK (0xff << 16) +# define VID_SHIFT 16 +# define LVRT(x) ((x) << 24) +# define LVRT_MASK (0xff << 24) +# define LVRT_SHIFT 24 +#define SMU_SCLK_DPM_STATE_0_CNTL_1 0x1f004 +# define DS_DIV(x) ((x) << 0) +# define DS_DIV_MASK (0xff << 0) +# define DS_DIV_SHIFT 0 +# define DS_SH_DIV(x) ((x) << 8) +# define DS_SH_DIV_MASK (0xff << 8) +# define DS_SH_DIV_SHIFT 8 +# define DISPLAY_WM(x) ((x) << 16) +# define DISPLAY_WM_MASK (0xff << 16) +# define DISPLAY_WM_SHIFT 16 +# define VCE_WM(x) ((x) << 24) +# define VCE_WM_MASK (0xff << 24) +# define VCE_WM_SHIFT 24 + +#define SMU_SCLK_DPM_STATE_0_CNTL_3 0x1f00c +# define GNB_SLOW(x) ((x) << 0) +# define GNB_SLOW_MASK (0xff << 0) +# define GNB_SLOW_SHIFT 0 +# define FORCE_NBPS1(x) ((x) << 8) +# define FORCE_NBPS1_MASK (0xff << 8) +# define FORCE_NBPS1_SHIFT 8 +#define SMU_SCLK_DPM_STATE_0_AT 0x1f010 +# define AT(x) ((x) << 0) +# define AT_MASK (0xff << 0) +# define AT_SHIFT 0 + +#define SMU_SCLK_DPM_STATE_0_PG_CNTL 0x1f014 +# define PD_SCLK_DIVIDER(x) ((x) << 16) +# define PD_SCLK_DIVIDER_MASK (0xff << 16) +# define PD_SCLK_DIVIDER_SHIFT 16 + +#define SMU_SCLK_DPM_STATE_1_CNTL_0 0x1f020 + +#define SMU_SCLK_DPM_CNTL 0x1f100 +# define SCLK_DPM_EN(x) ((x) << 0) +# define SCLK_DPM_EN_MASK (0xff << 0) +# define SCLK_DPM_EN_SHIFT 0 +# define SCLK_DPM_BOOT_STATE(x) ((x) << 16) +# define SCLK_DPM_BOOT_STATE_MASK (0xff << 16) +# define SCLK_DPM_BOOT_STATE_SHIFT 16 +# define VOLTAGE_CHG_EN(x) ((x) << 24) +# define VOLTAGE_CHG_EN_MASK (0xff << 24) +# define VOLTAGE_CHG_EN_SHIFT 24 + +#define SMU_SCLK_DPM_TT_CNTL 0x1f108 +# define SCLK_TT_EN(x) ((x) << 0) +# define SCLK_TT_EN_MASK (0xff << 0) +# define SCLK_TT_EN_SHIFT 0 +#define SMU_SCLK_DPM_TTT 0x1f10c +# define LT(x) ((x) << 0) +# define LT_MASK (0xffff << 0) +# define LT_SHIFT 0 +# define HT(x) ((x) << 16) +# define HT_MASK (0xffff << 16) +# define HT_SHIFT 16 + +#define SMU_S_PG_CNTL 0x1f118 +# define DS_PG_EN(x) ((x) << 16) +# define DS_PG_EN_MASK (0xff << 16) +# define DS_PG_EN_SHIFT 16 + +#define GFX_POWER_GATING_CNTL 0x1f38c +# define PDS_DIV(x) ((x) << 0) +# define PDS_DIV_MASK (0xff << 0) +# define PDS_DIV_SHIFT 0 +# define SSSD(x) ((x) << 8) +# define SSSD_MASK (0xff << 8) +# define SSSD_SHIFT 8 + +#define PM_CONFIG 0x1f428 +# define SVI_Mode (1 << 29) + +#define PM_I_CNTL_1 0x1f464 +# define SCLK_DPM(x) ((x) << 0) +# define SCLK_DPM_MASK (0xff << 0) +# define SCLK_DPM_SHIFT 0 +# define DS_PG_CNTL(x) ((x) << 16) +# define DS_PG_CNTL_MASK (0xff << 16) +# define DS_PG_CNTL_SHIFT 16 +#define PM_TP 0x1f468 + +#define NB_PSTATE_CONFIG 0x1f5f8 +# define Dpm0PgNbPsLo(x) ((x) << 0) +# define Dpm0PgNbPsLo_MASK (3 << 0) +# define Dpm0PgNbPsLo_SHIFT 0 +# define Dpm0PgNbPsHi(x) ((x) << 2) +# define Dpm0PgNbPsHi_MASK (3 << 2) +# define Dpm0PgNbPsHi_SHIFT 2 +# define DpmXNbPsLo(x) ((x) << 4) +# define DpmXNbPsLo_MASK (3 << 4) +# define DpmXNbPsLo_SHIFT 4 +# define DpmXNbPsHi(x) ((x) << 6) +# define DpmXNbPsHi_MASK (3 << 6) +# define DpmXNbPsHi_SHIFT 6 + +#define DC_CAC_VALUE 0x1f908 + +#define GPU_CAC_AVRG_CNTL 0x1f920 +# define WINDOW_SIZE(x) ((x) << 0) +# define WINDOW_SIZE_MASK (0xff << 0) +# define WINDOW_SIZE_SHIFT 0 + +#define CC_SMU_MISC_FUSES 0xe0001004 +# define MinSClkDid(x) ((x) << 2) +# define MinSClkDid_MASK (0x7f << 2) +# define MinSClkDid_SHIFT 2 + +#define CC_SMU_TST_EFUSE1_MISC 0xe000101c +# define RB_BACKEND_DISABLE(x) ((x) << 16) +# define RB_BACKEND_DISABLE_MASK (3 << 16) +# define RB_BACKEND_DISABLE_SHIFT 16 + +#define SMU_SCRATCH_A 0xe0003024 + +#define SMU_SCRATCH0 0xe0003040 + +/* mmio */ +#define SMC_INT_REQ 0x220 + +#define SMC_MESSAGE_0 0x22c +#define SMC_RESP_0 0x230 + +#define GENERAL_PWRMGT 0x670 +# define GLOBAL_PWRMGT_EN (1 << 0) + +#define SCLK_PWRMGT_CNTL 0x678 +# define DYN_PWR_DOWN_EN (1 << 2) +# define RESET_BUSY_CNT (1 << 4) +# define RESET_SCLK_CNT (1 << 5) +# define DYN_GFX_CLK_OFF_EN (1 << 7) +# define GFX_CLK_FORCE_ON (1 << 8) +# define DYNAMIC_PM_EN (1 << 21) + +#define TARGET_AND_CURRENT_PROFILE_INDEX 0x684 +# define TARGET_STATE(x) ((x) << 0) +# define TARGET_STATE_MASK (0xf << 0) +# define TARGET_STATE_SHIFT 0 +# define CURRENT_STATE(x) ((x) << 4) +# define CURRENT_STATE_MASK (0xf << 4) +# define CURRENT_STATE_SHIFT 4 + +#define CG_GIPOTS 0x6d8 +# define CG_GIPOT(x) ((x) << 16) +# define CG_GIPOT_MASK (0xffff << 16) +# define CG_GIPOT_SHIFT 16 + +#define CG_PG_CTRL 0x6e0 +# define SP(x) ((x) << 0) +# define SP_MASK (0xffff << 0) +# define SP_SHIFT 0 +# define SU(x) ((x) << 16) +# define SU_MASK (0xffff << 16) +# define SU_SHIFT 16 + +#define CG_THERMAL_INT_CTRL 0x738 +# define DIG_THERM_INTH(x) ((x) << 0) +# define DIG_THERM_INTH_MASK (0xff << 0) +# define DIG_THERM_INTH_SHIFT 0 +# define DIG_THERM_INTL(x) ((x) << 8) +# define DIG_THERM_INTL_MASK (0xff << 8) +# define DIG_THERM_INTL_SHIFT 8 +# define THERM_INTH_MASK (1 << 24) +# define THERM_INTL_MASK (1 << 25) + +#define CG_CG_VOLTAGE_CNTL 0x770 +# define EN (1 << 9) + +#define HW_REV 0x5564 +# define ATI_REV_ID_MASK (0xf << 28) +# define ATI_REV_ID_SHIFT 28 +/* 0 = A0, 1 = A1, 2 = B0, 3 = C0, etc. */ + +#define CGTS_SM_CTRL_REG 0x9150 + +#define GB_ADDR_CONFIG 0x98f8 + +#endif
From: Alex Deucher alexander.deucher@amd.com
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/sumo_dpm.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/sumo_dpm.c b/drivers/gpu/drm/radeon/sumo_dpm.c index 67c85c7..f103880 100644 --- a/drivers/gpu/drm/radeon/sumo_dpm.c +++ b/drivers/gpu/drm/radeon/sumo_dpm.c @@ -813,6 +813,12 @@ static void sumo_program_bootup_state(struct radeon_device *rdev)
void sumo_take_smu_control(struct radeon_device *rdev, bool enable) { +/* This bit selects who handles display phy powergating. + * Clear the bit to let atom handle it. + * Set it to let the driver handle it. + * For now we just let atom handle it. + */ +#if 0 u32 v = RREG32(DOUT_SCRATCH3);
if (enable) @@ -821,6 +827,7 @@ void sumo_take_smu_control(struct radeon_device *rdev, bool enable) v &= 0xFFFFFFFB;
WREG32(DOUT_SCRATCH3, v); +#endif }
static void sumo_enable_sclk_ds(struct radeon_device *rdev, bool enable)
From: Alex Deucher alexander.deucher@amd.com
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/rv770_dpm.c | 34 ++++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/rv770_dpm.h | 2 ++ 2 files changed, 36 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/rv770_dpm.c b/drivers/gpu/drm/radeon/rv770_dpm.c index 8ea6d69..42f559a 100644 --- a/drivers/gpu/drm/radeon/rv770_dpm.c +++ b/drivers/gpu/drm/radeon/rv770_dpm.c @@ -1427,6 +1427,38 @@ int rv770_set_boot_state(struct radeon_device *rdev) return 0; }
+void rv770_set_uvd_clock_before_set_eng_clock(struct radeon_device *rdev) +{ + struct rv7xx_ps *new_state = rv770_get_ps(rdev->pm.dpm.requested_ps); + struct rv7xx_ps *current_state = rv770_get_ps(rdev->pm.dpm.current_ps); + + if ((rdev->pm.dpm.requested_ps->vclk == rdev->pm.dpm.current_ps->vclk) && + (rdev->pm.dpm.requested_ps->dclk == rdev->pm.dpm.current_ps->dclk)) + return; + + if (new_state->high.sclk >= current_state->high.sclk) + return; + + radeon_set_uvd_clocks(rdev, rdev->pm.dpm.requested_ps->vclk, + rdev->pm.dpm.requested_ps->dclk); +} + +void rv770_set_uvd_clock_after_set_eng_clock(struct radeon_device *rdev) +{ + struct rv7xx_ps *new_state = rv770_get_ps(rdev->pm.dpm.requested_ps); + struct rv7xx_ps *current_state = rv770_get_ps(rdev->pm.dpm.current_ps); + + if ((rdev->pm.dpm.requested_ps->vclk == rdev->pm.dpm.current_ps->vclk) && + (rdev->pm.dpm.requested_ps->dclk == rdev->pm.dpm.current_ps->dclk)) + return; + + if (new_state->high.sclk < current_state->high.sclk) + return; + + radeon_set_uvd_clocks(rdev, rdev->pm.dpm.requested_ps->vclk, + rdev->pm.dpm.requested_ps->dclk); +} + int rv770_restrict_performance_levels_before_switch(struct radeon_device *rdev) { if (rv770_send_msg_to_smc(rdev, (PPSMC_Msg)(PPSMC_MSG_NoForcedLevel)) != PPSMC_Result_OK) @@ -1950,6 +1982,7 @@ int rv770_dpm_set_power_state(struct radeon_device *rdev) struct rv7xx_power_info *pi = rv770_get_pi(rdev);
rv770_restrict_performance_levels_before_switch(rdev); + rv770_set_uvd_clock_before_set_eng_clock(rdev); rv770_halt_smc(rdev); rv770_upload_sw_state(rdev); r7xx_program_memory_timing_parameters(rdev); @@ -1959,6 +1992,7 @@ int rv770_dpm_set_power_state(struct radeon_device *rdev) rv770_set_sw_state(rdev); if (pi->dcodt) rv770_program_dcodt_after_state_switch(rdev); + rv770_set_uvd_clock_after_set_eng_clock(rdev);
return 0; } diff --git a/drivers/gpu/drm/radeon/rv770_dpm.h b/drivers/gpu/drm/radeon/rv770_dpm.h index bd6ea7b..e42c064 100644 --- a/drivers/gpu/drm/radeon/rv770_dpm.h +++ b/drivers/gpu/drm/radeon/rv770_dpm.h @@ -262,6 +262,8 @@ int rv770_resume_smc(struct radeon_device *rdev); int rv770_set_sw_state(struct radeon_device *rdev); int rv770_set_boot_state(struct radeon_device *rdev); int rv7xx_parse_power_table(struct radeon_device *rdev); +void rv770_set_uvd_clock_before_set_eng_clock(struct radeon_device *rdev); +void rv770_set_uvd_clock_after_set_eng_clock(struct radeon_device *rdev);
/* smc */ int rv770_read_smc_soft_register(struct radeon_device *rdev,
From: Alex Deucher alexander.deucher@amd.com
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/btc_dpm.c | 62 ++++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/btc_dpm.h | 4 ++ drivers/gpu/drm/radeon/cypress_dpm.c | 10 +++++- drivers/gpu/drm/radeon/cypress_dpm.h | 10 +++++ drivers/gpu/drm/radeon/rv770_dpm.c | 30 +++++++++------- drivers/gpu/drm/radeon/rv770_dpm.h | 4 ++ drivers/gpu/drm/radeon/rv770_smc.h | 1 + 7 files changed, 107 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/drm/radeon/btc_dpm.c b/drivers/gpu/drm/radeon/btc_dpm.c index 6780120..2662ef0 100644 --- a/drivers/gpu/drm/radeon/btc_dpm.c +++ b/drivers/gpu/drm/radeon/btc_dpm.c @@ -1510,6 +1510,46 @@ static int btc_init_smc_table(struct radeon_device *rdev) pi->sram_end); }
+static void btc_set_at_for_uvd(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; + int idx = 0; + + if (r600_is_uvd_state(radeon_new_state->class, radeon_new_state->class2)) + idx = 1; + + if ((idx == 1) && !eg_pi->smu_uvd_hs) { + pi->rlp = 10; + pi->rmp = 100; + pi->lhp = 100; + pi->lmp = 10; + } else { + pi->rlp = eg_pi->ats[idx].rlp; + pi->rmp = eg_pi->ats[idx].rmp; + pi->lhp = eg_pi->ats[idx].lhp; + pi->lmp = eg_pi->ats[idx].lmp; + } + +} + +static void btc_notify_uvd_to_smc(struct radeon_device *rdev) +{ + struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + + if (r600_is_uvd_state(radeon_new_state->class, radeon_new_state->class2)) { + rv770_write_smc_soft_register(rdev, + RV770_SMC_SOFT_REGISTER_uvd_enabled, 1); + eg_pi->uvd_enabled = true; + } else { + rv770_write_smc_soft_register(rdev, + RV770_SMC_SOFT_REGISTER_uvd_enabled, 0); + eg_pi->uvd_enabled = false; + } +} + static int btc_reset_to_default(struct radeon_device *rdev) { if (rv770_send_msg_to_smc(rdev, PPSMC_MSG_ResetToDefaults) != PPSMC_Result_OK) @@ -1880,7 +1920,11 @@ int btc_dpm_set_power_state(struct radeon_device *rdev) if (eg_pi->pcie_performance_request) cypress_notify_link_speed_change_before_state_change(rdev);
+ rv770_set_uvd_clock_before_set_eng_clock(rdev); rv770_halt_smc(rdev); + btc_set_at_for_uvd(rdev); + if (eg_pi->smu_uvd_hs) + btc_notify_uvd_to_smc(rdev); cypress_upload_sw_state(rdev);
if (eg_pi->dynamic_ac_timing) @@ -1890,6 +1934,7 @@ int btc_dpm_set_power_state(struct radeon_device *rdev)
rv770_resume_smc(rdev); rv770_set_sw_state(rdev); + rv770_set_uvd_clock_after_set_eng_clock(rdev);
if (eg_pi->pcie_performance_request) cypress_notify_link_speed_change_after_state_change(rdev); @@ -2093,6 +2138,23 @@ int btc_dpm_init(struct radeon_device *rdev) pi->mclk_edc_enable_threshold = 40000; eg_pi->mclk_edc_wr_enable_threshold = 40000;
+ pi->rlp = RV770_RLP_DFLT; + pi->rmp = RV770_RMP_DFLT; + pi->lhp = RV770_LHP_DFLT; + pi->lmp = RV770_LMP_DFLT; + + eg_pi->ats[0].rlp = RV770_RLP_DFLT; + eg_pi->ats[0].rmp = RV770_RMP_DFLT; + eg_pi->ats[0].lhp = RV770_LHP_DFLT; + eg_pi->ats[0].lmp = RV770_LMP_DFLT; + + eg_pi->ats[1].rlp = BTC_RLP_UVD_DFLT; + eg_pi->ats[1].rmp = BTC_RMP_UVD_DFLT; + eg_pi->ats[1].lhp = BTC_LHP_UVD_DFLT; + eg_pi->ats[1].lmp = BTC_LMP_UVD_DFLT; + + eg_pi->smu_uvd_hs = true; + pi->voltage_control = radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC);
diff --git a/drivers/gpu/drm/radeon/btc_dpm.h b/drivers/gpu/drm/radeon/btc_dpm.h index a095d40..56b1957 100644 --- a/drivers/gpu/drm/radeon/btc_dpm.h +++ b/drivers/gpu/drm/radeon/btc_dpm.h @@ -23,6 +23,10 @@ #ifndef __BTC_DPM_H__ #define __BTC_DPM_H__
+#define BTC_RLP_UVD_DFLT 20 +#define BTC_RMP_UVD_DFLT 50 +#define BTC_LHP_UVD_DFLT 50 +#define BTC_LMP_UVD_DFLT 20 #define BARTS_MGCGCGTSSMCTRL_DFLT 0x81944000 #define TURKS_MGCGCGTSSMCTRL_DFLT 0x6e944000 #define CAICOS_MGCGCGTSSMCTRL_DFLT 0x46944040 diff --git a/drivers/gpu/drm/radeon/cypress_dpm.c b/drivers/gpu/drm/radeon/cypress_dpm.c index 2345c81..403ee15 100644 --- a/drivers/gpu/drm/radeon/cypress_dpm.c +++ b/drivers/gpu/drm/radeon/cypress_dpm.c @@ -690,7 +690,8 @@ int cypress_convert_power_level_to_smc(struct radeon_device *rdev,
level->mcFlags = 0; if (pi->mclk_stutter_mode_threshold && - (pl->mclk <= pi->mclk_stutter_mode_threshold)) { + (pl->mclk <= pi->mclk_stutter_mode_threshold) && + !eg_pi->uvd_enabled) { level->mcFlags |= SMC_MC_STUTTER_EN; if (eg_pi->sclk_deep_sleep) level->stateFlags |= PPSMC_STATEFLAG_AUTO_PULSE_SKIP; @@ -1938,6 +1939,7 @@ int cypress_dpm_set_power_state(struct radeon_device *rdev) if (eg_pi->pcie_performance_request) cypress_notify_link_speed_change_before_state_change(rdev);
+ rv770_set_uvd_clock_before_set_eng_clock(rdev); rv770_halt_smc(rdev); cypress_upload_sw_state(rdev);
@@ -1948,6 +1950,7 @@ int cypress_dpm_set_power_state(struct radeon_device *rdev)
rv770_resume_smc(rdev); rv770_set_sw_state(rdev); + rv770_set_uvd_clock_after_set_eng_clock(rdev);
if (eg_pi->pcie_performance_request) cypress_notify_link_speed_change_after_state_change(rdev); @@ -2010,6 +2013,11 @@ int cypress_dpm_init(struct radeon_device *rdev) pi->mclk_edc_enable_threshold = 40000; eg_pi->mclk_edc_wr_enable_threshold = 40000;
+ pi->rlp = RV770_RLP_DFLT; + pi->rmp = RV770_RMP_DFLT; + pi->lhp = RV770_LHP_DFLT; + pi->lmp = RV770_LMP_DFLT; + pi->voltage_control = radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC);
diff --git a/drivers/gpu/drm/radeon/cypress_dpm.h b/drivers/gpu/drm/radeon/cypress_dpm.h index 6cc3d3f..029bc9d 100644 --- a/drivers/gpu/drm/radeon/cypress_dpm.h +++ b/drivers/gpu/drm/radeon/cypress_dpm.h @@ -51,6 +51,13 @@ struct evergreen_arb_registers { u32 mc_arb_burst_time; };
+struct at { + u32 rlp; + u32 rmp; + u32 lhp; + u32 lmp; +}; + struct evergreen_power_info { /* must be first! */ struct rv7xx_power_info rv7xx; @@ -66,6 +73,8 @@ struct evergreen_power_info { bool sclk_deep_sleep; bool dll_default_on; bool ls_clock_gating; + bool smu_uvd_hs; + bool uvd_enabled; /* stored values */ u16 acpi_vddci; u8 mvdd_high_index; @@ -76,6 +85,7 @@ struct evergreen_power_info { struct atom_voltage_table vddci_voltage_table; struct evergreen_arb_registers bootup_arb_registers; struct evergreen_ulv_param ulv; + struct at ats[2]; /* smc offsets */ u16 mc_reg_table_start; }; diff --git a/drivers/gpu/drm/radeon/rv770_dpm.c b/drivers/gpu/drm/radeon/rv770_dpm.c index 42f559a..55e5126 100644 --- a/drivers/gpu/drm/radeon/rv770_dpm.c +++ b/drivers/gpu/drm/radeon/rv770_dpm.c @@ -265,22 +265,21 @@ int rv770_populate_smc_t(struct radeon_device *rdev, l[0] = 0; r[2] = 100;
- a_n = (int)state->medium.sclk * RV770_LMP_DFLT + - (int)state->low.sclk * (R600_AH_DFLT - RV770_RLP_DFLT); - a_d = (int)state->low.sclk * (100 - (int)RV770_RLP_DFLT) + - (int)state->medium.sclk * RV770_LMP_DFLT; + a_n = (int)state->medium.sclk * pi->lmp + + (int)state->low.sclk * (R600_AH_DFLT - pi->rlp); + a_d = (int)state->low.sclk * (100 - (int)pi->rlp) + + (int)state->medium.sclk * pi->lmp;
- l[1] = (u8)(RV770_LMP_DFLT - (int)RV770_LMP_DFLT * a_n / a_d); - r[0] = (u8)(RV770_RLP_DFLT + (100 - (int)RV770_RLP_DFLT) * a_n / a_d); + l[1] = (u8)(pi->lmp - (int)pi->lmp * a_n / a_d); + r[0] = (u8)(pi->rlp + (100 - (int)pi->rlp) * a_n / a_d);
- a_n = (int)state->high.sclk * RV770_LHP_DFLT + - (int)state->medium.sclk * - (R600_AH_DFLT - RV770_RMP_DFLT); - a_d = (int)state->medium.sclk * (100 - (int)RV770_RMP_DFLT) + - (int)state->high.sclk * RV770_LHP_DFLT; + a_n = (int)state->high.sclk * pi->lhp + (int)state->medium.sclk * + (R600_AH_DFLT - pi->rmp); + a_d = (int)state->medium.sclk * (100 - (int)pi->rmp) + + (int)state->high.sclk * pi->lhp;
- l[2] = (u8)(RV770_LHP_DFLT - (int)RV770_LHP_DFLT * a_n / a_d); - r[1] = (u8)(RV770_RMP_DFLT + (100 - (int)RV770_RMP_DFLT) * a_n / a_d); + l[2] = (u8)(pi->lhp - (int)pi->lhp * a_n / a_d); + r[1] = (u8)(pi->rmp + (100 - (int)pi->rmp) * a_n / a_d);
for (i = 0; i < (RV770_SMC_PERFORMANCE_LEVELS_PER_SWSTATE - 1); i++) { a_t = CG_R(r[i] * pi->bsp / 200) | CG_L(l[i] * pi->bsp / 200); @@ -2269,6 +2268,11 @@ int rv770_dpm_init(struct radeon_device *rdev) pi->mclk_strobe_mode_threshold = 30000; pi->mclk_edc_enable_threshold = 30000;
+ pi->rlp = RV770_RLP_DFLT; + pi->rmp = RV770_RMP_DFLT; + pi->lhp = RV770_LHP_DFLT; + pi->lmp = RV770_LMP_DFLT; + pi->voltage_control = radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC);
diff --git a/drivers/gpu/drm/radeon/rv770_dpm.h b/drivers/gpu/drm/radeon/rv770_dpm.h index e42c064..dfb133f 100644 --- a/drivers/gpu/drm/radeon/rv770_dpm.h +++ b/drivers/gpu/drm/radeon/rv770_dpm.h @@ -126,6 +126,10 @@ struct rv7xx_power_info { u32 pasi; u32 vrc; u32 restricted_levels; + u32 rlp; + u32 rmp; + u32 lhp; + u32 lmp; /* smc offsets */ u16 state_table_start; u16 soft_regs_start; diff --git a/drivers/gpu/drm/radeon/rv770_smc.h b/drivers/gpu/drm/radeon/rv770_smc.h index bdb652c..f78d92a 100644 --- a/drivers/gpu/drm/radeon/rv770_smc.h +++ b/drivers/gpu/drm/radeon/rv770_smc.h @@ -184,6 +184,7 @@ typedef struct RV770_SMC_STATETABLE RV770_SMC_STATETABLE; #define RV770_SMC_SOFT_REGISTER_mvdd_chg_time 0x68 #define RV770_SMC_SOFT_REGISTER_mclk_switch_lim 0x78 #define RV770_SMC_SOFT_REGISTER_mc_block_delay 0x90 +#define RV770_SMC_SOFT_REGISTER_uvd_enabled 0x9C #define RV770_SMC_SOFT_REGISTER_is_asic_lombok 0xA0
int rv770_set_smc_sram_address(struct radeon_device *rdev,
From: Alex Deucher alexander.deucher@amd.com
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/sumo_dpm.c | 55 +++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/sumod.h | 10 +++++++ 2 files changed, 65 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/sumo_dpm.c b/drivers/gpu/drm/radeon/sumo_dpm.c index f103880..cef25c4 100644 --- a/drivers/gpu/drm/radeon/sumo_dpm.c +++ b/drivers/gpu/drm/radeon/sumo_dpm.c @@ -811,6 +811,40 @@ static void sumo_program_bootup_state(struct radeon_device *rdev) sumo_power_level_enable(rdev, i, false); }
+static void sumo_set_uvd_clock_before_set_eng_clock(struct radeon_device *rdev) +{ + struct sumo_ps *new_ps = sumo_get_ps(rdev->pm.dpm.requested_ps); + struct sumo_ps *current_ps = sumo_get_ps(rdev->pm.dpm.current_ps); + + if ((rdev->pm.dpm.requested_ps->vclk == rdev->pm.dpm.current_ps->vclk) && + (rdev->pm.dpm.requested_ps->dclk == rdev->pm.dpm.current_ps->dclk)) + return; + + if (new_ps->levels[new_ps->num_levels - 1].sclk >= + current_ps->levels[current_ps->num_levels - 1].sclk) + return; + + radeon_set_uvd_clocks(rdev, rdev->pm.dpm.requested_ps->vclk, + rdev->pm.dpm.requested_ps->dclk); +} + +static void sumo_set_uvd_clock_after_set_eng_clock(struct radeon_device *rdev) +{ + struct sumo_ps *new_ps = sumo_get_ps(rdev->pm.dpm.requested_ps); + struct sumo_ps *current_ps = sumo_get_ps(rdev->pm.dpm.current_ps); + + if ((rdev->pm.dpm.requested_ps->vclk == rdev->pm.dpm.current_ps->vclk) && + (rdev->pm.dpm.requested_ps->dclk == rdev->pm.dpm.current_ps->dclk)) + return; + + if (new_ps->levels[new_ps->num_levels - 1].sclk < + current_ps->levels[current_ps->num_levels - 1].sclk) + return; + + radeon_set_uvd_clocks(rdev, rdev->pm.dpm.requested_ps->vclk, + rdev->pm.dpm.requested_ps->dclk); +} + void sumo_take_smu_control(struct radeon_device *rdev, bool enable) { /* This bit selects who handles display phy powergating. @@ -1096,6 +1130,22 @@ static void sumo_cleanup_asic(struct radeon_device *rdev) sumo_take_smu_control(rdev, false); }
+static void sumo_uvd_init(struct radeon_device *rdev) +{ + u32 tmp; + + tmp = RREG32(CG_VCLK_CNTL); + tmp &= ~VCLK_DIR_CNTL_EN; + WREG32(CG_VCLK_CNTL, tmp); + + tmp = RREG32(CG_DCLK_CNTL); + tmp &= ~DCLK_DIR_CNTL_EN; + WREG32(CG_DCLK_CNTL, tmp); + + /* 100 Mhz */ + radeon_set_uvd_clocks(rdev, 10000, 10000); +} + static int sumo_set_thermal_temperature_range(struct radeon_device *rdev, int min_temp, int max_temp) { @@ -1188,6 +1238,8 @@ int sumo_dpm_set_power_state(struct radeon_device *rdev)
if (pi->enable_dynamic_patch_ps) sumo_apply_state_adjust_rules(rdev); + if (pi->enable_dpm) + sumo_set_uvd_clock_before_set_eng_clock(rdev); sumo_update_current_power_levels(rdev); if (pi->enable_boost) { sumo_enable_boost(rdev, false); @@ -1211,6 +1263,8 @@ int sumo_dpm_set_power_state(struct radeon_device *rdev) } if (pi->enable_boost) sumo_enable_boost(rdev, true); + if (pi->enable_dpm) + sumo_set_uvd_clock_after_set_eng_clock(rdev);
return 0; } @@ -1237,6 +1291,7 @@ void sumo_dpm_setup_asic(struct radeon_device *rdev) sumo_program_acpi_power_level(rdev); sumo_enable_acpi_pm(rdev); sumo_take_smu_control(rdev, true); + sumo_uvd_init(rdev); }
void sumo_dpm_display_configuration_changed(struct radeon_device *rdev) diff --git a/drivers/gpu/drm/radeon/sumod.h b/drivers/gpu/drm/radeon/sumod.h index a5deba6..7c9c2d4 100644 --- a/drivers/gpu/drm/radeon/sumod.h +++ b/drivers/gpu/drm/radeon/sumod.h @@ -136,6 +136,16 @@ #define CG_SCLK_STATUS 0x604 # define SCLK_OVERCLK_DETECT (1 << 2)
+#define CG_DCLK_CNTL 0x610 +# define DCLK_DIVIDER_MASK 0x7f +# define DCLK_DIR_CNTL_EN (1 << 8) +#define CG_DCLK_STATUS 0x614 +# define DCLK_STATUS (1 << 0) +#define CG_VCLK_CNTL 0x618 +# define VCLK_DIVIDER_MASK 0x7f +# define VCLK_DIR_CNTL_EN (1 << 8) +#define CG_VCLK_STATUS 0x61c + #define GENERAL_PWRMGT 0x63c # define STATIC_PM_EN (1 << 1)
From: Alex Deucher alexander.deucher@amd.com
v2: fix typo noticed by Dan Carpenter
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/ppsmc.h | 1 + drivers/gpu/drm/radeon/trinity_dpm.c | 220 ++++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/trinity_dpm.h | 18 +++ drivers/gpu/drm/radeon/trinity_smc.c | 5 + drivers/gpu/drm/radeon/trinityd.h | 5 + 5 files changed, 249 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/ppsmc.h b/drivers/gpu/drm/radeon/ppsmc.h index 8ef479a..3d0786f 100644 --- a/drivers/gpu/drm/radeon/ppsmc.h +++ b/drivers/gpu/drm/radeon/ppsmc.h @@ -75,6 +75,7 @@ typedef uint8_t PPSMC_Result; #define PPSMC_MSG_PG_SIMD_Config ((uint32_t) 0x108) #define PPSMC_MSG_DCE_RemoveVoltageAdjustment ((uint32_t) 0x11d) #define PPSMC_MSG_DCE_AllowVoltageAdjustment ((uint32_t) 0x11e) +#define PPSMC_MSG_UVD_DPM_Config ((uint32_t) 0x124)
typedef uint16_t PPSMC_Msg; diff --git a/drivers/gpu/drm/radeon/trinity_dpm.c b/drivers/gpu/drm/radeon/trinity_dpm.c index c4779a6..1b3822f 100644 --- a/drivers/gpu/drm/radeon/trinity_dpm.c +++ b/drivers/gpu/drm/radeon/trinity_dpm.c @@ -866,6 +866,117 @@ static void trinity_program_bootup_state(struct radeon_device *rdev) trinity_power_level_enable_disable(rdev, i, false); }
+static void trinity_setup_uvd_clock_table(struct radeon_device *rdev, + struct radeon_ps *rps) +{ + struct trinity_ps *ps = trinity_get_ps(rps); + u32 uvdstates = (ps->vclk_low_divider | + ps->vclk_high_divider << 8 | + ps->dclk_low_divider << 16 | + ps->dclk_high_divider << 24); + + WREG32_SMC(SMU_UVD_DPM_STATES, uvdstates); +} + +static void trinity_setup_uvd_dpm_interval(struct radeon_device *rdev, + u32 interval) +{ + u32 p, u; + u32 tp = RREG32_SMC(PM_TP); + u32 val; + u32 xclk = sumo_get_xclk(rdev); + + r600_calculate_u_and_p(interval, xclk, 16, &p, &u); + + val = (p + tp - 1) / tp; + + WREG32_SMC(SMU_UVD_DPM_CNTL, val); +} + +static bool trinity_uvd_clocks_zero(struct radeon_ps *rps) +{ + if ((rps->vclk == 0) && (rps->dclk == 0)) + return true; + else + return false; +} + +static bool trinity_uvd_clocks_equal(struct radeon_ps *rps1, + struct radeon_ps *rps2) +{ + struct trinity_ps *ps1 = trinity_get_ps(rps1); + struct trinity_ps *ps2 = trinity_get_ps(rps2); + + if ((rps1->vclk == rps2->vclk) && + (rps1->dclk == rps2->dclk) && + (ps1->vclk_low_divider == ps2->vclk_low_divider) && + (ps1->vclk_high_divider == ps2->vclk_high_divider) && + (ps1->dclk_low_divider == ps2->dclk_low_divider) && + (ps1->dclk_high_divider == ps2->dclk_high_divider)) + return true; + else + return false; +} + +static void trinity_setup_uvd_clocks(struct radeon_device *rdev, + struct radeon_ps *current_rps, + struct radeon_ps *new_rps) +{ + struct trinity_power_info *pi = trinity_get_pi(rdev); + + if (pi->uvd_dpm) { + if (trinity_uvd_clocks_zero(new_rps) && + !trinity_uvd_clocks_zero(current_rps)) { + trinity_setup_uvd_dpm_interval(rdev, 0); + } else if (!trinity_uvd_clocks_zero(new_rps)) { + trinity_setup_uvd_clock_table(rdev, new_rps); + + if (trinity_uvd_clocks_zero(current_rps)) { + u32 tmp = RREG32(CG_MISC_REG); + tmp &= 0xfffffffd; + WREG32(CG_MISC_REG, tmp); + + radeon_set_uvd_clocks(rdev, new_rps->vclk, new_rps->dclk); + + trinity_setup_uvd_dpm_interval(rdev, 3000); + } + } + trinity_uvd_dpm_config(rdev); + } else { + if (trinity_uvd_clocks_zero(new_rps) || + trinity_uvd_clocks_equal(new_rps, current_rps)) + return; + + radeon_set_uvd_clocks(rdev, new_rps->vclk, new_rps->dclk); + } +} + +static void trinity_set_uvd_clock_before_set_eng_clock(struct radeon_device *rdev) +{ + struct trinity_ps *new_ps = trinity_get_ps(rdev->pm.dpm.requested_ps); + struct trinity_ps *current_ps = trinity_get_ps(rdev->pm.dpm.current_ps); + + if (new_ps->levels[new_ps->num_levels - 1].sclk >= + current_ps->levels[current_ps->num_levels - 1].sclk) + return; + + trinity_setup_uvd_clocks(rdev, rdev->pm.dpm.current_ps, + rdev->pm.dpm.requested_ps); +} + +static void trinity_set_uvd_clock_after_set_eng_clock(struct radeon_device *rdev) +{ + struct trinity_ps *new_ps = trinity_get_ps(rdev->pm.dpm.requested_ps); + struct trinity_ps *current_ps = trinity_get_ps(rdev->pm.dpm.current_ps); + + if (new_ps->levels[new_ps->num_levels - 1].sclk < + current_ps->levels[current_ps->num_levels - 1].sclk) + return; + + trinity_setup_uvd_clocks(rdev, rdev->pm.dpm.current_ps, + rdev->pm.dpm.requested_ps); +} + static void trinity_program_ttt(struct radeon_device *rdev) { struct trinity_power_info *pi = trinity_get_pi(rdev); @@ -1017,6 +1128,7 @@ int trinity_dpm_set_power_state(struct radeon_device *rdev)
trinity_acquire_mutex(rdev); if (pi->enable_dpm) { + trinity_set_uvd_clock_before_set_eng_clock(rdev); trinity_enable_power_level_0(rdev); trinity_force_level_0(rdev); trinity_wait_for_level_0(rdev); @@ -1024,6 +1136,7 @@ int trinity_dpm_set_power_state(struct radeon_device *rdev) trinity_program_power_levels_0_to_n(rdev); trinity_force_level_0(rdev); trinity_unforce_levels(rdev); + trinity_set_uvd_clock_after_set_eng_clock(rdev); } trinity_release_mutex(rdev);
@@ -1198,6 +1311,61 @@ static u8 trinity_calculate_display_wm(struct radeon_device *rdev, } }
+static u32 trinity_get_uvd_clock_index(struct radeon_device *rdev, + struct radeon_ps *rps) +{ + struct trinity_power_info *pi = trinity_get_pi(rdev); + u32 i = 0; + + for (i = 0; i < 4; i++) { + if ((rps->vclk == pi->sys_info.uvd_clock_table_entries[i].vclk) && + (rps->dclk == pi->sys_info.uvd_clock_table_entries[i].dclk)) + break; + } + + if (i >= 4) { + DRM_ERROR("UVD clock index not found!\n"); + i = 3; + } + return i; +} + +static void trinity_adjust_uvd_state(struct radeon_device *rdev, + struct radeon_ps *rps) +{ + struct trinity_ps *ps = trinity_get_ps(rps); + struct trinity_power_info *pi = trinity_get_pi(rdev); + u32 high_index = 0; + u32 low_index = 0; + + if (pi->uvd_dpm && r600_is_uvd_state(rps->class, rps->class2)) { + high_index = trinity_get_uvd_clock_index(rdev, rps); + + switch(high_index) { + case 3: + case 2: + low_index = 1; + break; + case 1: + case 0: + default: + low_index = 0; + break; + } + + ps->vclk_low_divider = + pi->sys_info.uvd_clock_table_entries[high_index].vclk_did; + ps->dclk_low_divider = + pi->sys_info.uvd_clock_table_entries[high_index].dclk_did; + ps->vclk_high_divider = + pi->sys_info.uvd_clock_table_entries[low_index].vclk_did; + ps->dclk_high_divider = + pi->sys_info.uvd_clock_table_entries[low_index].dclk_did; + } +} + + + static void trinity_apply_state_adjust_rules(struct radeon_device *rdev) { struct radeon_ps *rps = rdev->pm.dpm.requested_ps; @@ -1214,6 +1382,8 @@ static void trinity_apply_state_adjust_rules(struct radeon_device *rdev) if (rps->class & ATOM_PPLIB_CLASSIFICATION_THERMAL) return trinity_patch_thermal_state(rdev, ps, current_ps);
+ trinity_adjust_uvd_state(rdev, rps); + for (i = 0; i < ps->num_levels; i++) { if (ps->levels[i].vddc_index < min_voltage) ps->levels[i].vddc_index = min_voltage; @@ -1454,6 +1624,25 @@ union igp_info { struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_7 info_7; };
+static u32 trinity_convert_did_to_freq(struct radeon_device *rdev, u8 did) +{ + struct trinity_power_info *pi = trinity_get_pi(rdev); + u32 divider; + + if (did >= 8 && did <= 0x3f) + divider = did * 25; + else if (did > 0x3f && did <= 0x5f) + divider = (did - 64) * 50 + 1600; + else if (did > 0x5f && did <= 0x7e) + divider = (did - 96) * 100 + 3200; + else if (did == 0x7f) + divider = 128 * 100; + else + return 10000; + + return ((pi->sys_info.dentist_vco_freq * 100) + (divider - 1)) / divider; +} + static int trinity_parse_sys_info_table(struct radeon_device *rdev) { struct trinity_power_info *pi = trinity_get_pi(rdev); @@ -1476,6 +1665,7 @@ static int trinity_parse_sys_info_table(struct radeon_device *rdev) pi->sys_info.bootup_sclk = le32_to_cpu(igp_info->info_7.ulBootUpEngineClock); pi->sys_info.min_sclk = le32_to_cpu(igp_info->info_7.ulMinEngineClock); pi->sys_info.bootup_uma_clk = le32_to_cpu(igp_info->info_7.ulBootUpUMAClock); + pi->sys_info.dentist_vco_freq = le32_to_cpu(igp_info->info_7.ulDentistVCOFreq); pi->sys_info.bootup_nb_voltage_index = le16_to_cpu(igp_info->info_7.usBootUpNBVoltage); if (igp_info->info_7.ucHtcTmpLmt == 0) @@ -1521,6 +1711,35 @@ static int trinity_parse_sys_info_table(struct radeon_device *rdev) sumo_construct_vid_mapping_table(rdev, &pi->sys_info.vid_mapping_table, igp_info->info_7.sAvail_SCLK);
+ pi->sys_info.uvd_clock_table_entries[0].vclk_did = + igp_info->info_7.ucDPMState0VclkFid; + pi->sys_info.uvd_clock_table_entries[1].vclk_did = + igp_info->info_7.ucDPMState1VclkFid; + pi->sys_info.uvd_clock_table_entries[2].vclk_did = + igp_info->info_7.ucDPMState2VclkFid; + pi->sys_info.uvd_clock_table_entries[3].vclk_did = + igp_info->info_7.ucDPMState3VclkFid; + + pi->sys_info.uvd_clock_table_entries[0].dclk_did = + igp_info->info_7.ucDPMState0DclkFid; + pi->sys_info.uvd_clock_table_entries[1].dclk_did = + igp_info->info_7.ucDPMState1DclkFid; + pi->sys_info.uvd_clock_table_entries[2].dclk_did = + igp_info->info_7.ucDPMState2DclkFid; + pi->sys_info.uvd_clock_table_entries[3].dclk_did = + igp_info->info_7.ucDPMState3DclkFid; + + for (i = 0; i < 4; i++) { + pi->sys_info.uvd_clock_table_entries[i].vclk = + trinity_convert_did_to_freq(rdev, + pi->sys_info.uvd_clock_table_entries[i].vclk_did); + pi->sys_info.uvd_clock_table_entries[i].dclk = + trinity_convert_did_to_freq(rdev, + pi->sys_info.uvd_clock_table_entries[i].dclk_did); + } + + + } return 0; } @@ -1547,6 +1766,7 @@ int trinity_dpm_init(struct radeon_device *rdev) pi->override_dynamic_mgpg = true; pi->enable_auto_thermal_throttling = true; pi->voltage_drop_in_dce = false; /* need to restructure dpm/modeset interaction */ + pi->uvd_dpm = true; /* ??? */
ret = trinity_parse_sys_info_table(rdev); if (ret) diff --git a/drivers/gpu/drm/radeon/trinity_dpm.h b/drivers/gpu/drm/radeon/trinity_dpm.h index 15e050f..31100ac 100644 --- a/drivers/gpu/drm/radeon/trinity_dpm.h +++ b/drivers/gpu/drm/radeon/trinity_dpm.h @@ -55,14 +55,29 @@ struct trinity_ps { u8 Dpm0PgNbPsHi; u8 DpmXNbPsLo; u8 DpmXNbPsHi; + + u32 vclk_low_divider; + u32 vclk_high_divider; + u32 dclk_low_divider; + u32 dclk_high_divider; };
#define TRINITY_NUM_NBPSTATES 4
+struct trinity_uvd_clock_table_entry +{ + u32 vclk; + u32 dclk; + u8 vclk_did; + u8 dclk_did; + u8 rsv[2]; +}; + struct trinity_sys_info { u32 bootup_uma_clk; u32 bootup_sclk; u32 min_sclk; + u32 dentist_vco_freq; u32 nb_dpm_enable; u32 nbp_mclk[TRINITY_NUM_NBPSTATES]; u32 nbp_nclk[TRINITY_NUM_NBPSTATES]; @@ -73,6 +88,7 @@ struct trinity_sys_info { struct sumo_sclk_voltage_mapping_table sclk_voltage_mapping_table; struct sumo_vid_mapping_table vid_mapping_table; u32 uma_channel_number; + struct trinity_uvd_clock_table_entry uvd_clock_table_entries[4]; };
struct trinity_power_info { @@ -93,12 +109,14 @@ struct trinity_power_info { bool enable_auto_thermal_throttling; bool enable_dpm; bool enable_sclk_ds; + bool uvd_dpm; };
#define TRINITY_AT_DFLT 30
/* trinity_smc.c */ int trinity_dpm_config(struct radeon_device *rdev, bool enable); +int trinity_uvd_dpm_config(struct radeon_device *rdev); int trinity_dpm_force_state(struct radeon_device *rdev, u32 n); int trinity_dpm_no_forced_level(struct radeon_device *rdev); int trinity_dce_enable_voltage_adjustment(struct radeon_device *rdev, diff --git a/drivers/gpu/drm/radeon/trinity_smc.c b/drivers/gpu/drm/radeon/trinity_smc.c index 60ffc1e..85f86a2 100644 --- a/drivers/gpu/drm/radeon/trinity_smc.c +++ b/drivers/gpu/drm/radeon/trinity_smc.c @@ -73,6 +73,11 @@ int trinity_dpm_force_state(struct radeon_device *rdev, u32 n) return trinity_notify_message_to_smu(rdev, PPSMC_MSG_DPM_ForceState); }
+int trinity_uvd_dpm_config(struct radeon_device *rdev) +{ + return trinity_notify_message_to_smu(rdev, PPSMC_MSG_UVD_DPM_Config); +} + int trinity_dpm_no_forced_level(struct radeon_device *rdev) { return trinity_notify_message_to_smu(rdev, PPSMC_MSG_NoForcedLevel); diff --git a/drivers/gpu/drm/radeon/trinityd.h b/drivers/gpu/drm/radeon/trinityd.h index b234d36..fd32e27 100644 --- a/drivers/gpu/drm/radeon/trinityd.h +++ b/drivers/gpu/drm/radeon/trinityd.h @@ -100,6 +100,9 @@ # define HT_MASK (0xffff << 16) # define HT_SHIFT 16
+#define SMU_UVD_DPM_STATES 0x1f1a0 +#define SMU_UVD_DPM_CNTL 0x1f1a4 + #define SMU_S_PG_CNTL 0x1f118 # define DS_PG_EN(x) ((x) << 16) # define DS_PG_EN_MASK (0xff << 16) @@ -198,6 +201,8 @@ # define SU_MASK (0xffff << 16) # define SU_SHIFT 16
+#define CG_MISC_REG 0x708 + #define CG_THERMAL_INT_CTRL 0x738 # define DIG_THERM_INTH(x) ((x) << 0) # define DIG_THERM_INTH_MASK (0xff << 0)
From: Alex Deucher alexander.deucher@amd.com
When using UVD, the driver must switch to a special UVD power state. In the CS ioctl, switch to the power state and schedule work to change the power state back, when the work comes up, check if uvd is still busy and if not, switch back to the user state, otherwise, reschedule the work.
Note: We really need some better way to decide when to switch out of the uvd power state. Switching power states while playback is active make uvd angry.
V2: fix locking.
V3: switch from timer to delayed work
V4: check fence driver for UVD jobs, reduce timeout to 1 second and rearm timeout on activity
v5: rebase on new dpm tree
v6: rebase on interim uvd on demand changes
v7: fix UVD when DPM is disabled
v8: unify non-DPM and DPM UVD handling
v9: remove leftover idle work struct
Signed-off-by: Alex Deucher alexander.deucher@amd.com Signed-off-by: Christian König deathsimple@vodafone.de --- drivers/gpu/drm/radeon/radeon.h | 1 + drivers/gpu/drm/radeon/radeon_cs.c | 1 + drivers/gpu/drm/radeon/radeon_pm.c | 12 +++++++++++- drivers/gpu/drm/radeon/radeon_uvd.c | 24 +++++++++++++++++++----- 4 files changed, 32 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 037773d..3b345cc 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1246,6 +1246,7 @@ struct radeon_dpm { int current_active_crtc_count; /* special states active */ bool thermal_active; + bool uvd_active; /* thermal handling */ struct radeon_dpm_thermal thermal; }; diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c index 7e265a5..4f6b22b 100644 --- a/drivers/gpu/drm/radeon/radeon_cs.c +++ b/drivers/gpu/drm/radeon/radeon_cs.c @@ -550,6 +550,7 @@ int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) return r; }
+ /* XXX pick SD/HD/MVC */ if (parser.ring == R600_RING_TYPE_UVD_INDEX) radeon_uvd_note_usage(rdev);
diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index e6fce0b..5987580 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -699,7 +699,8 @@ static void radeon_dpm_change_power_state_locked(struct radeon_device *rdev)
if (rdev->pm.dpm.user_state != rdev->pm.dpm.state) { /* add other state override checks here */ - if (!rdev->pm.dpm.thermal_active) + if ((!rdev->pm.dpm.thermal_active) && + (!rdev->pm.dpm.uvd_active)) rdev->pm.dpm.state = rdev->pm.dpm.user_state; } dpm_state = rdev->pm.dpm.state; @@ -769,8 +770,16 @@ void radeon_dpm_enable_power_state(struct radeon_device *rdev, case POWER_STATE_TYPE_INTERNAL_THERMAL: rdev->pm.dpm.thermal_active = true; break; + case POWER_STATE_TYPE_INTERNAL_UVD: + case POWER_STATE_TYPE_INTERNAL_UVD_SD: + case POWER_STATE_TYPE_INTERNAL_UVD_HD: + case POWER_STATE_TYPE_INTERNAL_UVD_HD2: + case POWER_STATE_TYPE_INTERNAL_UVD_MVC: + rdev->pm.dpm.uvd_active = true; + break; default: rdev->pm.dpm.thermal_active = false; + rdev->pm.dpm.uvd_active = false; break; } rdev->pm.dpm.state = dpm_state; @@ -1223,6 +1232,7 @@ static void radeon_pm_compute_clocks_dpm(struct radeon_device *rdev) radeon_dpm_change_power_state_locked(rdev);
mutex_unlock(&rdev->pm.mutex); + }
void radeon_pm_compute_clocks(struct radeon_device *rdev) diff --git a/drivers/gpu/drm/radeon/radeon_uvd.c b/drivers/gpu/drm/radeon/radeon_uvd.c index fdc77d1..ce5a10c 100644 --- a/drivers/gpu/drm/radeon/radeon_uvd.c +++ b/drivers/gpu/drm/radeon/radeon_uvd.c @@ -699,11 +699,19 @@ static void radeon_uvd_idle_work_handler(struct work_struct *work) struct radeon_device *rdev = container_of(work, struct radeon_device, uvd.idle_work.work);
- if (radeon_fence_count_emitted(rdev, R600_RING_TYPE_UVD_INDEX) == 0) - radeon_set_uvd_clocks(rdev, 0, 0); - else + if (radeon_fence_count_emitted(rdev, R600_RING_TYPE_UVD_INDEX) == 0) { + if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) { + mutex_lock(&rdev->pm.mutex); + rdev->pm.dpm.uvd_active = false; + mutex_unlock(&rdev->pm.mutex); + radeon_pm_compute_clocks(rdev); + } else { + radeon_set_uvd_clocks(rdev, 0, 0); + } + } else { schedule_delayed_work(&rdev->uvd.idle_work, msecs_to_jiffies(UVD_IDLE_TIMEOUT_MS)); + } }
void radeon_uvd_note_usage(struct radeon_device *rdev) @@ -711,8 +719,14 @@ void radeon_uvd_note_usage(struct radeon_device *rdev) bool set_clocks = !cancel_delayed_work_sync(&rdev->uvd.idle_work); set_clocks &= schedule_delayed_work(&rdev->uvd.idle_work, msecs_to_jiffies(UVD_IDLE_TIMEOUT_MS)); - if (set_clocks) - radeon_set_uvd_clocks(rdev, 53300, 40000); + if (set_clocks) { + if ((rdev->pm.pm_method == PM_METHOD_DPM) && rdev->pm.dpm_enabled) { + /* XXX pick SD/HD/MVC */ + radeon_dpm_enable_power_state(rdev, POWER_STATE_TYPE_INTERNAL_UVD); + } else { + radeon_set_uvd_clocks(rdev, 53300, 40000); + } + } }
static unsigned radeon_uvd_calc_upll_post_div(unsigned vco_freq,
From: Alex Deucher alexander.deucher@amd.com
This data will be needed for dpm on newer asics.
v2: fix typo in rebase
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/r600_dpm.c | 179 +++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/r600_dpm.h | 3 + drivers/gpu/drm/radeon/radeon.h | 70 ++++++++++++++ 3 files changed, 252 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/r600_dpm.c b/drivers/gpu/drm/radeon/r600_dpm.c index bf396a0..c9f9647 100644 --- a/drivers/gpu/drm/radeon/r600_dpm.c +++ b/drivers/gpu/drm/radeon/r600_dpm.c @@ -721,3 +721,182 @@ bool r600_is_internal_thermal_sensor(enum radeon_int_thermal_type sensor) return false; } } + +union power_info { + struct _ATOM_POWERPLAY_INFO info; + struct _ATOM_POWERPLAY_INFO_V2 info_2; + struct _ATOM_POWERPLAY_INFO_V3 info_3; + struct _ATOM_PPLIB_POWERPLAYTABLE pplib; + struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2; + struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3; + struct _ATOM_PPLIB_POWERPLAYTABLE4 pplib4; + struct _ATOM_PPLIB_POWERPLAYTABLE5 pplib5; +}; + +union fan_info { + struct _ATOM_PPLIB_FANTABLE fan; + struct _ATOM_PPLIB_FANTABLE2 fan2; +}; + +static int r600_parse_clk_voltage_dep_table(struct radeon_clock_voltage_dependency_table *radeon_table, + ATOM_PPLIB_Clock_Voltage_Dependency_Table *atom_table) +{ + u32 size = atom_table->ucNumEntries * + sizeof(struct radeon_clock_voltage_dependency_entry); + int i; + + radeon_table->entries = kzalloc(size, GFP_KERNEL); + if (!radeon_table->entries) + return -ENOMEM; + + for (i = 0; i < atom_table->ucNumEntries; i++) { + radeon_table->entries[i].clk = le16_to_cpu(atom_table->entries[i].usClockLow) | + (atom_table->entries[i].ucClockHigh << 16); + radeon_table->entries[i].v = le16_to_cpu(atom_table->entries[i].usVoltage); + } + radeon_table->count = atom_table->ucNumEntries; + + return 0; +} + +int r600_parse_extended_power_table(struct radeon_device *rdev) +{ + struct radeon_mode_info *mode_info = &rdev->mode_info; + union power_info *power_info; + union fan_info *fan_info; + ATOM_PPLIB_Clock_Voltage_Dependency_Table *dep_table; + int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo); + u16 data_offset; + u8 frev, crev; + int ret, i; + + if (!atom_parse_data_header(mode_info->atom_context, index, NULL, + &frev, &crev, &data_offset)) + return -EINVAL; + power_info = (union power_info *)(mode_info->atom_context->bios + data_offset); + + /* fan table */ + if (power_info->pplib.usTableSize >= sizeof(struct _ATOM_PPLIB_POWERPLAYTABLE3)) { + if (power_info->pplib3.usFanTableOffset) { + fan_info = (union fan_info *)(mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib3.usFanTableOffset)); + rdev->pm.dpm.fan.t_hyst = fan_info->fan.ucTHyst; + rdev->pm.dpm.fan.t_min = le16_to_cpu(fan_info->fan.usTMin); + rdev->pm.dpm.fan.t_med = le16_to_cpu(fan_info->fan.usTMed); + rdev->pm.dpm.fan.t_high = le16_to_cpu(fan_info->fan.usTHigh); + rdev->pm.dpm.fan.pwm_min = le16_to_cpu(fan_info->fan.usPWMMin); + rdev->pm.dpm.fan.pwm_med = le16_to_cpu(fan_info->fan.usPWMMed); + rdev->pm.dpm.fan.pwm_high = le16_to_cpu(fan_info->fan.usPWMHigh); + if (fan_info->fan.ucFanTableFormat >= 2) + rdev->pm.dpm.fan.t_max = le16_to_cpu(fan_info->fan2.usTMax); + else + rdev->pm.dpm.fan.t_max = 10900; + rdev->pm.dpm.fan.cycle_delay = 100000; + rdev->pm.dpm.fan.ucode_fan_control = true; + } + } + + /* clock dependancy tables */ + if (power_info->pplib.usTableSize >= sizeof(struct _ATOM_PPLIB_POWERPLAYTABLE4)) { + if (power_info->pplib4.usVddcDependencyOnSCLKOffset) { + dep_table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *) + (mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib4.usVddcDependencyOnSCLKOffset)); + ret = r600_parse_clk_voltage_dep_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk, + dep_table); + if (ret) + return ret; + } + if (power_info->pplib4.usVddciDependencyOnMCLKOffset) { + dep_table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *) + (mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib4.usVddciDependencyOnMCLKOffset)); + ret = r600_parse_clk_voltage_dep_table(&rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk, + dep_table); + if (ret) { + kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries); + return ret; + } + } + if (power_info->pplib4.usVddcDependencyOnMCLKOffset) { + dep_table = (ATOM_PPLIB_Clock_Voltage_Dependency_Table *) + (mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib4.usVddcDependencyOnMCLKOffset)); + ret = r600_parse_clk_voltage_dep_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk, + dep_table); + if (ret) { + kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries); + kfree(rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk.entries); + return ret; + } + } + if (power_info->pplib4.usMaxClockVoltageOnDCOffset) { + ATOM_PPLIB_Clock_Voltage_Limit_Table *clk_v = + (ATOM_PPLIB_Clock_Voltage_Limit_Table *) + (mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib4.usMaxClockVoltageOnDCOffset)); + if (clk_v->ucNumEntries) { + rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc.sclk = + le16_to_cpu(clk_v->entries[0].usSclkLow) | + (clk_v->entries[0].ucSclkHigh << 16); + rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc.mclk = + le16_to_cpu(clk_v->entries[0].usMclkLow) | + (clk_v->entries[0].ucMclkHigh << 16); + rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc.vddc = + le16_to_cpu(clk_v->entries[0].usVddc); + rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc.vddci = + le16_to_cpu(clk_v->entries[0].usVddci); + } + } + } + + /* cac data */ + if (power_info->pplib.usTableSize >= sizeof(struct _ATOM_PPLIB_POWERPLAYTABLE5)) { + rdev->pm.dpm.tdp_limit = le32_to_cpu(power_info->pplib5.ulTDPLimit); + rdev->pm.dpm.near_tdp_limit = le32_to_cpu(power_info->pplib5.ulNearTDPLimit); + rdev->pm.dpm.tdp_od_limit = le16_to_cpu(power_info->pplib5.usTDPODLimit); + if (rdev->pm.dpm.tdp_od_limit) + rdev->pm.dpm.power_control = true; + else + rdev->pm.dpm.power_control = false; + rdev->pm.dpm.tdp_adjustment = 0; + rdev->pm.dpm.sq_ramping_threshold = le32_to_cpu(power_info->pplib5.ulSQRampingThreshold); + rdev->pm.dpm.cac_leakage = le32_to_cpu(power_info->pplib5.ulCACLeakage); + rdev->pm.dpm.load_line_slope = le16_to_cpu(power_info->pplib5.usLoadLineSlope); + if (power_info->pplib5.usCACLeakageTableOffset) { + ATOM_PPLIB_CAC_Leakage_Table *cac_table = + (ATOM_PPLIB_CAC_Leakage_Table *) + (mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib5.usCACLeakageTableOffset)); + u32 size = cac_table->ucNumEntries * sizeof(struct radeon_cac_leakage_table); + rdev->pm.dpm.dyn_state.cac_leakage_table.entries = kzalloc(size, GFP_KERNEL); + if (!rdev->pm.dpm.dyn_state.cac_leakage_table.entries) { + kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries); + kfree(rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk.entries); + kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk.entries); + return -ENOMEM; + } + for (i = 0; i < cac_table->ucNumEntries; i++) { + rdev->pm.dpm.dyn_state.cac_leakage_table.entries[i].vddc = + le16_to_cpu(cac_table->entries[i].usVddc); + rdev->pm.dpm.dyn_state.cac_leakage_table.entries[i].leakage = + le32_to_cpu(cac_table->entries[i].ulLeakageValue); + } + rdev->pm.dpm.dyn_state.cac_leakage_table.count = cac_table->ucNumEntries; + } + } + + return 0; +} + +void r600_free_extended_power_table(struct radeon_device *rdev) +{ + if (rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries) + kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries); + if (rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk.entries) + kfree(rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk.entries); + if (rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk.entries) + kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk.entries); + if (rdev->pm.dpm.dyn_state.cac_leakage_table.entries) + kfree(rdev->pm.dpm.dyn_state.cac_leakage_table.entries); +} diff --git a/drivers/gpu/drm/radeon/r600_dpm.h b/drivers/gpu/drm/radeon/r600_dpm.h index bd33aa1..c6b9e30 100644 --- a/drivers/gpu/drm/radeon/r600_dpm.h +++ b/drivers/gpu/drm/radeon/r600_dpm.h @@ -215,4 +215,7 @@ int r600_set_thermal_temperature_range(struct radeon_device *rdev, int min_temp, int max_temp); bool r600_is_internal_thermal_sensor(enum radeon_int_thermal_type sensor);
+int r600_parse_extended_power_table(struct radeon_device *rdev); +void r600_free_extended_power_table(struct radeon_device *rdev); + #endif diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 3b345cc..5bdb0bd 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1222,6 +1222,66 @@ struct radeon_dpm_thermal { bool high_to_low; };
+struct radeon_clock_and_voltage_limits { + u32 sclk; + u32 mclk; + u32 vddc; + u32 vddci; +}; + +struct radeon_clock_array { + u32 count; + u32 *values; +}; + +struct radeon_clock_voltage_dependency_entry { + u32 clk; + u16 v; +}; + +struct radeon_clock_voltage_dependency_table { + u32 count; + struct radeon_clock_voltage_dependency_entry *entries; +}; + +struct radeon_cac_leakage_entry { + u16 vddc; + u32 leakage; +}; + +struct radeon_cac_leakage_table { + u32 count; + struct radeon_cac_leakage_entry *entries; +}; + +struct radeon_dpm_dynamic_state { + struct radeon_clock_voltage_dependency_table vddc_dependency_on_sclk; + struct radeon_clock_voltage_dependency_table vddci_dependency_on_mclk; + struct radeon_clock_voltage_dependency_table vddc_dependency_on_mclk; + struct radeon_clock_array valid_sclk_values; + struct radeon_clock_array valid_mclk_values; + struct radeon_clock_and_voltage_limits max_clock_voltage_on_dc; + struct radeon_clock_and_voltage_limits max_clock_voltage_on_ac; + u32 mclk_sclk_ratio; + u32 sclk_mclk_delta; + u16 vddc_vddci_delta; + u16 min_vddc_for_pcie_gen2; + struct radeon_cac_leakage_table cac_leakage_table; +}; + +struct radeon_dpm_fan { + u16 t_min; + u16 t_med; + u16 t_high; + u16 pwm_min; + u16 pwm_med; + u16 pwm_high; + u8 t_hyst; + u32 cycle_delay; + u16 t_max; + bool ucode_fan_control; +}; + struct radeon_dpm { struct radeon_ps *ps; /* number of valid power states */ @@ -1244,6 +1304,16 @@ struct radeon_dpm { int new_active_crtc_count; u32 current_active_crtcs; int current_active_crtc_count; + struct radeon_dpm_dynamic_state dyn_state; + struct radeon_dpm_fan fan; + u32 tdp_limit; + u32 near_tdp_limit; + u32 sq_ramping_threshold; + u32 cac_leakage; + u16 tdp_od_limit; + u32 tdp_adjustment; + u16 load_line_slope; + bool power_control; /* special states active */ bool thermal_active; bool uvd_active;
From: Alex Deucher alexander.deucher@amd.com
Driver needs this information to validate power states.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/radeon.h | 1 + drivers/gpu/drm/radeon/radeon_pm.c | 7 +++++++ 2 files changed, 8 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 5bdb0bd..53aba4f 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1314,6 +1314,7 @@ struct radeon_dpm { u32 tdp_adjustment; u16 load_line_slope; bool power_control; + bool ac_power; /* special states active */ bool thermal_active; bool uvd_active; diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 5987580..9ac261f 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -1218,6 +1218,7 @@ static void radeon_pm_compute_clocks_dpm(struct radeon_device *rdev)
mutex_lock(&rdev->pm.mutex);
+ /* update active crtc counts */ rdev->pm.dpm.new_active_crtcs = 0; rdev->pm.dpm.new_active_crtc_count = 0; list_for_each_entry(crtc, @@ -1229,6 +1230,12 @@ static void radeon_pm_compute_clocks_dpm(struct radeon_device *rdev) } }
+ /* update battery/ac status */ + if (power_supply_is_system_supplied() > 0) + rdev->pm.dpm.ac_power = true; + else + rdev->pm.dpm.ac_power = false; + radeon_dpm_change_power_state_locked(rdev);
mutex_unlock(&rdev->pm.mutex);
From: Alex Deucher alexander.deucher@amd.com
Use a dedicated copy of the current power state since we may have to adjust it on the fly.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/radeon.h | 1 + drivers/gpu/drm/radeon/radeon_pm.c | 13 ++++++++++++- drivers/gpu/drm/radeon/sumo_dpm.c | 5 +++++ drivers/gpu/drm/radeon/sumo_dpm.h | 1 + 4 files changed, 19 insertions(+), 1 deletions(-)
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 53aba4f..a4d8d97 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1294,6 +1294,7 @@ struct radeon_dpm { struct radeon_ps *boot_ps; /* default uvd power state */ struct radeon_ps *uvd_ps; + struct radeon_ps hw_ps; enum radeon_pm_state_type state; enum radeon_pm_state_type user_state; u32 platform_caps; diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 9ac261f..7ee8cf6 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -687,6 +687,17 @@ restart_search: return NULL; }
+static void radeon_dpm_update_requested_ps(struct radeon_device *rdev, + struct radeon_ps *ps) +{ + /* copy the ps to the hw ps and point the requested ps + * at the hw state in case the driver wants to modify + * the state dynamically. + */ + rdev->pm.dpm.hw_ps = *ps; + rdev->pm.dpm.requested_ps = &rdev->pm.dpm.hw_ps; +} + static void radeon_dpm_change_power_state_locked(struct radeon_device *rdev) { int i; @@ -707,7 +718,7 @@ static void radeon_dpm_change_power_state_locked(struct radeon_device *rdev)
ps = radeon_dpm_pick_power_state(rdev, dpm_state); if (ps) - rdev->pm.dpm.requested_ps = ps; + radeon_dpm_update_requested_ps(rdev, ps); else return;
diff --git a/drivers/gpu/drm/radeon/sumo_dpm.c b/drivers/gpu/drm/radeon/sumo_dpm.c index cef25c4..3805302 100644 --- a/drivers/gpu/drm/radeon/sumo_dpm.c +++ b/drivers/gpu/drm/radeon/sumo_dpm.c @@ -1072,6 +1072,11 @@ static void sumo_apply_state_adjust_rules(struct radeon_device *rdev) u32 sclk_in_sr = pi->sys_info.min_sclk; /* ??? */ u32 i;
+ /* point to the hw copy since this function will modify the ps */ + pi->hw_ps = *ps; + rdev->pm.dpm.hw_ps.ps_priv = &pi->hw_ps; + ps = &pi->hw_ps; + if (rps->class & ATOM_PPLIB_CLASSIFICATION_THERMAL) return sumo_patch_thermal_state(rdev, ps, current_ps);
diff --git a/drivers/gpu/drm/radeon/sumo_dpm.h b/drivers/gpu/drm/radeon/sumo_dpm.h index d041a6c..a40b62a 100644 --- a/drivers/gpu/drm/radeon/sumo_dpm.h +++ b/drivers/gpu/drm/radeon/sumo_dpm.h @@ -129,6 +129,7 @@ struct sumo_power_info { bool enable_dynamic_patch_ps; bool enable_dpm; bool enable_boost; + struct sumo_ps hw_ps; };
#define SUMO_UTC_DFLT_00 0x48
From: Alex Deucher alexander.deucher@amd.com
Use a dedicated copy of the current power state since we may have to adjust it on the fly.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/trinity_dpm.c | 5 +++++ drivers/gpu/drm/radeon/trinity_dpm.h | 1 + 2 files changed, 6 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/trinity_dpm.c b/drivers/gpu/drm/radeon/trinity_dpm.c index 1b3822f..0c1b50a 100644 --- a/drivers/gpu/drm/radeon/trinity_dpm.c +++ b/drivers/gpu/drm/radeon/trinity_dpm.c @@ -1379,6 +1379,11 @@ static void trinity_apply_state_adjust_rules(struct radeon_device *rdev) bool force_high; u32 num_active_displays = rdev->pm.dpm.new_active_crtc_count;
+ /* point to the hw copy since this function will modify the ps */ + pi->hw_ps = *ps; + rdev->pm.dpm.hw_ps.ps_priv = &pi->hw_ps; + ps = &pi->hw_ps; + if (rps->class & ATOM_PPLIB_CLASSIFICATION_THERMAL) return trinity_patch_thermal_state(rdev, ps, current_ps);
diff --git a/drivers/gpu/drm/radeon/trinity_dpm.h b/drivers/gpu/drm/radeon/trinity_dpm.h index 31100ac..c663aed 100644 --- a/drivers/gpu/drm/radeon/trinity_dpm.h +++ b/drivers/gpu/drm/radeon/trinity_dpm.h @@ -110,6 +110,7 @@ struct trinity_power_info { bool enable_dpm; bool enable_sclk_ds; bool uvd_dpm; + struct trinity_ps hw_ps; };
#define TRINITY_AT_DFLT 30
From: Alex Deucher alexander.deucher@amd.com
Use a dedicated copy of the current power state since we may have to adjust it on the fly.
v2: fix up redundant state sets
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/btc_dpm.c | 340 ++++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/btc_dpm.h | 2 + drivers/gpu/drm/radeon/cypress_dpm.h | 1 + drivers/gpu/drm/radeon/radeon.h | 13 ++ drivers/gpu/drm/radeon/radeon_pm.c | 43 ++++- drivers/gpu/drm/radeon/rv770_dpm.c | 10 + 6 files changed, 400 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/radeon/btc_dpm.c b/drivers/gpu/drm/radeon/btc_dpm.c index 2662ef0..989592e 100644 --- a/drivers/gpu/drm/radeon/btc_dpm.c +++ b/drivers/gpu/drm/radeon/btc_dpm.c @@ -1152,6 +1152,164 @@ static const u32 turks_sysls_enable[] =
#endif
+u32 btc_valid_sclk[] = +{ + 5000, 10000, 15000, 20000, 25000, 30000, 35000, 40000, 45000, 50000, + 55000, 60000, 65000, 70000, 75000, 80000, 85000, 90000, 95000, 100000, + 105000, 110000, 11500, 120000, 125000, 130000, 135000, 140000, 145000, 150000, + 155000, 160000, 165000, 170000, 175000, 180000, 185000, 190000, 195000, 200000 +}; + +static const struct radeon_blacklist_clocks btc_blacklist_clocks[] = +{ + { 10000, 30000, RADEON_SCLK_UP }, + { 15000, 30000, RADEON_SCLK_UP }, + { 20000, 30000, RADEON_SCLK_UP }, + { 25000, 30000, RADEON_SCLK_UP } +}; + +static void btc_apply_voltage_dependency_rules(struct radeon_clock_voltage_dependency_table *table, + u32 clock, u16 max_voltage, u16 *voltage) +{ + u32 i; + + if ((table == NULL) || (table->count == 0)) + return; + + for (i= 0; i < table->count; i++) { + if (clock <= table->entries[i].clk) { + if (*voltage < table->entries[i].v) + *voltage = (u16)((table->entries[i].v < max_voltage) ? + table->entries[i].v : max_voltage); + return; + } + } + + *voltage = (*voltage > max_voltage) ? *voltage : max_voltage; +} + +static u32 btc_find_valid_clock(struct radeon_clock_array *clocks, + u32 max_clock, u32 requested_clock) +{ + unsigned int i; + + if ((clocks == NULL) || (clocks->count == 0)) + return (requested_clock < max_clock) ? requested_clock : max_clock; + + for (i = 0; i < clocks->count; i++) { + if (clocks->values[i] >= requested_clock) + return (clocks->values[i] < max_clock) ? clocks->values[i] : max_clock; + } + + return (clocks->values[clocks->count - 1] < max_clock) ? + clocks->values[clocks->count - 1] : max_clock; +} + +static u32 btc_get_valid_mclk(struct radeon_device *rdev, + u32 max_mclk, u32 requested_mclk) +{ + return btc_find_valid_clock(&rdev->pm.dpm.dyn_state.valid_mclk_values, + max_mclk, requested_mclk); +} + +static u32 btc_get_valid_sclk(struct radeon_device *rdev, + u32 max_sclk, u32 requested_sclk) +{ + return btc_find_valid_clock(&rdev->pm.dpm.dyn_state.valid_sclk_values, + max_sclk, requested_sclk); +} + +static void btc_skip_blacklist_clocks(struct radeon_device *rdev, + const u32 max_sclk, const u32 max_mclk, + u32 *sclk, u32 *mclk) +{ + int i, num_blacklist_clocks; + + if ((sclk == NULL) || (mclk == NULL)) + return; + + num_blacklist_clocks = ARRAY_SIZE(btc_blacklist_clocks); + + for (i = 0; i < num_blacklist_clocks; i++) { + if ((btc_blacklist_clocks[i].sclk == *sclk) && + (btc_blacklist_clocks[i].mclk == *mclk)) + break; + } + + if (i < num_blacklist_clocks) { + if (btc_blacklist_clocks[i].action == RADEON_SCLK_UP) { + *sclk = btc_get_valid_sclk(rdev, max_sclk, *sclk + 1); + + if (*sclk < max_sclk) + btc_skip_blacklist_clocks(rdev, max_sclk, max_mclk, sclk, mclk); + } + } +} + +static void btc_adjust_clock_combinations(struct radeon_device *rdev, + const struct radeon_clock_and_voltage_limits *max_limits, + struct rv7xx_pl *pl) +{ + + if ((pl->mclk == 0) || (pl->sclk == 0)) + return; + + if (pl->mclk == pl->sclk) + return; + + if (pl->mclk > pl->sclk) { + if (((pl->mclk + (pl->sclk - 1)) / pl->sclk) > rdev->pm.dpm.dyn_state.mclk_sclk_ratio) + pl->sclk = btc_get_valid_sclk(rdev, + max_limits->sclk, + (pl->mclk + + (rdev->pm.dpm.dyn_state.mclk_sclk_ratio - 1)) / + rdev->pm.dpm.dyn_state.mclk_sclk_ratio); + } else { + if ((pl->sclk - pl->mclk) > rdev->pm.dpm.dyn_state.sclk_mclk_delta) + pl->mclk = btc_get_valid_mclk(rdev, + max_limits->mclk, + pl->sclk - + rdev->pm.dpm.dyn_state.sclk_mclk_delta); + } +} + +static u16 btc_find_voltage(struct atom_voltage_table *table, u16 voltage) +{ + unsigned int i; + + for (i = 0; i < table->count; i++) { + if (voltage <= table->entries[i].value) + return table->entries[i].value; + } + + return table->entries[table->count - 1].value; +} + +static void btc_apply_voltage_delta_rules(struct radeon_device *rdev, + u16 max_vddc, u16 max_vddci, + u16 *vddc, u16 *vddci) +{ + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + u16 new_voltage; + + if ((0 == *vddc) || (0 == *vddci)) + return; + + if (*vddc > *vddci) { + if ((*vddc - *vddci) > rdev->pm.dpm.dyn_state.vddc_vddci_delta) { + new_voltage = btc_find_voltage(&eg_pi->vddci_voltage_table, + (*vddc - rdev->pm.dpm.dyn_state.vddc_vddci_delta)); + *vddci = (new_voltage < max_vddci) ? new_voltage : max_vddci; + } + } else { + if ((*vddci - *vddc) > rdev->pm.dpm.dyn_state.vddc_vddci_delta) { + new_voltage = btc_find_voltage(&eg_pi->vddc_voltage_table, + (*vddci - rdev->pm.dpm.dyn_state.vddc_vddci_delta)); + *vddc = (new_voltage < max_vddc) ? new_voltage : max_vddc; + } + } +} + static void btc_enable_bif_dynamic_pcie_gen2(struct radeon_device *rdev, bool enable) { @@ -1901,6 +2059,169 @@ static void btc_init_stutter_mode(struct radeon_device *rdev) } }
+static void btc_apply_state_adjust_rules(struct radeon_device *rdev) +{ + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct radeon_ps *rps = rdev->pm.dpm.requested_ps; + struct rv7xx_ps *ps = rv770_get_ps(rps); + struct radeon_clock_and_voltage_limits *max_limits; + bool disable_mclk_switching; + u32 mclk, sclk; + u16 vddc, vddci; + + /* point to the hw copy since this function will modify the ps */ + eg_pi->hw_ps = *ps; + rdev->pm.dpm.hw_ps.ps_priv = &eg_pi->hw_ps; + ps = &eg_pi->hw_ps; + + if (rdev->pm.dpm.new_active_crtc_count > 1) + disable_mclk_switching = true; + else + disable_mclk_switching = false; + + if (rdev->pm.dpm.ac_power) + max_limits = &rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac; + else + max_limits = &rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc; + + if (rdev->pm.dpm.ac_power == false) { + if (ps->high.mclk > max_limits->mclk) + ps->high.mclk = max_limits->mclk; + if (ps->high.sclk > max_limits->sclk) + ps->high.sclk = max_limits->sclk; + if (ps->high.vddc > max_limits->vddc) + ps->high.vddc = max_limits->vddc; + if (ps->high.vddci > max_limits->vddci) + ps->high.vddci = max_limits->vddci; + + if (ps->medium.mclk > max_limits->mclk) + ps->medium.mclk = max_limits->mclk; + if (ps->medium.sclk > max_limits->sclk) + ps->medium.sclk = max_limits->sclk; + if (ps->medium.vddc > max_limits->vddc) + ps->medium.vddc = max_limits->vddc; + if (ps->medium.vddci > max_limits->vddci) + ps->medium.vddci = max_limits->vddci; + + if (ps->low.mclk > max_limits->mclk) + ps->low.mclk = max_limits->mclk; + if (ps->low.sclk > max_limits->sclk) + ps->low.sclk = max_limits->sclk; + if (ps->low.vddc > max_limits->vddc) + ps->low.vddc = max_limits->vddc; + if (ps->low.vddci > max_limits->vddci) + ps->low.vddci = max_limits->vddci; + } + + /* XXX validate the min clocks required for display */ + + if (disable_mclk_switching) { + sclk = ps->low.sclk; + mclk = ps->high.mclk; + vddc = ps->low.vddc; + vddci = ps->high.vddci; + } else { + sclk = ps->low.sclk; + mclk = ps->low.mclk; + vddc = ps->low.vddc; + vddci = ps->low.vddci; + } + + /* adjusted low state */ + ps->low.sclk = sclk; + ps->low.mclk = mclk; + ps->low.vddc = vddc; + ps->low.vddci = vddci; + + btc_skip_blacklist_clocks(rdev, max_limits->sclk, max_limits->mclk, + &ps->low.sclk, &ps->low.mclk); + + /* adjusted medium, high states */ + if (ps->medium.sclk < ps->low.sclk) + ps->medium.sclk = ps->low.sclk; + if (ps->medium.vddc < ps->low.vddc) + ps->medium.vddc = ps->low.vddc; + if (ps->high.sclk < ps->medium.sclk) + ps->high.sclk = ps->medium.sclk; + if (ps->high.vddc < ps->medium.vddc) + ps->high.vddc = ps->medium.vddc; + + if (disable_mclk_switching) { + mclk = ps->low.mclk; + if (mclk < ps->medium.mclk) + mclk = ps->medium.mclk; + if (mclk < ps->high.mclk) + mclk = ps->high.mclk; + ps->low.mclk = mclk; + ps->low.vddci = vddci; + ps->medium.mclk = mclk; + ps->medium.vddci = vddci; + ps->high.mclk = mclk; + ps->high.vddci = vddci; + } else { + if (ps->medium.mclk < ps->low.mclk) + ps->medium.mclk = ps->low.mclk; + if (ps->medium.vddci < ps->low.vddci) + ps->medium.vddci = ps->low.vddci; + if (ps->high.mclk < ps->medium.mclk) + ps->high.mclk = ps->medium.mclk; + if (ps->high.vddci < ps->medium.vddci) + ps->high.vddci = ps->medium.vddci; + } + + btc_skip_blacklist_clocks(rdev, max_limits->sclk, max_limits->mclk, + &ps->medium.sclk, &ps->medium.mclk); + btc_skip_blacklist_clocks(rdev, max_limits->sclk, max_limits->mclk, + &ps->high.sclk, &ps->high.mclk); + + btc_adjust_clock_combinations(rdev, max_limits, &ps->low); + btc_adjust_clock_combinations(rdev, max_limits, &ps->medium); + btc_adjust_clock_combinations(rdev, max_limits, &ps->high); + + btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk, + ps->low.sclk, max_limits->vddc, &ps->low.vddc); + btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk, + ps->low.mclk, max_limits->vddci, &ps->low.vddci); + btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk, + ps->low.mclk, max_limits->vddc, &ps->low.vddc); + /* XXX validate the voltage required for display */ + btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk, + ps->medium.sclk, max_limits->vddc, &ps->medium.vddc); + btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk, + ps->medium.mclk, max_limits->vddci, &ps->medium.vddci); + btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk, + ps->medium.mclk, max_limits->vddc, &ps->medium.vddc); + /* XXX validate the voltage required for display */ + btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk, + ps->high.sclk, max_limits->vddc, &ps->high.vddc); + btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk, + ps->high.mclk, max_limits->vddci, &ps->high.vddci); + btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk, + ps->high.mclk, max_limits->vddc, &ps->high.vddc); + /* XXX validate the voltage required for display */ + + btc_apply_voltage_delta_rules(rdev, max_limits->vddc, max_limits->vddci, + &ps->low.vddc, &ps->low.vddci); + btc_apply_voltage_delta_rules(rdev, max_limits->vddc, max_limits->vddci, + &ps->medium.vddc, &ps->medium.vddci); + btc_apply_voltage_delta_rules(rdev, max_limits->vddc, max_limits->vddci, + &ps->high.vddc, &ps->high.vddci); + + if ((ps->high.vddc <= rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc.vddc) && + (ps->medium.vddc <= rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc.vddc) && + (ps->low.vddc <= rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc.vddc)) + ps->dc_compatible = true; + else + ps->dc_compatible = false; + + if (ps->low.vddc < rdev->pm.dpm.dyn_state.min_vddc_for_pcie_gen2) + ps->low.flags &= ~ATOM_PPLIB_R600_FLAGS_PCIEGEN2; + if (ps->medium.vddc < rdev->pm.dpm.dyn_state.min_vddc_for_pcie_gen2) + ps->medium.flags &= ~ATOM_PPLIB_R600_FLAGS_PCIEGEN2; + if (ps->high.vddc < rdev->pm.dpm.dyn_state.min_vddc_for_pcie_gen2) + ps->high.flags &= ~ATOM_PPLIB_R600_FLAGS_PCIEGEN2; +} + void btc_dpm_reset_asic(struct radeon_device *rdev) { rv770_restrict_performance_levels_before_switch(rdev); @@ -1913,6 +2234,8 @@ int btc_dpm_set_power_state(struct radeon_device *rdev) { struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+ btc_apply_state_adjust_rules(rdev); + btc_disable_ulv(rdev); btc_set_boot_state_timing(rdev); rv770_restrict_performance_levels_before_switch(rdev); @@ -2121,6 +2444,9 @@ int btc_dpm_init(struct radeon_device *rdev) ret = rv7xx_parse_power_table(rdev); if (ret) return ret; + ret = r600_parse_extended_power_table(rdev); + if (ret) + return ret;
if (rdev->pm.dpm.voltage_response_time == 0) rdev->pm.dpm.voltage_response_time = R600_VOLTAGERESPONSETIME_DFLT; @@ -2230,6 +2556,19 @@ int btc_dpm_init(struct radeon_device *rdev)
pi->sram_end = SMC_RAM_END;
+ rdev->pm.dpm.dyn_state.mclk_sclk_ratio = 4; + rdev->pm.dpm.dyn_state.vddc_vddci_delta = 200; + rdev->pm.dpm.dyn_state.min_vddc_for_pcie_gen2 = 900; + rdev->pm.dpm.dyn_state.valid_sclk_values.count = ARRAY_SIZE(btc_valid_sclk); + rdev->pm.dpm.dyn_state.valid_sclk_values.values = btc_valid_sclk; + rdev->pm.dpm.dyn_state.valid_mclk_values.count = 0; + rdev->pm.dpm.dyn_state.valid_mclk_values.values = NULL; + + if (rdev->family == CHIP_TURKS) + rdev->pm.dpm.dyn_state.sclk_mclk_delta = 15000; + else + rdev->pm.dpm.dyn_state.sclk_mclk_delta = 10000; + return 0; }
@@ -2242,4 +2581,5 @@ void btc_dpm_fini(struct radeon_device *rdev) } kfree(rdev->pm.dpm.ps); kfree(rdev->pm.dpm.priv); + r600_free_extended_power_table(rdev); } diff --git a/drivers/gpu/drm/radeon/btc_dpm.h b/drivers/gpu/drm/radeon/btc_dpm.h index 56b1957..807024d 100644 --- a/drivers/gpu/drm/radeon/btc_dpm.h +++ b/drivers/gpu/drm/radeon/btc_dpm.h @@ -33,4 +33,6 @@ #define BTC_CGULVPARAMETER_DFLT 0x00040035 #define BTC_CGULVCONTROL_DFLT 0x00001450
+extern u32 btc_valid_sclk[]; + #endif diff --git a/drivers/gpu/drm/radeon/cypress_dpm.h b/drivers/gpu/drm/radeon/cypress_dpm.h index 029bc9d..9e24d7a 100644 --- a/drivers/gpu/drm/radeon/cypress_dpm.h +++ b/drivers/gpu/drm/radeon/cypress_dpm.h @@ -88,6 +88,7 @@ struct evergreen_power_info { struct at ats[2]; /* smc offsets */ u16 mc_reg_table_start; + struct rv7xx_ps hw_ps; };
#define CYPRESS_HASI_DFLT 400000 diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index a4d8d97..16c7d52 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1222,6 +1222,19 @@ struct radeon_dpm_thermal { bool high_to_low; };
+enum radeon_clk_action +{ + RADEON_SCLK_UP = 1, + RADEON_SCLK_DOWN +}; + +struct radeon_blacklist_clocks +{ + u32 sclk; + u32 mclk; + enum radeon_clk_action action; +}; + struct radeon_clock_and_voltage_limits { u32 sclk; u32 mclk; diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 7ee8cf6..2eb41be 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -722,17 +722,42 @@ static void radeon_dpm_change_power_state_locked(struct radeon_device *rdev) else return;
- /* no need to reprogram if nothing changed */ + /* no need to reprogram if nothing changed unless we are on BTC+ */ if (rdev->pm.dpm.current_ps == rdev->pm.dpm.requested_ps) { - /* update display watermarks based on new power state */ - if (rdev->pm.dpm.new_active_crtcs != rdev->pm.dpm.current_active_crtcs) { - radeon_bandwidth_update(rdev); - /* update displays */ - radeon_dpm_display_configuration_changed(rdev); - rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs; - rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count; + if ((rdev->family < CHIP_BARTS) || (rdev->flags & RADEON_IS_IGP)) { + /* for pre-BTC and APUs if the num crtcs changed but state is the same, + * all we need to do is update the display configuration. + */ + if (rdev->pm.dpm.new_active_crtcs != rdev->pm.dpm.current_active_crtcs) { + /* update display watermarks based on new power state */ + radeon_bandwidth_update(rdev); + /* update displays */ + radeon_dpm_display_configuration_changed(rdev); + rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs; + rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count; + } + return; + } else { + /* for BTC+ if the num crtcs hasn't changed and state is the same, + * nothing to do, if the num crtcs is > 1 and state is the same, + * update display configuration. + */ + if (rdev->pm.dpm.new_active_crtcs == + rdev->pm.dpm.current_active_crtcs) { + return; + } else { + if ((rdev->pm.dpm.current_active_crtc_count > 1) && + (rdev->pm.dpm.new_active_crtc_count > 1)) { + /* update display watermarks based on new power state */ + radeon_bandwidth_update(rdev); + /* update displays */ + radeon_dpm_display_configuration_changed(rdev); + rdev->pm.dpm.current_active_crtcs = rdev->pm.dpm.new_active_crtcs; + rdev->pm.dpm.current_active_crtc_count = rdev->pm.dpm.new_active_crtc_count; + return; + } + } } - return; }
printk("switching from power state:\n"); diff --git a/drivers/gpu/drm/radeon/rv770_dpm.c b/drivers/gpu/drm/radeon/rv770_dpm.c index 55e5126..f5efa4c 100644 --- a/drivers/gpu/drm/radeon/rv770_dpm.c +++ b/drivers/gpu/drm/radeon/rv770_dpm.c @@ -2165,6 +2165,16 @@ static void rv7xx_parse_pplib_clock_info(struct radeon_device *rdev, pl->vddc = vddc; pl->vddci = vddci; } + + if (rdev->family >= CHIP_BARTS) { + if ((rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) == + ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE) { + rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac.sclk = pl->sclk; + rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac.mclk = pl->mclk; + rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac.vddc = pl->vddc; + rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac.vddci = pl->vddci; + } + } }
int rv7xx_parse_power_table(struct radeon_device *rdev)
From: Alex Deucher alexander.deucher@amd.com
This adds dpm support for cayman asics. This includes: - clockgating - dynamic engine clock scaling - dynamic memory clock scaling - dynamic voltage scaling - dynamic pcie gen1/gen2 switching (requires additional acpi support) - power containment - shader power scaling
Set radeon.dpm=1 to enable.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/Makefile | 2 +- drivers/gpu/drm/radeon/btc_dpm.c | 36 +- drivers/gpu/drm/radeon/btc_dpm.h | 20 +- drivers/gpu/drm/radeon/cypress_dpm.c | 11 +- drivers/gpu/drm/radeon/cypress_dpm.h | 4 + drivers/gpu/drm/radeon/ni.c | 4 +- drivers/gpu/drm/radeon/ni_dpm.c | 4093 +++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/ni_dpm.h | 233 ++ drivers/gpu/drm/radeon/nid.h | 552 +++++ drivers/gpu/drm/radeon/nislands_smc.h | 329 +++ drivers/gpu/drm/radeon/ppsmc.h | 12 + drivers/gpu/drm/radeon/radeon_asic.c | 12 + drivers/gpu/drm/radeon/radeon_asic.h | 10 + drivers/gpu/drm/radeon/radeon_pm.c | 1 + drivers/gpu/drm/radeon/radeon_ucode.h | 5 + drivers/gpu/drm/radeon/rv770_smc.c | 27 + 16 files changed, 5323 insertions(+), 28 deletions(-) create mode 100644 drivers/gpu/drm/radeon/ni_dpm.c create mode 100644 drivers/gpu/drm/radeon/ni_dpm.h create mode 100644 drivers/gpu/drm/radeon/nislands_smc.h
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index 2239ec2..32d1c7f 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile @@ -79,7 +79,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \ si_blit_shaders.o radeon_prime.o radeon_uvd.o cik.o cik_blit_shaders.o \ r600_dpm.o rs780_dpm.o rv6xx_dpm.o rv770_dpm.o rv730_dpm.o rv740_dpm.o \ rv770_smc.o cypress_dpm.o btc_dpm.o sumo_dpm.o sumo_smc.o trinity_dpm.o \ - trinity_smc.o + trinity_smc.o ni_dpm.o
radeon-$(CONFIG_COMPAT) += radeon_ioc32.o radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o diff --git a/drivers/gpu/drm/radeon/btc_dpm.c b/drivers/gpu/drm/radeon/btc_dpm.c index 989592e..79f5ed4 100644 --- a/drivers/gpu/drm/radeon/btc_dpm.c +++ b/drivers/gpu/drm/radeon/btc_dpm.c @@ -1152,7 +1152,7 @@ static const u32 turks_sysls_enable[] =
#endif
-u32 btc_valid_sclk[] = +u32 btc_valid_sclk[40] = { 5000, 10000, 15000, 20000, 25000, 30000, 35000, 40000, 45000, 50000, 55000, 60000, 65000, 70000, 75000, 80000, 85000, 90000, 95000, 100000, @@ -1168,8 +1168,8 @@ static const struct radeon_blacklist_clocks btc_blacklist_clocks[] = { 25000, 30000, RADEON_SCLK_UP } };
-static void btc_apply_voltage_dependency_rules(struct radeon_clock_voltage_dependency_table *table, - u32 clock, u16 max_voltage, u16 *voltage) +void btc_apply_voltage_dependency_rules(struct radeon_clock_voltage_dependency_table *table, + u32 clock, u16 max_voltage, u16 *voltage) { u32 i;
@@ -1219,9 +1219,9 @@ static u32 btc_get_valid_sclk(struct radeon_device *rdev, max_sclk, requested_sclk); }
-static void btc_skip_blacklist_clocks(struct radeon_device *rdev, - const u32 max_sclk, const u32 max_mclk, - u32 *sclk, u32 *mclk) +void btc_skip_blacklist_clocks(struct radeon_device *rdev, + const u32 max_sclk, const u32 max_mclk, + u32 *sclk, u32 *mclk) { int i, num_blacklist_clocks;
@@ -1246,9 +1246,9 @@ static void btc_skip_blacklist_clocks(struct radeon_device *rdev, } }
-static void btc_adjust_clock_combinations(struct radeon_device *rdev, - const struct radeon_clock_and_voltage_limits *max_limits, - struct rv7xx_pl *pl) +void btc_adjust_clock_combinations(struct radeon_device *rdev, + const struct radeon_clock_and_voltage_limits *max_limits, + struct rv7xx_pl *pl) {
if ((pl->mclk == 0) || (pl->sclk == 0)) @@ -1285,9 +1285,9 @@ static u16 btc_find_voltage(struct atom_voltage_table *table, u16 voltage) return table->entries[table->count - 1].value; }
-static void btc_apply_voltage_delta_rules(struct radeon_device *rdev, - u16 max_vddc, u16 max_vddci, - u16 *vddc, u16 *vddci) +void btc_apply_voltage_delta_rules(struct radeon_device *rdev, + u16 max_vddc, u16 max_vddci, + u16 *vddc, u16 *vddci) { struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); u16 new_voltage; @@ -1417,8 +1417,8 @@ static int btc_populate_smc_acpi_state(struct radeon_device *rdev, return ret; }
-static void btc_program_mgcg_hw_sequence(struct radeon_device *rdev, - const u32 *sequence, u32 count) +void btc_program_mgcg_hw_sequence(struct radeon_device *rdev, + const u32 *sequence, u32 count) { u32 i, length = count * 3; u32 tmp; @@ -1596,7 +1596,7 @@ static void btc_ls_clock_gating_enable(struct radeon_device *rdev, btc_program_mgcg_hw_sequence(rdev, p, count); }
-static bool btc_dpm_enabled(struct radeon_device *rdev) +bool btc_dpm_enabled(struct radeon_device *rdev) { if (rv770_is_smc_running(rdev)) return true; @@ -1692,7 +1692,7 @@ static void btc_set_at_for_uvd(struct radeon_device *rdev)
}
-static void btc_notify_uvd_to_smc(struct radeon_device *rdev) +void btc_notify_uvd_to_smc(struct radeon_device *rdev) { struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); @@ -1708,7 +1708,7 @@ static void btc_notify_uvd_to_smc(struct radeon_device *rdev) } }
-static int btc_reset_to_default(struct radeon_device *rdev) +int btc_reset_to_default(struct radeon_device *rdev) { if (rv770_send_msg_to_smc(rdev, PPSMC_MSG_ResetToDefaults) != PPSMC_Result_OK) return -EINVAL; @@ -1730,7 +1730,7 @@ static void btc_stop_smc(struct radeon_device *rdev) r7xx_stop_smc(rdev); }
-static void btc_read_arb_registers(struct radeon_device *rdev) +void btc_read_arb_registers(struct radeon_device *rdev) { struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); struct evergreen_arb_registers *arb_registers = diff --git a/drivers/gpu/drm/radeon/btc_dpm.h b/drivers/gpu/drm/radeon/btc_dpm.h index 807024d..c22d39f 100644 --- a/drivers/gpu/drm/radeon/btc_dpm.h +++ b/drivers/gpu/drm/radeon/btc_dpm.h @@ -33,6 +33,24 @@ #define BTC_CGULVPARAMETER_DFLT 0x00040035 #define BTC_CGULVCONTROL_DFLT 0x00001450
-extern u32 btc_valid_sclk[]; +extern u32 btc_valid_sclk[40]; + +void btc_read_arb_registers(struct radeon_device *rdev); +void btc_program_mgcg_hw_sequence(struct radeon_device *rdev, + const u32 *sequence, u32 count); +void btc_skip_blacklist_clocks(struct radeon_device *rdev, + const u32 max_sclk, const u32 max_mclk, + u32 *sclk, u32 *mclk); +void btc_adjust_clock_combinations(struct radeon_device *rdev, + const struct radeon_clock_and_voltage_limits *max_limits, + struct rv7xx_pl *pl); +void btc_apply_voltage_dependency_rules(struct radeon_clock_voltage_dependency_table *table, + u32 clock, u16 max_voltage, u16 *voltage); +void btc_apply_voltage_delta_rules(struct radeon_device *rdev, + u16 max_vddc, u16 max_vddci, + u16 *vddc, u16 *vddci); +bool btc_dpm_enabled(struct radeon_device *rdev); +int btc_reset_to_default(struct radeon_device *rdev); +void btc_notify_uvd_to_smc(struct radeon_device *rdev);
#endif diff --git a/drivers/gpu/drm/radeon/cypress_dpm.c b/drivers/gpu/drm/radeon/cypress_dpm.c index 403ee15..22297b1 100644 --- a/drivers/gpu/drm/radeon/cypress_dpm.c +++ b/drivers/gpu/drm/radeon/cypress_dpm.c @@ -45,9 +45,6 @@ struct rv7xx_ps *rv770_get_ps(struct radeon_ps *rps); struct rv7xx_power_info *rv770_get_pi(struct radeon_device *rdev); struct evergreen_power_info *evergreen_get_pi(struct radeon_device *rdev);
-static u8 cypress_get_mclk_frequency_ratio(struct radeon_device *rdev, - u32 memory_clock, bool strobe_mode); - static void cypress_enable_bif_dynamic_pcie_gen2(struct radeon_device *rdev, bool enable) { @@ -416,7 +413,7 @@ static int cypress_populate_voltage_value(struct radeon_device *rdev, return 0; }
-static u8 cypress_get_strobe_mode_settings(struct radeon_device *rdev, u32 mclk) +u8 cypress_get_strobe_mode_settings(struct radeon_device *rdev, u32 mclk) { struct rv7xx_power_info *pi = rv770_get_pi(rdev); u8 result = 0; @@ -434,7 +431,7 @@ static u8 cypress_get_strobe_mode_settings(struct radeon_device *rdev, u32 mclk) return result; }
-static u32 cypress_map_clkf_to_ibias(struct radeon_device *rdev, u32 clkf) +u32 cypress_map_clkf_to_ibias(struct radeon_device *rdev, u32 clkf) { u32 ref_clk = rdev->clock.mpll.reference_freq; u32 vco = clkf * ref_clk; @@ -603,8 +600,8 @@ static int cypress_populate_mclk_value(struct radeon_device *rdev, return 0; }
-static u8 cypress_get_mclk_frequency_ratio(struct radeon_device *rdev, - u32 memory_clock, bool strobe_mode) +u8 cypress_get_mclk_frequency_ratio(struct radeon_device *rdev, + u32 memory_clock, bool strobe_mode) { u8 mc_para_index;
diff --git a/drivers/gpu/drm/radeon/cypress_dpm.h b/drivers/gpu/drm/radeon/cypress_dpm.h index 9e24d7a..9b6198e 100644 --- a/drivers/gpu/drm/radeon/cypress_dpm.h +++ b/drivers/gpu/drm/radeon/cypress_dpm.h @@ -141,5 +141,9 @@ void cypress_enable_mclk_control(struct radeon_device *rdev, bool enable); void cypress_start_dpm(struct radeon_device *rdev); void cypress_advertise_gen2_capability(struct radeon_device *rdev); +u32 cypress_map_clkf_to_ibias(struct radeon_device *rdev, u32 clkf); +u8 cypress_get_mclk_frequency_ratio(struct radeon_device *rdev, + u32 memory_clock, bool strobe_mode); +u8 cypress_get_strobe_mode_settings(struct radeon_device *rdev, u32 mclk);
#endif diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c index 7407762..ad65143 100644 --- a/drivers/gpu/drm/radeon/ni.c +++ b/drivers/gpu/drm/radeon/ni.c @@ -194,6 +194,7 @@ MODULE_FIRMWARE("radeon/CAYMAN_pfp.bin"); MODULE_FIRMWARE("radeon/CAYMAN_me.bin"); MODULE_FIRMWARE("radeon/CAYMAN_mc.bin"); MODULE_FIRMWARE("radeon/CAYMAN_rlc.bin"); +MODULE_FIRMWARE("radeon/CAYMAN_smc.bin"); MODULE_FIRMWARE("radeon/ARUBA_pfp.bin"); MODULE_FIRMWARE("radeon/ARUBA_me.bin"); MODULE_FIRMWARE("radeon/ARUBA_rlc.bin"); @@ -734,6 +735,7 @@ int ni_init_microcode(struct radeon_device *rdev) me_req_size = CAYMAN_PM4_UCODE_SIZE * 4; rlc_req_size = CAYMAN_RLC_UCODE_SIZE * 4; mc_req_size = CAYMAN_MC_UCODE_SIZE * 4; + smc_req_size = ALIGN(CAYMAN_SMC_UCODE_SIZE, 4); break; case CHIP_ARUBA: chip_name = "ARUBA"; @@ -797,7 +799,7 @@ int ni_init_microcode(struct radeon_device *rdev) } }
- if ((rdev->family >= CHIP_BARTS) && (rdev->family <= CHIP_CAICOS)) { + if ((rdev->family >= CHIP_BARTS) && (rdev->family <= CHIP_CAYMAN)) { snprintf(fw_name, sizeof(fw_name), "radeon/%s_smc.bin", chip_name); err = request_firmware(&rdev->smc_fw, fw_name, &pdev->dev); if (err) diff --git a/drivers/gpu/drm/radeon/ni_dpm.c b/drivers/gpu/drm/radeon/ni_dpm.c new file mode 100644 index 0000000..635bf04 --- /dev/null +++ b/drivers/gpu/drm/radeon/ni_dpm.c @@ -0,0 +1,4093 @@ +/* + * Copyright 2012 Advanced Micro Devices, 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. + * + */ + +#include "drmP.h" +#include "radeon.h" +#include "nid.h" +#include "r600_dpm.h" +#include "ni_dpm.h" +#include "atom.h" + +#define MC_CG_ARB_FREQ_F0 0x0a +#define MC_CG_ARB_FREQ_F1 0x0b +#define MC_CG_ARB_FREQ_F2 0x0c +#define MC_CG_ARB_FREQ_F3 0x0d + +#define SMC_RAM_END 0xC000 + +static const struct ni_cac_weights cac_weights_cayman_xt = +{ + 0x15, + 0x2, + 0x19, + 0x2, + 0x8, + 0x14, + 0x2, + 0x16, + 0xE, + 0x17, + 0x13, + 0x2B, + 0x10, + 0x7, + 0x5, + 0x5, + 0x5, + 0x2, + 0x3, + 0x9, + 0x10, + 0x10, + 0x2B, + 0xA, + 0x9, + 0x4, + 0xD, + 0xD, + 0x3E, + 0x18, + 0x14, + 0, + 0x3, + 0x3, + 0x5, + 0, + 0x2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0x1CC, + 0, + 0x164, + 1, + 1, + 1, + 1, + 12, + 12, + 12, + 0x12, + 0x1F, + 132, + 5, + 7, + 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + true +}; + +static const struct ni_cac_weights cac_weights_cayman_pro = +{ + 0x16, + 0x4, + 0x10, + 0x2, + 0xA, + 0x16, + 0x2, + 0x18, + 0x10, + 0x1A, + 0x16, + 0x2D, + 0x12, + 0xA, + 0x6, + 0x6, + 0x6, + 0x2, + 0x4, + 0xB, + 0x11, + 0x11, + 0x2D, + 0xC, + 0xC, + 0x7, + 0x10, + 0x10, + 0x3F, + 0x1A, + 0x16, + 0, + 0x7, + 0x4, + 0x6, + 1, + 0x2, + 0x1, + 0, + 0, + 0, + 0, + 0, + 0, + 0x30, + 0, + 0x1CF, + 0, + 0x166, + 1, + 1, + 1, + 1, + 12, + 12, + 12, + 0x15, + 0x1F, + 132, + 6, + 6, + 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + true +}; + +static const struct ni_cac_weights cac_weights_cayman_le = +{ + 0x7, + 0xE, + 0x1, + 0xA, + 0x1, + 0x3F, + 0x2, + 0x18, + 0x10, + 0x1A, + 0x1, + 0x3F, + 0x1, + 0xE, + 0x6, + 0x6, + 0x6, + 0x2, + 0x4, + 0x9, + 0x1A, + 0x1A, + 0x2C, + 0xA, + 0x11, + 0x8, + 0x19, + 0x19, + 0x1, + 0x1, + 0x1A, + 0, + 0x8, + 0x5, + 0x8, + 0x1, + 0x3, + 0x1, + 0, + 0, + 0, + 0, + 0, + 0, + 0x38, + 0x38, + 0x239, + 0x3, + 0x18A, + 1, + 1, + 1, + 1, + 12, + 12, + 12, + 0x15, + 0x22, + 132, + 6, + 6, + 0, + { 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0 }, + true +}; + +#define NISLANDS_MGCG_SEQUENCE 300 + +static const u32 cayman_cgcg_cgls_default[] = +{ + 0x000008f8, 0x00000010, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000011, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000012, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000013, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000014, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000015, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000016, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000017, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000018, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000019, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000001a, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000001b, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000020, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000021, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000022, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000023, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000024, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000025, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000026, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000027, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000028, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000029, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000002a, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000002b, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff +}; +#define CAYMAN_CGCG_CGLS_DEFAULT_LENGTH sizeof(cayman_cgcg_cgls_default) / (3 * sizeof(u32)) + +static const u32 cayman_cgcg_cgls_disable[] = +{ + 0x000008f8, 0x00000010, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000011, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000012, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000013, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000014, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000015, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000016, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000017, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000018, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000019, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x0000001a, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x0000001b, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000020, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000021, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000022, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000023, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000024, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000025, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000026, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000027, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000028, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000029, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000002a, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000002b, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x00000644, 0x000f7902, 0x001f4180, + 0x00000644, 0x000f3802, 0x001f4180 +}; +#define CAYMAN_CGCG_CGLS_DISABLE_LENGTH sizeof(cayman_cgcg_cgls_disable) / (3 * sizeof(u32)) + +static const u32 cayman_cgcg_cgls_enable[] = +{ + 0x00000644, 0x000f7882, 0x001f4080, + 0x000008f8, 0x00000010, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000011, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000012, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000013, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000014, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000015, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000016, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000017, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000018, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000019, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000001a, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000001b, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000020, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000021, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000022, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000023, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000024, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000025, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000026, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000027, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000028, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000029, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x0000002a, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x0000002b, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff +}; +#define CAYMAN_CGCG_CGLS_ENABLE_LENGTH sizeof(cayman_cgcg_cgls_enable) / (3 * sizeof(u32)) + +static const u32 cayman_mgcg_default[] = +{ + 0x0000802c, 0xc0000000, 0xffffffff, + 0x00003fc4, 0xc0000000, 0xffffffff, + 0x00005448, 0x00000100, 0xffffffff, + 0x000055e4, 0x00000100, 0xffffffff, + 0x0000160c, 0x00000100, 0xffffffff, + 0x00008984, 0x06000100, 0xffffffff, + 0x0000c164, 0x00000100, 0xffffffff, + 0x00008a18, 0x00000100, 0xffffffff, + 0x0000897c, 0x06000100, 0xffffffff, + 0x00008b28, 0x00000100, 0xffffffff, + 0x00009144, 0x00800200, 0xffffffff, + 0x00009a60, 0x00000100, 0xffffffff, + 0x00009868, 0x00000100, 0xffffffff, + 0x00008d58, 0x00000100, 0xffffffff, + 0x00009510, 0x00000100, 0xffffffff, + 0x0000949c, 0x00000100, 0xffffffff, + 0x00009654, 0x00000100, 0xffffffff, + 0x00009030, 0x00000100, 0xffffffff, + 0x00009034, 0x00000100, 0xffffffff, + 0x00009038, 0x00000100, 0xffffffff, + 0x0000903c, 0x00000100, 0xffffffff, + 0x00009040, 0x00000100, 0xffffffff, + 0x0000a200, 0x00000100, 0xffffffff, + 0x0000a204, 0x00000100, 0xffffffff, + 0x0000a208, 0x00000100, 0xffffffff, + 0x0000a20c, 0x00000100, 0xffffffff, + 0x00009744, 0x00000100, 0xffffffff, + 0x00003f80, 0x00000100, 0xffffffff, + 0x0000a210, 0x00000100, 0xffffffff, + 0x0000a214, 0x00000100, 0xffffffff, + 0x000004d8, 0x00000100, 0xffffffff, + 0x00009664, 0x00000100, 0xffffffff, + 0x00009698, 0x00000100, 0xffffffff, + 0x000004d4, 0x00000200, 0xffffffff, + 0x000004d0, 0x00000000, 0xffffffff, + 0x000030cc, 0x00000104, 0xffffffff, + 0x0000d0c0, 0x00000100, 0xffffffff, + 0x0000d8c0, 0x00000100, 0xffffffff, + 0x0000802c, 0x40000000, 0xffffffff, + 0x00003fc4, 0x40000000, 0xffffffff, + 0x0000915c, 0x00010000, 0xffffffff, + 0x00009160, 0x00030002, 0xffffffff, + 0x00009164, 0x00050004, 0xffffffff, + 0x00009168, 0x00070006, 0xffffffff, + 0x00009178, 0x00070000, 0xffffffff, + 0x0000917c, 0x00030002, 0xffffffff, + 0x00009180, 0x00050004, 0xffffffff, + 0x0000918c, 0x00010006, 0xffffffff, + 0x00009190, 0x00090008, 0xffffffff, + 0x00009194, 0x00070000, 0xffffffff, + 0x00009198, 0x00030002, 0xffffffff, + 0x0000919c, 0x00050004, 0xffffffff, + 0x000091a8, 0x00010006, 0xffffffff, + 0x000091ac, 0x00090008, 0xffffffff, + 0x000091b0, 0x00070000, 0xffffffff, + 0x000091b4, 0x00030002, 0xffffffff, + 0x000091b8, 0x00050004, 0xffffffff, + 0x000091c4, 0x00010006, 0xffffffff, + 0x000091c8, 0x00090008, 0xffffffff, + 0x000091cc, 0x00070000, 0xffffffff, + 0x000091d0, 0x00030002, 0xffffffff, + 0x000091d4, 0x00050004, 0xffffffff, + 0x000091e0, 0x00010006, 0xffffffff, + 0x000091e4, 0x00090008, 0xffffffff, + 0x000091e8, 0x00000000, 0xffffffff, + 0x000091ec, 0x00070000, 0xffffffff, + 0x000091f0, 0x00030002, 0xffffffff, + 0x000091f4, 0x00050004, 0xffffffff, + 0x00009200, 0x00010006, 0xffffffff, + 0x00009204, 0x00090008, 0xffffffff, + 0x00009208, 0x00070000, 0xffffffff, + 0x0000920c, 0x00030002, 0xffffffff, + 0x00009210, 0x00050004, 0xffffffff, + 0x0000921c, 0x00010006, 0xffffffff, + 0x00009220, 0x00090008, 0xffffffff, + 0x00009224, 0x00070000, 0xffffffff, + 0x00009228, 0x00030002, 0xffffffff, + 0x0000922c, 0x00050004, 0xffffffff, + 0x00009238, 0x00010006, 0xffffffff, + 0x0000923c, 0x00090008, 0xffffffff, + 0x00009240, 0x00070000, 0xffffffff, + 0x00009244, 0x00030002, 0xffffffff, + 0x00009248, 0x00050004, 0xffffffff, + 0x00009254, 0x00010006, 0xffffffff, + 0x00009258, 0x00090008, 0xffffffff, + 0x0000925c, 0x00070000, 0xffffffff, + 0x00009260, 0x00030002, 0xffffffff, + 0x00009264, 0x00050004, 0xffffffff, + 0x00009270, 0x00010006, 0xffffffff, + 0x00009274, 0x00090008, 0xffffffff, + 0x00009278, 0x00070000, 0xffffffff, + 0x0000927c, 0x00030002, 0xffffffff, + 0x00009280, 0x00050004, 0xffffffff, + 0x0000928c, 0x00010006, 0xffffffff, + 0x00009290, 0x00090008, 0xffffffff, + 0x000092a8, 0x00070000, 0xffffffff, + 0x000092ac, 0x00030002, 0xffffffff, + 0x000092b0, 0x00050004, 0xffffffff, + 0x000092bc, 0x00010006, 0xffffffff, + 0x000092c0, 0x00090008, 0xffffffff, + 0x000092c4, 0x00070000, 0xffffffff, + 0x000092c8, 0x00030002, 0xffffffff, + 0x000092cc, 0x00050004, 0xffffffff, + 0x000092d8, 0x00010006, 0xffffffff, + 0x000092dc, 0x00090008, 0xffffffff, + 0x00009294, 0x00000000, 0xffffffff, + 0x0000802c, 0x40010000, 0xffffffff, + 0x00003fc4, 0x40010000, 0xffffffff, + 0x0000915c, 0x00010000, 0xffffffff, + 0x00009160, 0x00030002, 0xffffffff, + 0x00009164, 0x00050004, 0xffffffff, + 0x00009168, 0x00070006, 0xffffffff, + 0x00009178, 0x00070000, 0xffffffff, + 0x0000917c, 0x00030002, 0xffffffff, + 0x00009180, 0x00050004, 0xffffffff, + 0x0000918c, 0x00010006, 0xffffffff, + 0x00009190, 0x00090008, 0xffffffff, + 0x00009194, 0x00070000, 0xffffffff, + 0x00009198, 0x00030002, 0xffffffff, + 0x0000919c, 0x00050004, 0xffffffff, + 0x000091a8, 0x00010006, 0xffffffff, + 0x000091ac, 0x00090008, 0xffffffff, + 0x000091b0, 0x00070000, 0xffffffff, + 0x000091b4, 0x00030002, 0xffffffff, + 0x000091b8, 0x00050004, 0xffffffff, + 0x000091c4, 0x00010006, 0xffffffff, + 0x000091c8, 0x00090008, 0xffffffff, + 0x000091cc, 0x00070000, 0xffffffff, + 0x000091d0, 0x00030002, 0xffffffff, + 0x000091d4, 0x00050004, 0xffffffff, + 0x000091e0, 0x00010006, 0xffffffff, + 0x000091e4, 0x00090008, 0xffffffff, + 0x000091e8, 0x00000000, 0xffffffff, + 0x000091ec, 0x00070000, 0xffffffff, + 0x000091f0, 0x00030002, 0xffffffff, + 0x000091f4, 0x00050004, 0xffffffff, + 0x00009200, 0x00010006, 0xffffffff, + 0x00009204, 0x00090008, 0xffffffff, + 0x00009208, 0x00070000, 0xffffffff, + 0x0000920c, 0x00030002, 0xffffffff, + 0x00009210, 0x00050004, 0xffffffff, + 0x0000921c, 0x00010006, 0xffffffff, + 0x00009220, 0x00090008, 0xffffffff, + 0x00009224, 0x00070000, 0xffffffff, + 0x00009228, 0x00030002, 0xffffffff, + 0x0000922c, 0x00050004, 0xffffffff, + 0x00009238, 0x00010006, 0xffffffff, + 0x0000923c, 0x00090008, 0xffffffff, + 0x00009240, 0x00070000, 0xffffffff, + 0x00009244, 0x00030002, 0xffffffff, + 0x00009248, 0x00050004, 0xffffffff, + 0x00009254, 0x00010006, 0xffffffff, + 0x00009258, 0x00090008, 0xffffffff, + 0x0000925c, 0x00070000, 0xffffffff, + 0x00009260, 0x00030002, 0xffffffff, + 0x00009264, 0x00050004, 0xffffffff, + 0x00009270, 0x00010006, 0xffffffff, + 0x00009274, 0x00090008, 0xffffffff, + 0x00009278, 0x00070000, 0xffffffff, + 0x0000927c, 0x00030002, 0xffffffff, + 0x00009280, 0x00050004, 0xffffffff, + 0x0000928c, 0x00010006, 0xffffffff, + 0x00009290, 0x00090008, 0xffffffff, + 0x000092a8, 0x00070000, 0xffffffff, + 0x000092ac, 0x00030002, 0xffffffff, + 0x000092b0, 0x00050004, 0xffffffff, + 0x000092bc, 0x00010006, 0xffffffff, + 0x000092c0, 0x00090008, 0xffffffff, + 0x000092c4, 0x00070000, 0xffffffff, + 0x000092c8, 0x00030002, 0xffffffff, + 0x000092cc, 0x00050004, 0xffffffff, + 0x000092d8, 0x00010006, 0xffffffff, + 0x000092dc, 0x00090008, 0xffffffff, + 0x00009294, 0x00000000, 0xffffffff, + 0x0000802c, 0xc0000000, 0xffffffff, + 0x00003fc4, 0xc0000000, 0xffffffff, + 0x000008f8, 0x00000010, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000011, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000012, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000013, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000014, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000015, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000016, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000017, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000018, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000019, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000001a, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x0000001b, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff +}; +#define CAYMAN_MGCG_DEFAULT_LENGTH sizeof(cayman_mgcg_default) / (3 * sizeof(u32)) + +static const u32 cayman_mgcg_disable[] = +{ + 0x0000802c, 0xc0000000, 0xffffffff, + 0x000008f8, 0x00000000, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000001, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000002, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x000008f8, 0x00000003, 0xffffffff, + 0x000008fc, 0xffffffff, 0xffffffff, + 0x00009150, 0x00600000, 0xffffffff +}; +#define CAYMAN_MGCG_DISABLE_LENGTH sizeof(cayman_mgcg_disable) / (3 * sizeof(u32)) + +static const u32 cayman_mgcg_enable[] = +{ + 0x0000802c, 0xc0000000, 0xffffffff, + 0x000008f8, 0x00000000, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000001, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x000008f8, 0x00000002, 0xffffffff, + 0x000008fc, 0x00600000, 0xffffffff, + 0x000008f8, 0x00000003, 0xffffffff, + 0x000008fc, 0x00000000, 0xffffffff, + 0x00009150, 0x96944200, 0xffffffff +}; + +#define CAYMAN_MGCG_ENABLE_LENGTH sizeof(cayman_mgcg_enable) / (3 * sizeof(u32)) + +#define NISLANDS_SYSLS_SEQUENCE 100 + +static const u32 cayman_sysls_default[] = +{ + /* Register, Value, Mask bits */ + 0x000055e8, 0x00000000, 0xffffffff, + 0x0000d0bc, 0x00000000, 0xffffffff, + 0x0000d8bc, 0x00000000, 0xffffffff, + 0x000015c0, 0x000c1401, 0xffffffff, + 0x0000264c, 0x000c0400, 0xffffffff, + 0x00002648, 0x000c0400, 0xffffffff, + 0x00002650, 0x000c0400, 0xffffffff, + 0x000020b8, 0x000c0400, 0xffffffff, + 0x000020bc, 0x000c0400, 0xffffffff, + 0x000020c0, 0x000c0c80, 0xffffffff, + 0x0000f4a0, 0x000000c0, 0xffffffff, + 0x0000f4a4, 0x00680fff, 0xffffffff, + 0x00002f50, 0x00000404, 0xffffffff, + 0x000004c8, 0x00000001, 0xffffffff, + 0x000064ec, 0x00000000, 0xffffffff, + 0x00000c7c, 0x00000000, 0xffffffff, + 0x00008dfc, 0x00000000, 0xffffffff +}; +#define CAYMAN_SYSLS_DEFAULT_LENGTH sizeof(cayman_sysls_default) / (3 * sizeof(u32)) + +static const u32 cayman_sysls_disable[] = +{ + /* Register, Value, Mask bits */ + 0x0000d0c0, 0x00000000, 0xffffffff, + 0x0000d8c0, 0x00000000, 0xffffffff, + 0x000055e8, 0x00000000, 0xffffffff, + 0x0000d0bc, 0x00000000, 0xffffffff, + 0x0000d8bc, 0x00000000, 0xffffffff, + 0x000015c0, 0x00041401, 0xffffffff, + 0x0000264c, 0x00040400, 0xffffffff, + 0x00002648, 0x00040400, 0xffffffff, + 0x00002650, 0x00040400, 0xffffffff, + 0x000020b8, 0x00040400, 0xffffffff, + 0x000020bc, 0x00040400, 0xffffffff, + 0x000020c0, 0x00040c80, 0xffffffff, + 0x0000f4a0, 0x000000c0, 0xffffffff, + 0x0000f4a4, 0x00680000, 0xffffffff, + 0x00002f50, 0x00000404, 0xffffffff, + 0x000004c8, 0x00000001, 0xffffffff, + 0x000064ec, 0x00007ffd, 0xffffffff, + 0x00000c7c, 0x0000ff00, 0xffffffff, + 0x00008dfc, 0x0000007f, 0xffffffff +}; +#define CAYMAN_SYSLS_DISABLE_LENGTH sizeof(cayman_sysls_disable) / (3 * sizeof(u32)) + +static const u32 cayman_sysls_enable[] = +{ + /* Register, Value, Mask bits */ + 0x000055e8, 0x00000001, 0xffffffff, + 0x0000d0bc, 0x00000100, 0xffffffff, + 0x0000d8bc, 0x00000100, 0xffffffff, + 0x000015c0, 0x000c1401, 0xffffffff, + 0x0000264c, 0x000c0400, 0xffffffff, + 0x00002648, 0x000c0400, 0xffffffff, + 0x00002650, 0x000c0400, 0xffffffff, + 0x000020b8, 0x000c0400, 0xffffffff, + 0x000020bc, 0x000c0400, 0xffffffff, + 0x000020c0, 0x000c0c80, 0xffffffff, + 0x0000f4a0, 0x000000c0, 0xffffffff, + 0x0000f4a4, 0x00680fff, 0xffffffff, + 0x00002f50, 0x00000903, 0xffffffff, + 0x000004c8, 0x00000000, 0xffffffff, + 0x000064ec, 0x00000000, 0xffffffff, + 0x00000c7c, 0x00000000, 0xffffffff, + 0x00008dfc, 0x00000000, 0xffffffff +}; +#define CAYMAN_SYSLS_ENABLE_LENGTH sizeof(cayman_sysls_enable) / (3 * sizeof(u32)) + +struct rv7xx_power_info *rv770_get_pi(struct radeon_device *rdev); +struct evergreen_power_info *evergreen_get_pi(struct radeon_device *rdev); + +static struct ni_power_info *ni_get_pi(struct radeon_device *rdev) +{ + struct ni_power_info *pi = rdev->pm.dpm.priv; + + return pi; +} + +struct ni_ps *ni_get_ps(struct radeon_ps *rps) +{ + struct ni_ps *ps = rps->ps_priv; + + return ps; +} + +/* XXX: fix for kernel use */ +#if 0 +static double ni_exp(double x) +{ + int count = 1; + double sum = 1.0, term, tolerance = 0.000000001, y = x; + + if (x < 0) + y = -1 * x; + term = y; + + while (term >= tolerance) { + sum = sum + term; + count = count + 1; + term = term * (y / count); + } + + if (x < 0) + sum = 1.0 / sum; + + return sum; +} +#endif + +static void ni_calculate_leakage_for_v_and_t_formula(const struct ni_leakage_coeffients *coeff, + u16 v, s32 t, + u32 ileakage, + u32 *leakage) +{ +/* XXX: fix for kernel use */ +#if 0 + double kt, kv, leakage_w, i_leakage, vddc, temperature; + + i_leakage = ((double)ileakage) / 1000; + vddc = ((double)v) / 1000; + temperature = ((double)t) / 1000; + + kt = (((double)(coeff->at)) / 1000) * ni_exp((((double)(coeff->bt)) / 1000) * temperature); + kv = (((double)(coeff->av)) / 1000) * ni_exp((((double)(coeff->bv)) / 1000) * vddc); + + leakage_w = i_leakage * kt * kv * vddc; + + *leakage = (u32)(leakage_w * 1000); +#endif +} + +static void ni_calculate_leakage_for_v_and_t(struct radeon_device *rdev, + const struct ni_leakage_coeffients *coeff, + u16 v, + s32 t, + u32 i_leakage, + u32 *leakage) +{ + ni_calculate_leakage_for_v_and_t_formula(coeff, v, t, i_leakage, leakage); +} + +static void ni_apply_state_adjust_rules(struct radeon_device *rdev) +{ + struct ni_power_info *ni_pi = ni_get_pi(rdev); + struct radeon_ps *rps = rdev->pm.dpm.requested_ps; + struct ni_ps *ps = ni_get_ps(rps); + struct radeon_clock_and_voltage_limits *max_limits; + bool disable_mclk_switching; + u32 mclk, sclk; + u16 vddc, vddci; + int i; + + /* point to the hw copy since this function will modify the ps */ + ni_pi->hw_ps = *ps; + rdev->pm.dpm.hw_ps.ps_priv = &ni_pi->hw_ps; + ps = &ni_pi->hw_ps; + + if (rdev->pm.dpm.new_active_crtc_count > 1) + disable_mclk_switching = true; + else + disable_mclk_switching = false; + + if (rdev->pm.dpm.ac_power) + max_limits = &rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac; + else + max_limits = &rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc; + + if (rdev->pm.dpm.ac_power == false) { + for (i = 0; i < ps->performance_level_count; i++) { + if (ps->performance_levels[i].mclk > max_limits->mclk) + ps->performance_levels[i].mclk = max_limits->mclk; + if (ps->performance_levels[i].sclk > max_limits->sclk) + ps->performance_levels[i].sclk = max_limits->sclk; + if (ps->performance_levels[i].vddc > max_limits->vddc) + ps->performance_levels[i].vddc = max_limits->vddc; + if (ps->performance_levels[i].vddci > max_limits->vddci) + ps->performance_levels[i].vddci = max_limits->vddci; + } + } + + /* XXX validate the min clocks required for display */ + + if (disable_mclk_switching) { + mclk = ps->performance_levels[ps->performance_level_count - 1].mclk; + sclk = ps->performance_levels[0].sclk; + vddc = ps->performance_levels[0].vddc; + vddci = ps->performance_levels[ps->performance_level_count - 1].vddci; + } else { + sclk = ps->performance_levels[0].sclk; + mclk = ps->performance_levels[0].mclk; + vddc = ps->performance_levels[0].vddc; + vddci = ps->performance_levels[0].vddci; + } + + /* adjusted low state */ + ps->performance_levels[0].sclk = sclk; + ps->performance_levels[0].mclk = mclk; + ps->performance_levels[0].vddc = vddc; + ps->performance_levels[0].vddci = vddci; + + btc_skip_blacklist_clocks(rdev, max_limits->sclk, max_limits->mclk, + &ps->performance_levels[0].sclk, + &ps->performance_levels[0].mclk); + + for (i = 1; i < ps->performance_level_count; i++) { + if (ps->performance_levels[i].sclk < ps->performance_levels[i - 1].sclk) + ps->performance_levels[i].sclk = ps->performance_levels[i - 1].sclk; + if (ps->performance_levels[i].vddc < ps->performance_levels[i - 1].vddc) + ps->performance_levels[i].vddc = ps->performance_levels[i - 1].vddc; + } + + if (disable_mclk_switching) { + mclk = ps->performance_levels[0].mclk; + for (i = 1; i < ps->performance_level_count; i++) { + if (mclk < ps->performance_levels[i].mclk) + mclk = ps->performance_levels[i].mclk; + } + for (i = 0; i < ps->performance_level_count; i++) { + ps->performance_levels[i].mclk = mclk; + ps->performance_levels[i].vddci = vddci; + } + } else { + for (i = 1; i < ps->performance_level_count; i++) { + if (ps->performance_levels[i].mclk < ps->performance_levels[i - 1].mclk) + ps->performance_levels[i].mclk = ps->performance_levels[i - 1].mclk; + if (ps->performance_levels[i].vddci < ps->performance_levels[i - 1].vddci) + ps->performance_levels[i].vddci = ps->performance_levels[i - 1].vddci; + } + } + + for (i = 1; i < ps->performance_level_count; i++) + btc_skip_blacklist_clocks(rdev, max_limits->sclk, max_limits->mclk, + &ps->performance_levels[i].sclk, + &ps->performance_levels[i].mclk); + + for (i = 0; i < ps->performance_level_count; i++) + btc_adjust_clock_combinations(rdev, max_limits, + &ps->performance_levels[i]); + + for (i = 0; i < ps->performance_level_count; i++) { + btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk, + ps->performance_levels[i].sclk, + max_limits->vddc, &ps->performance_levels[i].vddc); + btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk, + ps->performance_levels[i].mclk, + max_limits->vddci, &ps->performance_levels[i].vddci); + btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk, + ps->performance_levels[i].mclk, + max_limits->vddc, &ps->performance_levels[i].vddc); + /* XXX validate the voltage required for display */ + } + + for (i = 0; i < ps->performance_level_count; i++) { + btc_apply_voltage_delta_rules(rdev, + max_limits->vddc, max_limits->vddci, + &ps->performance_levels[i].vddc, + &ps->performance_levels[i].vddci); + } + + ps->dc_compatible = true; + for (i = 0; i < ps->performance_level_count; i++) { + if (ps->performance_levels[i].vddc > rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc.vddc) + ps->dc_compatible = false; + + if (ps->performance_levels[i].vddc < rdev->pm.dpm.dyn_state.min_vddc_for_pcie_gen2) + ps->performance_levels[i].flags &= ~ATOM_PPLIB_R600_FLAGS_PCIEGEN2; + } +} + +static void ni_cg_clockgating_default(struct radeon_device *rdev) +{ + u32 count; + const u32 *ps = NULL; + + ps = (const u32 *)&cayman_cgcg_cgls_default; + count = CAYMAN_CGCG_CGLS_DEFAULT_LENGTH; + + btc_program_mgcg_hw_sequence(rdev, ps, count); +} + +static void ni_gfx_clockgating_enable(struct radeon_device *rdev, + bool enable) +{ + u32 count; + const u32 *ps = NULL; + + if (enable) { + ps = (const u32 *)&cayman_cgcg_cgls_enable; + count = CAYMAN_CGCG_CGLS_ENABLE_LENGTH; + } else { + ps = (const u32 *)&cayman_cgcg_cgls_disable; + count = CAYMAN_CGCG_CGLS_DISABLE_LENGTH; + } + + btc_program_mgcg_hw_sequence(rdev, ps, count); +} + +static void ni_mg_clockgating_default(struct radeon_device *rdev) +{ + u32 count; + const u32 *ps = NULL; + + ps = (const u32 *)&cayman_mgcg_default; + count = CAYMAN_MGCG_DEFAULT_LENGTH; + + btc_program_mgcg_hw_sequence(rdev, ps, count); +} + +static void ni_mg_clockgating_enable(struct radeon_device *rdev, + bool enable) +{ + u32 count; + const u32 *ps = NULL; + + if (enable) { + ps = (const u32 *)&cayman_mgcg_enable; + count = CAYMAN_MGCG_ENABLE_LENGTH; + } else { + ps = (const u32 *)&cayman_mgcg_disable; + count = CAYMAN_MGCG_DISABLE_LENGTH; + } + + btc_program_mgcg_hw_sequence(rdev, ps, count); +} + +static void ni_ls_clockgating_default(struct radeon_device *rdev) +{ + u32 count; + const u32 *ps = NULL; + + ps = (const u32 *)&cayman_sysls_default; + count = CAYMAN_SYSLS_DEFAULT_LENGTH; + + btc_program_mgcg_hw_sequence(rdev, ps, count); +} + +static void ni_ls_clockgating_enable(struct radeon_device *rdev, + bool enable) +{ + u32 count; + const u32 *ps = NULL; + + if (enable) { + ps = (const u32 *)&cayman_sysls_enable; + count = CAYMAN_SYSLS_ENABLE_LENGTH; + } else { + ps = (const u32 *)&cayman_sysls_disable; + count = CAYMAN_SYSLS_DISABLE_LENGTH; + } + + btc_program_mgcg_hw_sequence(rdev, ps, count); + +} + +static int ni_patch_single_dependency_table_based_on_leakage(struct radeon_device *rdev, + struct radeon_clock_voltage_dependency_table *table) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + u32 i; + + if (table) { + for (i = 0; i < table->count; i++) { + if (0xff01 == table->entries[i].v) { + if (pi->max_vddc == 0) + return -EINVAL; + table->entries[i].v = pi->max_vddc; + } + } + } + return 0; +} + +static int ni_patch_dependency_tables_based_on_leakage(struct radeon_device *rdev) +{ + int ret = 0; + + ret = ni_patch_single_dependency_table_based_on_leakage(rdev, + &rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk); + + ret = ni_patch_single_dependency_table_based_on_leakage(rdev, + &rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk); + return ret; +} + +static void ni_stop_dpm(struct radeon_device *rdev) +{ + WREG32_P(GENERAL_PWRMGT, 0, ~GLOBAL_PWRMGT_EN); +} + +#if 0 +static int ni_notify_hw_of_power_source(struct radeon_device *rdev, + bool ac_power) +{ + if (ac_power) + return (rv770_send_msg_to_smc(rdev, PPSMC_MSG_RunningOnAC) == PPSMC_Result_OK) ? + 0 : -EINVAL; + + return 0; +} +#endif + +static PPSMC_Result ni_send_msg_to_smc_with_parameter(struct radeon_device *rdev, + PPSMC_Msg msg, u32 parameter) +{ + WREG32(SMC_SCRATCH0, parameter); + return rv770_send_msg_to_smc(rdev, msg); +} + +static int ni_restrict_performance_levels_before_switch(struct radeon_device *rdev) +{ + if (rv770_send_msg_to_smc(rdev, PPSMC_MSG_NoForcedLevel) != PPSMC_Result_OK) + return -EINVAL; + + return (ni_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetEnabledLevels, 1) == PPSMC_Result_OK) ? + 0 : -EINVAL; +} + +static void ni_stop_smc(struct radeon_device *rdev) +{ + u32 tmp; + int i; + + for (i = 0; i < rdev->usec_timeout; i++) { + tmp = RREG32(LB_SYNC_RESET_SEL) & LB_SYNC_RESET_SEL_MASK; + if (tmp != 1) + break; + udelay(1); + } + + udelay(100); + + r7xx_stop_smc(rdev); +} + +static int ni_process_firmware_header(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct ni_power_info *ni_pi = ni_get_pi(rdev); + u32 tmp; + int ret; + + ret = rv770_read_smc_sram_dword(rdev, + NISLANDS_SMC_FIRMWARE_HEADER_LOCATION + + NISLANDS_SMC_FIRMWARE_HEADER_stateTable, + &tmp, pi->sram_end); + + if (ret) + return ret; + + pi->state_table_start = (u16)tmp; + + ret = rv770_read_smc_sram_dword(rdev, + NISLANDS_SMC_FIRMWARE_HEADER_LOCATION + + NISLANDS_SMC_FIRMWARE_HEADER_softRegisters, + &tmp, pi->sram_end); + + if (ret) + return ret; + + pi->soft_regs_start = (u16)tmp; + + ret = rv770_read_smc_sram_dword(rdev, + NISLANDS_SMC_FIRMWARE_HEADER_LOCATION + + NISLANDS_SMC_FIRMWARE_HEADER_mcRegisterTable, + &tmp, pi->sram_end); + + if (ret) + return ret; + + eg_pi->mc_reg_table_start = (u16)tmp; + + ret = rv770_read_smc_sram_dword(rdev, + NISLANDS_SMC_FIRMWARE_HEADER_LOCATION + + NISLANDS_SMC_FIRMWARE_HEADER_fanTable, + &tmp, pi->sram_end); + + if (ret) + return ret; + + ni_pi->fan_table_start = (u16)tmp; + + ret = rv770_read_smc_sram_dword(rdev, + NISLANDS_SMC_FIRMWARE_HEADER_LOCATION + + NISLANDS_SMC_FIRMWARE_HEADER_mcArbDramAutoRefreshTable, + &tmp, pi->sram_end); + + if (ret) + return ret; + + ni_pi->arb_table_start = (u16)tmp; + + ret = rv770_read_smc_sram_dword(rdev, + NISLANDS_SMC_FIRMWARE_HEADER_LOCATION + + NISLANDS_SMC_FIRMWARE_HEADER_cacTable, + &tmp, pi->sram_end); + + if (ret) + return ret; + + ni_pi->cac_table_start = (u16)tmp; + + ret = rv770_read_smc_sram_dword(rdev, + NISLANDS_SMC_FIRMWARE_HEADER_LOCATION + + NISLANDS_SMC_FIRMWARE_HEADER_spllTable, + &tmp, pi->sram_end); + + if (ret) + return ret; + + ni_pi->spll_table_start = (u16)tmp; + + + return ret; +} + +static void ni_read_clock_registers(struct radeon_device *rdev) +{ + struct ni_power_info *ni_pi = ni_get_pi(rdev); + + ni_pi->clock_registers.cg_spll_func_cntl = RREG32(CG_SPLL_FUNC_CNTL); + ni_pi->clock_registers.cg_spll_func_cntl_2 = RREG32(CG_SPLL_FUNC_CNTL_2); + ni_pi->clock_registers.cg_spll_func_cntl_3 = RREG32(CG_SPLL_FUNC_CNTL_3); + ni_pi->clock_registers.cg_spll_func_cntl_4 = RREG32(CG_SPLL_FUNC_CNTL_4); + ni_pi->clock_registers.cg_spll_spread_spectrum = RREG32(CG_SPLL_SPREAD_SPECTRUM); + ni_pi->clock_registers.cg_spll_spread_spectrum_2 = RREG32(CG_SPLL_SPREAD_SPECTRUM_2); + ni_pi->clock_registers.mpll_ad_func_cntl = RREG32(MPLL_AD_FUNC_CNTL); + ni_pi->clock_registers.mpll_ad_func_cntl_2 = RREG32(MPLL_AD_FUNC_CNTL_2); + ni_pi->clock_registers.mpll_dq_func_cntl = RREG32(MPLL_DQ_FUNC_CNTL); + ni_pi->clock_registers.mpll_dq_func_cntl_2 = RREG32(MPLL_DQ_FUNC_CNTL_2); + ni_pi->clock_registers.mclk_pwrmgt_cntl = RREG32(MCLK_PWRMGT_CNTL); + ni_pi->clock_registers.dll_cntl = RREG32(DLL_CNTL); + ni_pi->clock_registers.mpll_ss1 = RREG32(MPLL_SS1); + ni_pi->clock_registers.mpll_ss2 = RREG32(MPLL_SS2); +} + +#if 0 +static int ni_enter_ulp_state(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + + if (pi->gfx_clock_gating) { + WREG32_P(SCLK_PWRMGT_CNTL, 0, ~DYN_GFX_CLK_OFF_EN); + WREG32_P(SCLK_PWRMGT_CNTL, GFX_CLK_FORCE_ON, ~GFX_CLK_FORCE_ON); + WREG32_P(SCLK_PWRMGT_CNTL, 0, ~GFX_CLK_FORCE_ON); + RREG32(GB_ADDR_CONFIG); + } + + WREG32_P(SMC_MSG, HOST_SMC_MSG(PPSMC_MSG_SwitchToMinimumPower), + ~HOST_SMC_MSG_MASK); + + udelay(25000); + + return 0; +} +#endif + +static void ni_program_response_times(struct radeon_device *rdev) +{ + u32 voltage_response_time, backbias_response_time, acpi_delay_time, vbi_time_out; + u32 vddc_dly, bb_dly, acpi_dly, vbi_dly, mclk_switch_limit; + u32 reference_clock; + + rv770_write_smc_soft_register(rdev, NI_SMC_SOFT_REGISTER_mvdd_chg_time, 1); + + voltage_response_time = (u32)rdev->pm.dpm.voltage_response_time; + backbias_response_time = (u32)rdev->pm.dpm.backbias_response_time; + + if (voltage_response_time == 0) + voltage_response_time = 1000; + + if (backbias_response_time == 0) + backbias_response_time = 1000; + + acpi_delay_time = 15000; + vbi_time_out = 100000; + + reference_clock = radeon_get_xclk(rdev); + + vddc_dly = (voltage_response_time * reference_clock) / 1600; + bb_dly = (backbias_response_time * reference_clock) / 1600; + acpi_dly = (acpi_delay_time * reference_clock) / 1600; + vbi_dly = (vbi_time_out * reference_clock) / 1600; + + mclk_switch_limit = (460 * reference_clock) / 100; + + rv770_write_smc_soft_register(rdev, NI_SMC_SOFT_REGISTER_delay_vreg, vddc_dly); + rv770_write_smc_soft_register(rdev, NI_SMC_SOFT_REGISTER_delay_bbias, bb_dly); + rv770_write_smc_soft_register(rdev, NI_SMC_SOFT_REGISTER_delay_acpi, acpi_dly); + rv770_write_smc_soft_register(rdev, NI_SMC_SOFT_REGISTER_mclk_chg_timeout, vbi_dly); + rv770_write_smc_soft_register(rdev, NI_SMC_SOFT_REGISTER_mc_block_delay, 0xAA); + rv770_write_smc_soft_register(rdev, NI_SMC_SOFT_REGISTER_mclk_switch_lim, mclk_switch_limit); +} + +static void ni_populate_smc_voltage_table(struct radeon_device *rdev, + struct atom_voltage_table *voltage_table, + NISLANDS_SMC_STATETABLE *table) +{ + unsigned int i; + + for (i = 0; i < voltage_table->count; i++) { + table->highSMIO[i] = 0; + table->lowSMIO[i] |= cpu_to_be32(voltage_table->entries[i].smio_low); + } +} + +static void ni_populate_smc_voltage_tables(struct radeon_device *rdev, + NISLANDS_SMC_STATETABLE *table) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + unsigned char i; + + if (eg_pi->vddc_voltage_table.count) { + ni_populate_smc_voltage_table(rdev, &eg_pi->vddc_voltage_table, table); + table->voltageMaskTable.highMask[NISLANDS_SMC_VOLTAGEMASK_VDDC] = 0; + table->voltageMaskTable.lowMask[NISLANDS_SMC_VOLTAGEMASK_VDDC] = + cpu_to_be32(eg_pi->vddc_voltage_table.mask_low); + + for (i = 0; i < eg_pi->vddc_voltage_table.count; i++) { + if (pi->max_vddc_in_table <= eg_pi->vddc_voltage_table.entries[i].value) { + table->maxVDDCIndexInPPTable = i; + break; + } + } + } + + if (eg_pi->vddci_voltage_table.count) { + ni_populate_smc_voltage_table(rdev, &eg_pi->vddci_voltage_table, table); + + table->voltageMaskTable.highMask[NISLANDS_SMC_VOLTAGEMASK_VDDCI] = 0; + table->voltageMaskTable.lowMask[NISLANDS_SMC_VOLTAGEMASK_VDDCI] = + cpu_to_be32(eg_pi->vddc_voltage_table.mask_low); + } +} + +static int ni_populate_voltage_value(struct radeon_device *rdev, + struct atom_voltage_table *table, + u16 value, + NISLANDS_SMC_VOLTAGE_VALUE *voltage) +{ + unsigned int i; + + for (i = 0; i < table->count; i++) { + if (value <= table->entries[i].value) { + voltage->index = (u8)i; + voltage->value = cpu_to_be16(table->entries[i].value); + break; + } + } + + if (i >= table->count) + return -EINVAL; + + return 0; +} + +static void ni_populate_mvdd_value(struct radeon_device *rdev, + u32 mclk, + NISLANDS_SMC_VOLTAGE_VALUE *voltage) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + + if (!pi->mvdd_control) { + voltage->index = eg_pi->mvdd_high_index; + voltage->value = cpu_to_be16(MVDD_HIGH_VALUE); + return; + } + + if (mclk <= pi->mvdd_split_frequency) { + voltage->index = eg_pi->mvdd_low_index; + voltage->value = cpu_to_be16(MVDD_LOW_VALUE); + } else { + voltage->index = eg_pi->mvdd_high_index; + voltage->value = cpu_to_be16(MVDD_HIGH_VALUE); + } +} + +static int ni_get_std_voltage_value(struct radeon_device *rdev, + NISLANDS_SMC_VOLTAGE_VALUE *voltage, + u16 *std_voltage) +{ + if (rdev->pm.dpm.dyn_state.cac_leakage_table.entries && + ((u32)voltage->index < rdev->pm.dpm.dyn_state.cac_leakage_table.count)) + *std_voltage = rdev->pm.dpm.dyn_state.cac_leakage_table.entries[voltage->index].vddc; + else + *std_voltage = be16_to_cpu(voltage->value); + + return 0; +} + +static void ni_populate_std_voltage_value(struct radeon_device *rdev, + u16 value, u8 index, + NISLANDS_SMC_VOLTAGE_VALUE *voltage) +{ + voltage->index = index; + voltage->value = cpu_to_be16(value); +} + +static u32 ni_get_smc_power_scaling_factor(struct radeon_device *rdev) +{ + u32 xclk_period; + u32 xclk = radeon_get_xclk(rdev); + u32 tmp = RREG32(CG_CAC_CTRL) & TID_CNT_MASK; + + xclk_period = (1000000000UL / xclk); + xclk_period /= 10000UL; + + return tmp * xclk_period; +} + +static u32 ni_scale_power_for_smc(u32 power_in_watts, u32 scaling_factor) +{ + return (power_in_watts * scaling_factor) << 2; +} + +static u32 ni_calculate_power_boost_limit(struct radeon_device *rdev, + struct radeon_ps *radeon_state, + u32 near_tdp_limit) +{ + struct ni_ps *state = ni_get_ps(radeon_state); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct ni_power_info *ni_pi = ni_get_pi(rdev); + u32 power_boost_limit = 0; + int ret; + + if (ni_pi->enable_power_containment && + ni_pi->use_power_boost_limit) { + NISLANDS_SMC_VOLTAGE_VALUE vddc; + u16 std_vddc_med; + u16 std_vddc_high; + u64 tmp; + + if (state->performance_level_count < 3) + return 0; + + ret = ni_populate_voltage_value(rdev, &eg_pi->vddc_voltage_table, + state->performance_levels[state->performance_level_count - 2].vddc, + &vddc); + if (ret) + return 0; + + ret = ni_get_std_voltage_value(rdev, &vddc, &std_vddc_med); + if (ret) + return 0; + + ret = ni_populate_voltage_value(rdev, &eg_pi->vddc_voltage_table, + state->performance_levels[state->performance_level_count - 1].vddc, + &vddc); + if (ret) + return 0; + + ret = ni_get_std_voltage_value(rdev, &vddc, &std_vddc_high); + if (ret) + return 0; + + tmp = ((u64)near_tdp_limit * ((u64)std_vddc_med * (u64)std_vddc_med) * 90) / + ((u64)std_vddc_high * (u64)std_vddc_high * 100); + if (tmp >> 32) + return 0; + power_boost_limit = (u32)tmp; + } + + return power_boost_limit; +} + +static int ni_calculate_adjusted_tdp_limits(struct radeon_device *rdev, + bool adjust_polarity, + u32 tdp_adjustment, + u32 *tdp_limit, + u32 *near_tdp_limit) +{ + if (tdp_adjustment > (u32)rdev->pm.dpm.tdp_od_limit) + return -EINVAL; + + if (adjust_polarity) { + *tdp_limit = ((100 + tdp_adjustment) * rdev->pm.dpm.tdp_limit) / 100; + *near_tdp_limit = rdev->pm.dpm.near_tdp_limit + (*tdp_limit - rdev->pm.dpm.tdp_limit); + } else { + *tdp_limit = ((100 - tdp_adjustment) * rdev->pm.dpm.tdp_limit) / 100; + *near_tdp_limit = rdev->pm.dpm.near_tdp_limit - (rdev->pm.dpm.tdp_limit - *tdp_limit); + } + + return 0; +} + +static int ni_populate_smc_tdp_limits(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct ni_power_info *ni_pi = ni_get_pi(rdev); + + if (ni_pi->enable_power_containment) { + struct radeon_ps *radeon_state = rdev->pm.dpm.requested_ps; + NISLANDS_SMC_STATETABLE *smc_table = &ni_pi->smc_statetable; + u32 scaling_factor = ni_get_smc_power_scaling_factor(rdev); + u32 tdp_limit; + u32 near_tdp_limit; + u32 power_boost_limit; + int ret; + + if (scaling_factor == 0) + return -EINVAL; + + memset(smc_table, 0, sizeof(NISLANDS_SMC_STATETABLE)); + + ret = ni_calculate_adjusted_tdp_limits(rdev, + false, /* ??? */ + rdev->pm.dpm.tdp_adjustment, + &tdp_limit, + &near_tdp_limit); + if (ret) + return ret; + + power_boost_limit = ni_calculate_power_boost_limit(rdev, radeon_state, + near_tdp_limit); + + smc_table->dpm2Params.TDPLimit = + cpu_to_be32(ni_scale_power_for_smc(tdp_limit, scaling_factor)); + smc_table->dpm2Params.NearTDPLimit = + cpu_to_be32(ni_scale_power_for_smc(near_tdp_limit, scaling_factor)); + smc_table->dpm2Params.SafePowerLimit = + cpu_to_be32(ni_scale_power_for_smc((near_tdp_limit * NISLANDS_DPM2_TDP_SAFE_LIMIT_PERCENT) / 100, + scaling_factor)); + smc_table->dpm2Params.PowerBoostLimit = + cpu_to_be32(ni_scale_power_for_smc(power_boost_limit, scaling_factor)); + + ret = rv770_copy_bytes_to_smc(rdev, + (u16)(pi->state_table_start + offsetof(NISLANDS_SMC_STATETABLE, dpm2Params) + + offsetof(PP_NIslands_DPM2Parameters, TDPLimit)), + (u8 *)(&smc_table->dpm2Params.TDPLimit), + sizeof(u32) * 4, pi->sram_end); + if (ret) + return ret; + } + + return 0; +} + +static int ni_copy_and_switch_arb_sets(struct radeon_device *rdev, + u32 arb_freq_src, u32 arb_freq_dest) +{ + u32 mc_arb_dram_timing; + u32 mc_arb_dram_timing2; + u32 burst_time; + u32 mc_cg_config; + + switch (arb_freq_src) { + case MC_CG_ARB_FREQ_F0: + mc_arb_dram_timing = RREG32(MC_ARB_DRAM_TIMING); + mc_arb_dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2); + burst_time = (RREG32(MC_ARB_BURST_TIME) & STATE0_MASK) >> STATE0_SHIFT; + break; + case MC_CG_ARB_FREQ_F1: + mc_arb_dram_timing = RREG32(MC_ARB_DRAM_TIMING_1); + mc_arb_dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2_1); + burst_time = (RREG32(MC_ARB_BURST_TIME) & STATE1_MASK) >> STATE1_SHIFT; + break; + case MC_CG_ARB_FREQ_F2: + mc_arb_dram_timing = RREG32(MC_ARB_DRAM_TIMING_2); + mc_arb_dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2_2); + burst_time = (RREG32(MC_ARB_BURST_TIME) & STATE2_MASK) >> STATE2_SHIFT; + break; + case MC_CG_ARB_FREQ_F3: + mc_arb_dram_timing = RREG32(MC_ARB_DRAM_TIMING_3); + mc_arb_dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2_3); + burst_time = (RREG32(MC_ARB_BURST_TIME) & STATE3_MASK) >> STATE3_SHIFT; + break; + default: + return -EINVAL; + } + + switch (arb_freq_dest) { + case MC_CG_ARB_FREQ_F0: + WREG32(MC_ARB_DRAM_TIMING, mc_arb_dram_timing); + WREG32(MC_ARB_DRAM_TIMING2, mc_arb_dram_timing2); + WREG32_P(MC_ARB_BURST_TIME, STATE0(burst_time), ~STATE0_MASK); + break; + case MC_CG_ARB_FREQ_F1: + WREG32(MC_ARB_DRAM_TIMING_1, mc_arb_dram_timing); + WREG32(MC_ARB_DRAM_TIMING2_1, mc_arb_dram_timing2); + WREG32_P(MC_ARB_BURST_TIME, STATE1(burst_time), ~STATE1_MASK); + break; + case MC_CG_ARB_FREQ_F2: + WREG32(MC_ARB_DRAM_TIMING_2, mc_arb_dram_timing); + WREG32(MC_ARB_DRAM_TIMING2_2, mc_arb_dram_timing2); + WREG32_P(MC_ARB_BURST_TIME, STATE2(burst_time), ~STATE2_MASK); + break; + case MC_CG_ARB_FREQ_F3: + WREG32(MC_ARB_DRAM_TIMING_3, mc_arb_dram_timing); + WREG32(MC_ARB_DRAM_TIMING2_3, mc_arb_dram_timing2); + WREG32_P(MC_ARB_BURST_TIME, STATE3(burst_time), ~STATE3_MASK); + break; + default: + return -EINVAL; + } + + mc_cg_config = RREG32(MC_CG_CONFIG) | 0x0000000F; + WREG32(MC_CG_CONFIG, mc_cg_config); + WREG32_P(MC_ARB_CG, CG_ARB_REQ(arb_freq_dest), ~CG_ARB_REQ_MASK); + + return 0; +} + +static int ni_init_arb_table_index(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct ni_power_info *ni_pi = ni_get_pi(rdev); + u32 tmp; + int ret; + + ret = rv770_read_smc_sram_dword(rdev, ni_pi->arb_table_start, + &tmp, pi->sram_end); + if (ret) + return ret; + + tmp &= 0x00FFFFFF; + tmp |= ((u32)MC_CG_ARB_FREQ_F1) << 24; + + return rv770_write_smc_sram_dword(rdev, ni_pi->arb_table_start, + tmp, pi->sram_end); +} + +static int ni_initial_switch_from_arb_f0_to_f1(struct radeon_device *rdev) +{ + return ni_copy_and_switch_arb_sets(rdev, MC_CG_ARB_FREQ_F0, MC_CG_ARB_FREQ_F1); +} + +static int ni_force_switch_to_arb_f0(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct ni_power_info *ni_pi = ni_get_pi(rdev); + u32 tmp; + int ret; + + ret = rv770_read_smc_sram_dword(rdev, ni_pi->arb_table_start, + &tmp, pi->sram_end); + if (ret) + return ret; + + tmp = (tmp >> 24) & 0xff; + + if (tmp == MC_CG_ARB_FREQ_F0) + return 0; + + return ni_copy_and_switch_arb_sets(rdev, tmp, MC_CG_ARB_FREQ_F0); +} + +static int ni_populate_memory_timing_parameters(struct radeon_device *rdev, + struct rv7xx_pl *pl, + SMC_NIslands_MCArbDramTimingRegisterSet *arb_regs) +{ + u32 dram_timing; + u32 dram_timing2; + + arb_regs->mc_arb_rfsh_rate = + (u8)rv770_calculate_memory_refresh_rate(rdev, pl->sclk); + + + radeon_atom_set_engine_dram_timings(rdev, + pl->sclk, + pl->mclk); + + dram_timing = RREG32(MC_ARB_DRAM_TIMING); + dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2); + + arb_regs->mc_arb_dram_timing = cpu_to_be32(dram_timing); + arb_regs->mc_arb_dram_timing2 = cpu_to_be32(dram_timing2); + + return 0; +} + +static int ni_do_program_memory_timing_parameters(struct radeon_device *rdev, + struct radeon_ps *radeon_state, + unsigned int first_arb_set) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct ni_power_info *ni_pi = ni_get_pi(rdev); + struct ni_ps *state = ni_get_ps(radeon_state); + SMC_NIslands_MCArbDramTimingRegisterSet arb_regs = { 0 }; + int i, ret = 0; + + for (i = 0; i < state->performance_level_count; i++) { + ret = ni_populate_memory_timing_parameters(rdev, &state->performance_levels[i], &arb_regs); + if (ret) + break; + + ret = rv770_copy_bytes_to_smc(rdev, + (u16)(ni_pi->arb_table_start + + offsetof(SMC_NIslands_MCArbDramTimingRegisters, data) + + sizeof(SMC_NIslands_MCArbDramTimingRegisterSet) * (first_arb_set + i)), + (u8 *)&arb_regs, + (u16)sizeof(SMC_NIslands_MCArbDramTimingRegisterSet), + pi->sram_end); + if (ret) + break; + } + return ret; +} + +static int ni_program_memory_timing_parameters(struct radeon_device *rdev) +{ + struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; + + return ni_do_program_memory_timing_parameters(rdev, radeon_new_state, + NISLANDS_DRIVER_STATE_ARB_INDEX); +} + +static void ni_populate_initial_mvdd_value(struct radeon_device *rdev, + struct NISLANDS_SMC_VOLTAGE_VALUE *voltage) +{ + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + + voltage->index = eg_pi->mvdd_high_index; + voltage->value = cpu_to_be16(MVDD_HIGH_VALUE); +} + +static int ni_populate_smc_initial_state(struct radeon_device *rdev, + struct radeon_ps *radeon_initial_state, + NISLANDS_SMC_STATETABLE *table) +{ + struct ni_ps *initial_state = ni_get_ps(radeon_initial_state); + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct ni_power_info *ni_pi = ni_get_pi(rdev); + u32 reg; + int ret; + + table->initialState.levels[0].mclk.vMPLL_AD_FUNC_CNTL = + cpu_to_be32(ni_pi->clock_registers.mpll_ad_func_cntl); + table->initialState.levels[0].mclk.vMPLL_AD_FUNC_CNTL_2 = + cpu_to_be32(ni_pi->clock_registers.mpll_ad_func_cntl_2); + table->initialState.levels[0].mclk.vMPLL_DQ_FUNC_CNTL = + cpu_to_be32(ni_pi->clock_registers.mpll_dq_func_cntl); + table->initialState.levels[0].mclk.vMPLL_DQ_FUNC_CNTL_2 = + cpu_to_be32(ni_pi->clock_registers.mpll_dq_func_cntl_2); + table->initialState.levels[0].mclk.vMCLK_PWRMGT_CNTL = + cpu_to_be32(ni_pi->clock_registers.mclk_pwrmgt_cntl); + table->initialState.levels[0].mclk.vDLL_CNTL = + cpu_to_be32(ni_pi->clock_registers.dll_cntl); + table->initialState.levels[0].mclk.vMPLL_SS = + cpu_to_be32(ni_pi->clock_registers.mpll_ss1); + table->initialState.levels[0].mclk.vMPLL_SS2 = + cpu_to_be32(ni_pi->clock_registers.mpll_ss2); + table->initialState.levels[0].mclk.mclk_value = + cpu_to_be32(initial_state->performance_levels[0].mclk); + + table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL = + cpu_to_be32(ni_pi->clock_registers.cg_spll_func_cntl); + table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_2 = + cpu_to_be32(ni_pi->clock_registers.cg_spll_func_cntl_2); + table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_3 = + cpu_to_be32(ni_pi->clock_registers.cg_spll_func_cntl_3); + table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_4 = + cpu_to_be32(ni_pi->clock_registers.cg_spll_func_cntl_4); + table->initialState.levels[0].sclk.vCG_SPLL_SPREAD_SPECTRUM = + cpu_to_be32(ni_pi->clock_registers.cg_spll_spread_spectrum); + table->initialState.levels[0].sclk.vCG_SPLL_SPREAD_SPECTRUM_2 = + cpu_to_be32(ni_pi->clock_registers.cg_spll_spread_spectrum_2); + table->initialState.levels[0].sclk.sclk_value = + cpu_to_be32(initial_state->performance_levels[0].sclk); + table->initialState.levels[0].arbRefreshState = + NISLANDS_INITIAL_STATE_ARB_INDEX; + + table->initialState.levels[0].ACIndex = 0; + + ret = ni_populate_voltage_value(rdev, &eg_pi->vddc_voltage_table, + initial_state->performance_levels[0].vddc, + &table->initialState.levels[0].vddc); + if (!ret) { + u16 std_vddc; + + ret = ni_get_std_voltage_value(rdev, + &table->initialState.levels[0].vddc, + &std_vddc); + if (!ret) + ni_populate_std_voltage_value(rdev, std_vddc, + table->initialState.levels[0].vddc.index, + &table->initialState.levels[0].std_vddc); + } + + if (eg_pi->vddci_control) + ni_populate_voltage_value(rdev, + &eg_pi->vddci_voltage_table, + initial_state->performance_levels[0].vddci, + &table->initialState.levels[0].vddci); + + ni_populate_initial_mvdd_value(rdev, &table->initialState.levels[0].mvdd); + + reg = CG_R(0xffff) | CG_L(0); + table->initialState.levels[0].aT = cpu_to_be32(reg); + + table->initialState.levels[0].bSP = cpu_to_be32(pi->dsp); + + if (pi->boot_in_gen2) + table->initialState.levels[0].gen2PCIE = 1; + else + table->initialState.levels[0].gen2PCIE = 0; + + if (pi->mem_gddr5) { + table->initialState.levels[0].strobeMode = + cypress_get_strobe_mode_settings(rdev, + initial_state->performance_levels[0].mclk); + + if (initial_state->performance_levels[0].mclk > pi->mclk_edc_enable_threshold) + table->initialState.levels[0].mcFlags = NISLANDS_SMC_MC_EDC_RD_FLAG | NISLANDS_SMC_MC_EDC_WR_FLAG; + else + table->initialState.levels[0].mcFlags = 0; + } + + table->initialState.levelCount = 1; + + table->initialState.flags |= PPSMC_SWSTATE_FLAG_DC; + + table->initialState.levels[0].dpm2.MaxPS = 0; + table->initialState.levels[0].dpm2.NearTDPDec = 0; + table->initialState.levels[0].dpm2.AboveSafeInc = 0; + table->initialState.levels[0].dpm2.BelowSafeInc = 0; + + reg = MIN_POWER_MASK | MAX_POWER_MASK; + table->initialState.levels[0].SQPowerThrottle = cpu_to_be32(reg); + + reg = MAX_POWER_DELTA_MASK | STI_SIZE_MASK | LTI_RATIO_MASK; + table->initialState.levels[0].SQPowerThrottle_2 = cpu_to_be32(reg); + + return 0; +} + +static int ni_populate_smc_acpi_state(struct radeon_device *rdev, + NISLANDS_SMC_STATETABLE *table) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct ni_power_info *ni_pi = ni_get_pi(rdev); + u32 mpll_ad_func_cntl = ni_pi->clock_registers.mpll_ad_func_cntl; + u32 mpll_ad_func_cntl_2 = ni_pi->clock_registers.mpll_ad_func_cntl_2; + u32 mpll_dq_func_cntl = ni_pi->clock_registers.mpll_dq_func_cntl; + u32 mpll_dq_func_cntl_2 = ni_pi->clock_registers.mpll_dq_func_cntl_2; + u32 spll_func_cntl = ni_pi->clock_registers.cg_spll_func_cntl; + u32 spll_func_cntl_2 = ni_pi->clock_registers.cg_spll_func_cntl_2; + u32 spll_func_cntl_3 = ni_pi->clock_registers.cg_spll_func_cntl_3; + u32 spll_func_cntl_4 = ni_pi->clock_registers.cg_spll_func_cntl_4; + u32 mclk_pwrmgt_cntl = ni_pi->clock_registers.mclk_pwrmgt_cntl; + u32 dll_cntl = ni_pi->clock_registers.dll_cntl; + u32 reg; + int ret; + + table->ACPIState = table->initialState; + + table->ACPIState.flags &= ~PPSMC_SWSTATE_FLAG_DC; + + if (pi->acpi_vddc) { + ret = ni_populate_voltage_value(rdev, + &eg_pi->vddc_voltage_table, + pi->acpi_vddc, &table->ACPIState.levels[0].vddc); + if (!ret) { + u16 std_vddc; + + ret = ni_get_std_voltage_value(rdev, + &table->ACPIState.levels[0].vddc, &std_vddc); + if (!ret) + ni_populate_std_voltage_value(rdev, std_vddc, + table->ACPIState.levels[0].vddc.index, + &table->ACPIState.levels[0].std_vddc); + } + + if (pi->pcie_gen2) { + if (pi->acpi_pcie_gen2) + table->ACPIState.levels[0].gen2PCIE = 1; + else + table->ACPIState.levels[0].gen2PCIE = 0; + } else { + table->ACPIState.levels[0].gen2PCIE = 0; + } + } else { + ret = ni_populate_voltage_value(rdev, + &eg_pi->vddc_voltage_table, + pi->min_vddc_in_table, + &table->ACPIState.levels[0].vddc); + if (!ret) { + u16 std_vddc; + + ret = ni_get_std_voltage_value(rdev, + &table->ACPIState.levels[0].vddc, + &std_vddc); + if (!ret) + ni_populate_std_voltage_value(rdev, std_vddc, + table->ACPIState.levels[0].vddc.index, + &table->ACPIState.levels[0].std_vddc); + } + table->ACPIState.levels[0].gen2PCIE = 0; + } + + if (eg_pi->acpi_vddci) { + if (eg_pi->vddci_control) + ni_populate_voltage_value(rdev, + &eg_pi->vddci_voltage_table, + eg_pi->acpi_vddci, + &table->ACPIState.levels[0].vddci); + } + + + mpll_ad_func_cntl &= ~PDNB; + + mpll_ad_func_cntl_2 |= BIAS_GEN_PDNB | RESET_EN; + + if (pi->mem_gddr5) + mpll_dq_func_cntl &= ~PDNB; + mpll_dq_func_cntl_2 |= BIAS_GEN_PDNB | RESET_EN | BYPASS; + + + mclk_pwrmgt_cntl |= (MRDCKA0_RESET | + MRDCKA1_RESET | + MRDCKB0_RESET | + MRDCKB1_RESET | + MRDCKC0_RESET | + MRDCKC1_RESET | + MRDCKD0_RESET | + MRDCKD1_RESET); + + mclk_pwrmgt_cntl &= ~(MRDCKA0_PDNB | + MRDCKA1_PDNB | + MRDCKB0_PDNB | + MRDCKB1_PDNB | + MRDCKC0_PDNB | + MRDCKC1_PDNB | + MRDCKD0_PDNB | + MRDCKD1_PDNB); + + dll_cntl |= (MRDCKA0_BYPASS | + MRDCKA1_BYPASS | + MRDCKB0_BYPASS | + MRDCKB1_BYPASS | + MRDCKC0_BYPASS | + MRDCKC1_BYPASS | + MRDCKD0_BYPASS | + MRDCKD1_BYPASS); + + spll_func_cntl_2 &= ~SCLK_MUX_SEL_MASK; + spll_func_cntl_2 |= SCLK_MUX_SEL(4); + + table->ACPIState.levels[0].mclk.vMPLL_AD_FUNC_CNTL = cpu_to_be32(mpll_ad_func_cntl); + table->ACPIState.levels[0].mclk.vMPLL_AD_FUNC_CNTL_2 = cpu_to_be32(mpll_ad_func_cntl_2); + table->ACPIState.levels[0].mclk.vMPLL_DQ_FUNC_CNTL = cpu_to_be32(mpll_dq_func_cntl); + table->ACPIState.levels[0].mclk.vMPLL_DQ_FUNC_CNTL_2 = cpu_to_be32(mpll_dq_func_cntl_2); + table->ACPIState.levels[0].mclk.vMCLK_PWRMGT_CNTL = cpu_to_be32(mclk_pwrmgt_cntl); + table->ACPIState.levels[0].mclk.vDLL_CNTL = cpu_to_be32(dll_cntl); + + table->ACPIState.levels[0].mclk.mclk_value = 0; + + table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL = cpu_to_be32(spll_func_cntl); + table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_2 = cpu_to_be32(spll_func_cntl_2); + table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_3 = cpu_to_be32(spll_func_cntl_3); + table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_4 = cpu_to_be32(spll_func_cntl_4); + + table->ACPIState.levels[0].sclk.sclk_value = 0; + + ni_populate_mvdd_value(rdev, 0, &table->ACPIState.levels[0].mvdd); + + if (eg_pi->dynamic_ac_timing) + table->ACPIState.levels[0].ACIndex = 1; + + table->ACPIState.levels[0].dpm2.MaxPS = 0; + table->ACPIState.levels[0].dpm2.NearTDPDec = 0; + table->ACPIState.levels[0].dpm2.AboveSafeInc = 0; + table->ACPIState.levels[0].dpm2.BelowSafeInc = 0; + + reg = MIN_POWER_MASK | MAX_POWER_MASK; + table->ACPIState.levels[0].SQPowerThrottle = cpu_to_be32(reg); + + reg = MAX_POWER_DELTA_MASK | STI_SIZE_MASK | LTI_RATIO_MASK; + table->ACPIState.levels[0].SQPowerThrottle_2 = cpu_to_be32(reg); + + return 0; +} + +static int ni_init_smc_table(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct ni_power_info *ni_pi = ni_get_pi(rdev); + int ret; + struct radeon_ps *radeon_boot_state = rdev->pm.dpm.boot_ps; + NISLANDS_SMC_STATETABLE *table = &ni_pi->smc_statetable; + + memset(table, 0, sizeof(NISLANDS_SMC_STATETABLE)); + + ni_populate_smc_voltage_tables(rdev, table); + + switch (rdev->pm.int_thermal_type) { + case THERMAL_TYPE_NI: + case THERMAL_TYPE_EMC2103_WITH_INTERNAL: + table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_INTERNAL; + break; + case THERMAL_TYPE_NONE: + table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_NONE; + break; + default: + table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_EXTERNAL; + break; + } + + if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_HARDWAREDC) + table->systemFlags |= PPSMC_SYSTEMFLAG_GPIO_DC; + + if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_REGULATOR_HOT) + table->systemFlags |= PPSMC_SYSTEMFLAG_REGULATOR_HOT; + + if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_STEPVDDC) + table->systemFlags |= PPSMC_SYSTEMFLAG_STEPVDDC; + + if (pi->mem_gddr5) + table->systemFlags |= PPSMC_SYSTEMFLAG_GDDR5; + + ret = ni_populate_smc_initial_state(rdev, radeon_boot_state, table); + if (ret) + return ret; + + ret = ni_populate_smc_acpi_state(rdev, table); + if (ret) + return ret; + + table->driverState = table->initialState; + + table->ULVState = table->initialState; + + ret = ni_do_program_memory_timing_parameters(rdev, radeon_boot_state, + NISLANDS_INITIAL_STATE_ARB_INDEX); + if (ret) + return ret; + + return rv770_copy_bytes_to_smc(rdev, pi->state_table_start, (u8 *)table, + sizeof(NISLANDS_SMC_STATETABLE), pi->sram_end); +} + +static int ni_calculate_sclk_params(struct radeon_device *rdev, + u32 engine_clock, + NISLANDS_SMC_SCLK_VALUE *sclk) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct ni_power_info *ni_pi = ni_get_pi(rdev); + struct atom_clock_dividers dividers; + u32 spll_func_cntl = ni_pi->clock_registers.cg_spll_func_cntl; + u32 spll_func_cntl_2 = ni_pi->clock_registers.cg_spll_func_cntl_2; + u32 spll_func_cntl_3 = ni_pi->clock_registers.cg_spll_func_cntl_3; + u32 spll_func_cntl_4 = ni_pi->clock_registers.cg_spll_func_cntl_4; + u32 cg_spll_spread_spectrum = ni_pi->clock_registers.cg_spll_spread_spectrum; + u32 cg_spll_spread_spectrum_2 = ni_pi->clock_registers.cg_spll_spread_spectrum_2; + u64 tmp; + u32 reference_clock = rdev->clock.spll.reference_freq; + u32 reference_divider; + u32 fbdiv; + int ret; + + ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM, + engine_clock, false, ÷rs); + if (ret) + return ret; + + reference_divider = 1 + dividers.ref_div; + + tmp = (u64) engine_clock * reference_divider * dividers.post_div; + + fbdiv = (u32) ((16384 * tmp) / reference_clock); + + spll_func_cntl &= ~(SPLL_PDIV_A_MASK | SPLL_REF_DIV_MASK); + spll_func_cntl |= SPLL_REF_DIV(dividers.ref_div); + spll_func_cntl |= SPLL_PDIV_A(dividers.post_div); + + spll_func_cntl_2 &= ~SCLK_MUX_SEL_MASK; + spll_func_cntl_2 |= SCLK_MUX_SEL(2); + + spll_func_cntl_3 &= ~SPLL_FB_DIV_MASK; + spll_func_cntl_3 |= SPLL_FB_DIV(fbdiv); + spll_func_cntl_3 |= SPLL_DITHEN; + + if (pi->sclk_ss) { + struct radeon_atom_ss ss; + u32 vco_freq = engine_clock * dividers.post_div; + + if (radeon_atombios_get_asic_ss_info(rdev, &ss, + ASIC_INTERNAL_ENGINE_SS, vco_freq)) { + u32 clk_s = reference_clock * 5 / (reference_divider * ss.rate); + u32 clk_v = 4 * ss.percentage * fbdiv / (clk_s * 10000); + + cg_spll_spread_spectrum &= ~CLK_S_MASK; + cg_spll_spread_spectrum |= CLK_S(clk_s); + cg_spll_spread_spectrum |= SSEN; + + cg_spll_spread_spectrum_2 &= ~CLK_V_MASK; + cg_spll_spread_spectrum_2 |= CLK_V(clk_v); + } + } + + sclk->sclk_value = engine_clock; + sclk->vCG_SPLL_FUNC_CNTL = spll_func_cntl; + sclk->vCG_SPLL_FUNC_CNTL_2 = spll_func_cntl_2; + sclk->vCG_SPLL_FUNC_CNTL_3 = spll_func_cntl_3; + sclk->vCG_SPLL_FUNC_CNTL_4 = spll_func_cntl_4; + sclk->vCG_SPLL_SPREAD_SPECTRUM = cg_spll_spread_spectrum; + sclk->vCG_SPLL_SPREAD_SPECTRUM_2 = cg_spll_spread_spectrum_2; + + return 0; +} + +static int ni_populate_sclk_value(struct radeon_device *rdev, + u32 engine_clock, + NISLANDS_SMC_SCLK_VALUE *sclk) +{ + NISLANDS_SMC_SCLK_VALUE sclk_tmp; + int ret; + + ret = ni_calculate_sclk_params(rdev, engine_clock, &sclk_tmp); + if (!ret) { + sclk->sclk_value = cpu_to_be32(sclk_tmp.sclk_value); + sclk->vCG_SPLL_FUNC_CNTL = cpu_to_be32(sclk_tmp.vCG_SPLL_FUNC_CNTL); + sclk->vCG_SPLL_FUNC_CNTL_2 = cpu_to_be32(sclk_tmp.vCG_SPLL_FUNC_CNTL_2); + sclk->vCG_SPLL_FUNC_CNTL_3 = cpu_to_be32(sclk_tmp.vCG_SPLL_FUNC_CNTL_3); + sclk->vCG_SPLL_FUNC_CNTL_4 = cpu_to_be32(sclk_tmp.vCG_SPLL_FUNC_CNTL_4); + sclk->vCG_SPLL_SPREAD_SPECTRUM = cpu_to_be32(sclk_tmp.vCG_SPLL_SPREAD_SPECTRUM); + sclk->vCG_SPLL_SPREAD_SPECTRUM_2 = cpu_to_be32(sclk_tmp.vCG_SPLL_SPREAD_SPECTRUM_2); + } + + return ret; +} + +static int ni_init_smc_spll_table(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct ni_power_info *ni_pi = ni_get_pi(rdev); + SMC_NISLANDS_SPLL_DIV_TABLE *spll_table; + NISLANDS_SMC_SCLK_VALUE sclk_params; + u32 fb_div; + u32 p_div; + u32 clk_s; + u32 clk_v; + u32 sclk = 0; + int i, ret; + u32 tmp; + + if (ni_pi->spll_table_start == 0) + return -EINVAL; + + spll_table = kzalloc(sizeof(SMC_NISLANDS_SPLL_DIV_TABLE), GFP_KERNEL); + if (spll_table == NULL) + return -ENOMEM; + + for (i = 0; i < 256; i++) { + ret = ni_calculate_sclk_params(rdev, sclk, &sclk_params); + if (ret) + break; + + p_div = (sclk_params.vCG_SPLL_FUNC_CNTL & SPLL_PDIV_A_MASK) >> SPLL_PDIV_A_SHIFT; + fb_div = (sclk_params.vCG_SPLL_FUNC_CNTL_3 & SPLL_FB_DIV_MASK) >> SPLL_FB_DIV_SHIFT; + clk_s = (sclk_params.vCG_SPLL_SPREAD_SPECTRUM & CLK_S_MASK) >> CLK_S_SHIFT; + clk_v = (sclk_params.vCG_SPLL_SPREAD_SPECTRUM_2 & CLK_V_MASK) >> CLK_V_SHIFT; + + fb_div &= ~0x00001FFF; + fb_div >>= 1; + clk_v >>= 6; + + if (p_div & ~(SMC_NISLANDS_SPLL_DIV_TABLE_PDIV_MASK >> SMC_NISLANDS_SPLL_DIV_TABLE_PDIV_SHIFT)) + ret = -EINVAL; + + if (clk_s & ~(SMC_NISLANDS_SPLL_DIV_TABLE_CLKS_MASK >> SMC_NISLANDS_SPLL_DIV_TABLE_CLKS_SHIFT)) + ret = -EINVAL; + + if (clk_s & ~(SMC_NISLANDS_SPLL_DIV_TABLE_CLKS_MASK >> SMC_NISLANDS_SPLL_DIV_TABLE_CLKS_SHIFT)) + ret = -EINVAL; + + if (clk_v & ~(SMC_NISLANDS_SPLL_DIV_TABLE_CLKV_MASK >> SMC_NISLANDS_SPLL_DIV_TABLE_CLKV_SHIFT)) + ret = -EINVAL; + + if (ret) + break; + + tmp = ((fb_div << SMC_NISLANDS_SPLL_DIV_TABLE_FBDIV_SHIFT) & SMC_NISLANDS_SPLL_DIV_TABLE_FBDIV_MASK) | + ((p_div << SMC_NISLANDS_SPLL_DIV_TABLE_PDIV_SHIFT) & SMC_NISLANDS_SPLL_DIV_TABLE_PDIV_MASK); + spll_table->freq[i] = cpu_to_be32(tmp); + + tmp = ((clk_v << SMC_NISLANDS_SPLL_DIV_TABLE_CLKV_SHIFT) & SMC_NISLANDS_SPLL_DIV_TABLE_CLKV_MASK) | + ((clk_s << SMC_NISLANDS_SPLL_DIV_TABLE_CLKS_SHIFT) & SMC_NISLANDS_SPLL_DIV_TABLE_CLKS_MASK); + spll_table->ss[i] = cpu_to_be32(tmp); + + sclk += 512; + } + + if (!ret) + ret = rv770_copy_bytes_to_smc(rdev, ni_pi->spll_table_start, (u8 *)spll_table, + sizeof(SMC_NISLANDS_SPLL_DIV_TABLE), pi->sram_end); + + kfree(spll_table); + + return ret; +} + +static int ni_populate_mclk_value(struct radeon_device *rdev, + u32 engine_clock, + u32 memory_clock, + NISLANDS_SMC_MCLK_VALUE *mclk, + bool strobe_mode, + bool dll_state_on) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct ni_power_info *ni_pi = ni_get_pi(rdev); + u32 mpll_ad_func_cntl = ni_pi->clock_registers.mpll_ad_func_cntl; + u32 mpll_ad_func_cntl_2 = ni_pi->clock_registers.mpll_ad_func_cntl_2; + u32 mpll_dq_func_cntl = ni_pi->clock_registers.mpll_dq_func_cntl; + u32 mpll_dq_func_cntl_2 = ni_pi->clock_registers.mpll_dq_func_cntl_2; + u32 mclk_pwrmgt_cntl = ni_pi->clock_registers.mclk_pwrmgt_cntl; + u32 dll_cntl = ni_pi->clock_registers.dll_cntl; + u32 mpll_ss1 = ni_pi->clock_registers.mpll_ss1; + u32 mpll_ss2 = ni_pi->clock_registers.mpll_ss2; + struct atom_clock_dividers dividers; + u32 ibias; + u32 dll_speed; + int ret; + u32 mc_seq_misc7; + + ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_MEMORY_PLL_PARAM, + memory_clock, strobe_mode, ÷rs); + if (ret) + return ret; + + if (!strobe_mode) { + mc_seq_misc7 = RREG32(MC_SEQ_MISC7); + + if (mc_seq_misc7 & 0x8000000) + dividers.post_div = 1; + } + + ibias = cypress_map_clkf_to_ibias(rdev, dividers.whole_fb_div); + + mpll_ad_func_cntl &= ~(CLKR_MASK | + YCLK_POST_DIV_MASK | + CLKF_MASK | + CLKFRAC_MASK | + IBIAS_MASK); + mpll_ad_func_cntl |= CLKR(dividers.ref_div); + mpll_ad_func_cntl |= YCLK_POST_DIV(dividers.post_div); + mpll_ad_func_cntl |= CLKF(dividers.whole_fb_div); + mpll_ad_func_cntl |= CLKFRAC(dividers.frac_fb_div); + mpll_ad_func_cntl |= IBIAS(ibias); + + if (dividers.vco_mode) + mpll_ad_func_cntl_2 |= VCO_MODE; + else + mpll_ad_func_cntl_2 &= ~VCO_MODE; + + if (pi->mem_gddr5) { + mpll_dq_func_cntl &= ~(CLKR_MASK | + YCLK_POST_DIV_MASK | + CLKF_MASK | + CLKFRAC_MASK | + IBIAS_MASK); + mpll_dq_func_cntl |= CLKR(dividers.ref_div); + mpll_dq_func_cntl |= YCLK_POST_DIV(dividers.post_div); + mpll_dq_func_cntl |= CLKF(dividers.whole_fb_div); + mpll_dq_func_cntl |= CLKFRAC(dividers.frac_fb_div); + mpll_dq_func_cntl |= IBIAS(ibias); + + if (strobe_mode) + mpll_dq_func_cntl &= ~PDNB; + else + mpll_dq_func_cntl |= PDNB; + + if (dividers.vco_mode) + mpll_dq_func_cntl_2 |= VCO_MODE; + else + mpll_dq_func_cntl_2 &= ~VCO_MODE; + } + + if (pi->mclk_ss) { + struct radeon_atom_ss ss; + u32 vco_freq = memory_clock * dividers.post_div; + + if (radeon_atombios_get_asic_ss_info(rdev, &ss, + ASIC_INTERNAL_MEMORY_SS, vco_freq)) { + u32 reference_clock = rdev->clock.mpll.reference_freq; + u32 decoded_ref = rv740_get_decoded_reference_divider(dividers.ref_div); + u32 clk_s = reference_clock * 5 / (decoded_ref * ss.rate); + u32 clk_v = ss.percentage * + (0x4000 * dividers.whole_fb_div + 0x800 * dividers.frac_fb_div) / (clk_s * 625); + + mpll_ss1 &= ~CLKV_MASK; + mpll_ss1 |= CLKV(clk_v); + + mpll_ss2 &= ~CLKS_MASK; + mpll_ss2 |= CLKS(clk_s); + } + } + + dll_speed = rv740_get_dll_speed(pi->mem_gddr5, + memory_clock); + + mclk_pwrmgt_cntl &= ~DLL_SPEED_MASK; + mclk_pwrmgt_cntl |= DLL_SPEED(dll_speed); + if (dll_state_on) + mclk_pwrmgt_cntl |= (MRDCKA0_PDNB | + MRDCKA1_PDNB | + MRDCKB0_PDNB | + MRDCKB1_PDNB | + MRDCKC0_PDNB | + MRDCKC1_PDNB | + MRDCKD0_PDNB | + MRDCKD1_PDNB); + else + mclk_pwrmgt_cntl &= ~(MRDCKA0_PDNB | + MRDCKA1_PDNB | + MRDCKB0_PDNB | + MRDCKB1_PDNB | + MRDCKC0_PDNB | + MRDCKC1_PDNB | + MRDCKD0_PDNB | + MRDCKD1_PDNB); + + + mclk->mclk_value = cpu_to_be32(memory_clock); + mclk->vMPLL_AD_FUNC_CNTL = cpu_to_be32(mpll_ad_func_cntl); + mclk->vMPLL_AD_FUNC_CNTL_2 = cpu_to_be32(mpll_ad_func_cntl_2); + mclk->vMPLL_DQ_FUNC_CNTL = cpu_to_be32(mpll_dq_func_cntl); + mclk->vMPLL_DQ_FUNC_CNTL_2 = cpu_to_be32(mpll_dq_func_cntl_2); + mclk->vMCLK_PWRMGT_CNTL = cpu_to_be32(mclk_pwrmgt_cntl); + mclk->vDLL_CNTL = cpu_to_be32(dll_cntl); + mclk->vMPLL_SS = cpu_to_be32(mpll_ss1); + mclk->vMPLL_SS2 = cpu_to_be32(mpll_ss2); + + return 0; +} + +static void ni_populate_smc_sp(struct radeon_device *rdev, + struct radeon_ps *radeon_state, + NISLANDS_SMC_SWSTATE *smc_state) +{ + struct ni_ps *ps = ni_get_ps(radeon_state); + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + int i; + + for (i = 0; i < ps->performance_level_count - 1; i++) + smc_state->levels[i].bSP = cpu_to_be32(pi->dsp); + + smc_state->levels[ps->performance_level_count - 1].bSP = + cpu_to_be32(pi->psp); +} + +static int ni_convert_power_level_to_smc(struct radeon_device *rdev, + struct rv7xx_pl *pl, + NISLANDS_SMC_HW_PERFORMANCE_LEVEL *level) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct ni_power_info *ni_pi = ni_get_pi(rdev); + int ret; + bool dll_state_on; + u16 std_vddc; + u32 tmp = RREG32(DC_STUTTER_CNTL); + + level->gen2PCIE = pi->pcie_gen2 ? + ((pl->flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) ? 1 : 0) : 0; + + ret = ni_populate_sclk_value(rdev, pl->sclk, &level->sclk); + if (ret) + return ret; + + level->mcFlags = 0; + if (pi->mclk_stutter_mode_threshold && + (pl->mclk <= pi->mclk_stutter_mode_threshold) && + !eg_pi->uvd_enabled && + (tmp & DC_STUTTER_ENABLE_A) && + (tmp & DC_STUTTER_ENABLE_B)) + level->mcFlags |= NISLANDS_SMC_MC_STUTTER_EN; + + if (pi->mem_gddr5) { + if (pl->mclk > pi->mclk_edc_enable_threshold) + level->mcFlags |= NISLANDS_SMC_MC_EDC_RD_FLAG; + if (pl->mclk > eg_pi->mclk_edc_wr_enable_threshold) + level->mcFlags |= NISLANDS_SMC_MC_EDC_WR_FLAG; + + level->strobeMode = cypress_get_strobe_mode_settings(rdev, pl->mclk); + + if (level->strobeMode & NISLANDS_SMC_STROBE_ENABLE) { + if (cypress_get_mclk_frequency_ratio(rdev, pl->mclk, true) >= + ((RREG32(MC_SEQ_MISC7) >> 16) & 0xf)) + dll_state_on = ((RREG32(MC_SEQ_MISC5) >> 1) & 0x1) ? true : false; + else + dll_state_on = ((RREG32(MC_SEQ_MISC6) >> 1) & 0x1) ? true : false; + } else { + dll_state_on = false; + if (pl->mclk > ni_pi->mclk_rtt_mode_threshold) + level->mcFlags |= NISLANDS_SMC_MC_RTT_ENABLE; + } + + ret = ni_populate_mclk_value(rdev, pl->sclk, pl->mclk, + &level->mclk, + (level->strobeMode & NISLANDS_SMC_STROBE_ENABLE) != 0, + dll_state_on); + } else + ret = ni_populate_mclk_value(rdev, pl->sclk, pl->mclk, &level->mclk, 1, 1); + + if (ret) + return ret; + + ret = ni_populate_voltage_value(rdev, &eg_pi->vddc_voltage_table, + pl->vddc, &level->vddc); + if (ret) + return ret; + + ret = ni_get_std_voltage_value(rdev, &level->vddc, &std_vddc); + if (ret) + return ret; + + ni_populate_std_voltage_value(rdev, std_vddc, + level->vddc.index, &level->std_vddc); + + if (eg_pi->vddci_control) { + ret = ni_populate_voltage_value(rdev, &eg_pi->vddci_voltage_table, + pl->vddci, &level->vddci); + if (ret) + return ret; + } + + ni_populate_mvdd_value(rdev, pl->mclk, &level->mvdd); + + return ret; +} + +static int ni_populate_smc_t(struct radeon_device *rdev, + struct radeon_ps *radeon_state, + NISLANDS_SMC_SWSTATE *smc_state) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct ni_ps *state = ni_get_ps(radeon_state); + u32 a_t; + u32 t_l, t_h; + u32 high_bsp; + int i, ret; + + if (state->performance_level_count >= 9) + return -EINVAL; + + if (state->performance_level_count < 2) { + a_t = CG_R(0xffff) | CG_L(0); + smc_state->levels[0].aT = cpu_to_be32(a_t); + return 0; + } + + smc_state->levels[0].aT = cpu_to_be32(0); + + for (i = 0; i <= state->performance_level_count - 2; i++) { + if (eg_pi->uvd_enabled) + ret = r600_calculate_at( + 1000 * (i * (eg_pi->smu_uvd_hs ? 2 : 8) + 2), + 100 * R600_AH_DFLT, + state->performance_levels[i + 1].sclk, + state->performance_levels[i].sclk, + &t_l, + &t_h); + else + ret = r600_calculate_at( + 1000 * (i + 1), + 100 * R600_AH_DFLT, + state->performance_levels[i + 1].sclk, + state->performance_levels[i].sclk, + &t_l, + &t_h); + + if (ret) { + t_h = (i + 1) * 1000 - 50 * R600_AH_DFLT; + t_l = (i + 1) * 1000 + 50 * R600_AH_DFLT; + } + + a_t = be32_to_cpu(smc_state->levels[i].aT) & ~CG_R_MASK; + a_t |= CG_R(t_l * pi->bsp / 20000); + smc_state->levels[i].aT = cpu_to_be32(a_t); + + high_bsp = (i == state->performance_level_count - 2) ? + pi->pbsp : pi->bsp; + + a_t = CG_R(0xffff) | CG_L(t_h * high_bsp / 20000); + smc_state->levels[i + 1].aT = cpu_to_be32(a_t); + } + + return 0; +} + +static int ni_populate_power_containment_values(struct radeon_device *rdev, + struct radeon_ps *radeon_state, + NISLANDS_SMC_SWSTATE *smc_state) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct ni_power_info *ni_pi = ni_get_pi(rdev); + struct ni_ps *state = ni_get_ps(radeon_state); + u32 prev_sclk; + u32 max_sclk; + u32 min_sclk; + int i, ret; + u32 tdp_limit; + u32 near_tdp_limit; + u32 power_boost_limit; + u8 max_ps_percent; + + if (ni_pi->enable_power_containment == false) + return 0; + + if (state->performance_level_count == 0) + return -EINVAL; + + if (smc_state->levelCount != state->performance_level_count) + return -EINVAL; + + ret = ni_calculate_adjusted_tdp_limits(rdev, + false, /* ??? */ + rdev->pm.dpm.tdp_adjustment, + &tdp_limit, + &near_tdp_limit); + if (ret) + return ret; + + power_boost_limit = ni_calculate_power_boost_limit(rdev, radeon_state, near_tdp_limit); + + ret = rv770_write_smc_sram_dword(rdev, + pi->state_table_start + + offsetof(NISLANDS_SMC_STATETABLE, dpm2Params) + + offsetof(PP_NIslands_DPM2Parameters, PowerBoostLimit), + ni_scale_power_for_smc(power_boost_limit, ni_get_smc_power_scaling_factor(rdev)), + pi->sram_end); + if (ret) + power_boost_limit = 0; + + smc_state->levels[0].dpm2.MaxPS = 0; + smc_state->levels[0].dpm2.NearTDPDec = 0; + smc_state->levels[0].dpm2.AboveSafeInc = 0; + smc_state->levels[0].dpm2.BelowSafeInc = 0; + smc_state->levels[0].stateFlags |= power_boost_limit ? PPSMC_STATEFLAG_POWERBOOST : 0; + + for (i = 1; i < state->performance_level_count; i++) { + prev_sclk = state->performance_levels[i-1].sclk; + max_sclk = state->performance_levels[i].sclk; + max_ps_percent = (i != (state->performance_level_count - 1)) ? + NISLANDS_DPM2_MAXPS_PERCENT_M : NISLANDS_DPM2_MAXPS_PERCENT_H; + + if (max_sclk < prev_sclk) + return -EINVAL; + + if ((max_ps_percent == 0) || (prev_sclk == max_sclk) || eg_pi->uvd_enabled) + min_sclk = max_sclk; + else if (1 == i) + min_sclk = prev_sclk; + else + min_sclk = (prev_sclk * (u32)max_ps_percent) / 100; + + if (min_sclk < state->performance_levels[0].sclk) + min_sclk = state->performance_levels[0].sclk; + + if (min_sclk == 0) + return -EINVAL; + + smc_state->levels[i].dpm2.MaxPS = + (u8)((NISLANDS_DPM2_MAX_PULSE_SKIP * (max_sclk - min_sclk)) / max_sclk); + smc_state->levels[i].dpm2.NearTDPDec = NISLANDS_DPM2_NEAR_TDP_DEC; + smc_state->levels[i].dpm2.AboveSafeInc = NISLANDS_DPM2_ABOVE_SAFE_INC; + smc_state->levels[i].dpm2.BelowSafeInc = NISLANDS_DPM2_BELOW_SAFE_INC; + smc_state->levels[i].stateFlags |= + ((i != (state->performance_level_count - 1)) && power_boost_limit) ? + PPSMC_STATEFLAG_POWERBOOST : 0; + } + + return 0; +} + +static int ni_populate_sq_ramping_values(struct radeon_device *rdev, + struct radeon_ps *radeon_state, + NISLANDS_SMC_SWSTATE *smc_state) +{ + struct ni_power_info *ni_pi = ni_get_pi(rdev); + struct ni_ps *state = ni_get_ps(radeon_state); + u32 sq_power_throttle; + u32 sq_power_throttle2; + bool enable_sq_ramping = ni_pi->enable_sq_ramping; + int i; + + if (state->performance_level_count == 0) + return -EINVAL; + + if (smc_state->levelCount != state->performance_level_count) + return -EINVAL; + + if (rdev->pm.dpm.sq_ramping_threshold == 0) + return -EINVAL; + + if (NISLANDS_DPM2_SQ_RAMP_MAX_POWER > (MAX_POWER_MASK >> MAX_POWER_SHIFT)) + enable_sq_ramping = false; + + if (NISLANDS_DPM2_SQ_RAMP_MIN_POWER > (MIN_POWER_MASK >> MIN_POWER_SHIFT)) + enable_sq_ramping = false; + + if (NISLANDS_DPM2_SQ_RAMP_MAX_POWER_DELTA > (MAX_POWER_DELTA_MASK >> MAX_POWER_DELTA_SHIFT)) + enable_sq_ramping = false; + + if (NISLANDS_DPM2_SQ_RAMP_STI_SIZE > (STI_SIZE_MASK >> STI_SIZE_SHIFT)) + enable_sq_ramping = false; + + if (NISLANDS_DPM2_SQ_RAMP_LTI_RATIO <= (LTI_RATIO_MASK >> LTI_RATIO_SHIFT)) + enable_sq_ramping = false; + + for (i = 0; i < state->performance_level_count; i++) { + sq_power_throttle = 0; + sq_power_throttle2 = 0; + + if ((state->performance_levels[i].sclk >= rdev->pm.dpm.sq_ramping_threshold) && + enable_sq_ramping) { + sq_power_throttle |= MAX_POWER(NISLANDS_DPM2_SQ_RAMP_MAX_POWER); + sq_power_throttle |= MIN_POWER(NISLANDS_DPM2_SQ_RAMP_MIN_POWER); + sq_power_throttle2 |= MAX_POWER_DELTA(NISLANDS_DPM2_SQ_RAMP_MAX_POWER_DELTA); + sq_power_throttle2 |= STI_SIZE(NISLANDS_DPM2_SQ_RAMP_STI_SIZE); + sq_power_throttle2 |= LTI_RATIO(NISLANDS_DPM2_SQ_RAMP_LTI_RATIO); + } else { + sq_power_throttle |= MAX_POWER_MASK | MIN_POWER_MASK; + sq_power_throttle2 |= MAX_POWER_DELTA_MASK | STI_SIZE_MASK | LTI_RATIO_MASK; + } + + smc_state->levels[i].SQPowerThrottle = cpu_to_be32(sq_power_throttle); + smc_state->levels[i].SQPowerThrottle_2 = cpu_to_be32(sq_power_throttle2); + } + + return 0; +} + +static int ni_enable_power_containment(struct radeon_device *rdev, bool enable) +{ + struct ni_power_info *ni_pi = ni_get_pi(rdev); + PPSMC_Result smc_result; + int ret = 0; + + if (ni_pi->enable_power_containment) { + if (enable) { + struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; + + if (!r600_is_uvd_state(radeon_new_state->class, radeon_new_state->class2)) { + smc_result = rv770_send_msg_to_smc(rdev, PPSMC_TDPClampingActive); + if (smc_result != PPSMC_Result_OK) { + ret = -EINVAL; + ni_pi->pc_enabled = false; + } else { + ni_pi->pc_enabled = true; + } + } + } else { + smc_result = rv770_send_msg_to_smc(rdev, PPSMC_TDPClampingInactive); + if (smc_result != PPSMC_Result_OK) + ret = -EINVAL; + ni_pi->pc_enabled = false; + } + } + + return ret; +} + +static int ni_convert_power_state_to_smc(struct radeon_device *rdev, + struct radeon_ps *radeon_state, + NISLANDS_SMC_SWSTATE *smc_state) +{ + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct ni_power_info *ni_pi = ni_get_pi(rdev); + struct ni_ps *state = ni_get_ps(radeon_state); + int i, ret; + u32 threshold = state->performance_levels[state->performance_level_count - 1].sclk * 100 / 100; + + if (!(radeon_state->caps & ATOM_PPLIB_DISALLOW_ON_DC)) + smc_state->flags |= PPSMC_SWSTATE_FLAG_DC; + + smc_state->levelCount = 0; + + if (state->performance_level_count > NISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE) + return -EINVAL; + + for (i = 0; i < state->performance_level_count; i++) { + ret = ni_convert_power_level_to_smc(rdev, &state->performance_levels[i], + &smc_state->levels[i]); + smc_state->levels[i].arbRefreshState = + (u8)(NISLANDS_DRIVER_STATE_ARB_INDEX + i); + + if (ret) + return ret; + + if (ni_pi->enable_power_containment) + smc_state->levels[i].displayWatermark = + (state->performance_levels[i].sclk < threshold) ? + PPSMC_DISPLAY_WATERMARK_LOW : PPSMC_DISPLAY_WATERMARK_HIGH; + else + smc_state->levels[i].displayWatermark = (i < 2) ? + PPSMC_DISPLAY_WATERMARK_LOW : PPSMC_DISPLAY_WATERMARK_HIGH; + + if (eg_pi->dynamic_ac_timing) + smc_state->levels[i].ACIndex = NISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT + i; + else + smc_state->levels[i].ACIndex = 0; + + smc_state->levelCount++; + } + + rv770_write_smc_soft_register(rdev, NI_SMC_SOFT_REGISTER_watermark_threshold, + cpu_to_be32(threshold / 512)); + + ni_populate_smc_sp(rdev, radeon_state, smc_state); + + ret = ni_populate_power_containment_values(rdev, radeon_state, smc_state); + if (ret) + ni_pi->enable_power_containment = false; + + ret = ni_populate_sq_ramping_values(rdev, radeon_state, smc_state); + if (ret) + ni_pi->enable_sq_ramping = false; + + return ni_populate_smc_t(rdev, radeon_state, smc_state); +} + +static int ni_upload_sw_state(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; + u16 address = pi->state_table_start + + offsetof(NISLANDS_SMC_STATETABLE, driverState); + u16 state_size = sizeof(NISLANDS_SMC_SWSTATE) + + ((NISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE - 1) * sizeof(NISLANDS_SMC_HW_PERFORMANCE_LEVEL)); + int ret; + NISLANDS_SMC_SWSTATE *smc_state = kzalloc(state_size, GFP_KERNEL); + + if (smc_state == NULL) + return -ENOMEM; + + ret = ni_convert_power_state_to_smc(rdev, radeon_new_state, smc_state); + if (ret) + goto done; + + ret = rv770_copy_bytes_to_smc(rdev, address, (u8 *)smc_state, state_size, pi->sram_end); + +done: + kfree(smc_state); + + return ret; +} + +static int ni_set_mc_special_registers(struct radeon_device *rdev, + struct ni_mc_reg_table *table) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + u8 i, j, k; + u32 temp_reg; + + for (i = 0, j = table->last; i < table->last; i++) { + switch (table->mc_reg_address[i].s1) { + case MC_SEQ_MISC1 >> 2: + if (j >= SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE) + return -EINVAL; + temp_reg = RREG32(MC_PMG_CMD_EMRS); + table->mc_reg_address[j].s1 = MC_PMG_CMD_EMRS >> 2; + table->mc_reg_address[j].s0 = MC_SEQ_PMG_CMD_EMRS_LP >> 2; + for (k = 0; k < table->num_entries; k++) + table->mc_reg_table_entry[k].mc_data[j] = + ((temp_reg & 0xffff0000)) | + ((table->mc_reg_table_entry[k].mc_data[i] & 0xffff0000) >> 16); + j++; + if (j >= SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE) + return -EINVAL; + + temp_reg = RREG32(MC_PMG_CMD_MRS); + table->mc_reg_address[j].s1 = MC_PMG_CMD_MRS >> 2; + table->mc_reg_address[j].s0 = MC_SEQ_PMG_CMD_MRS_LP >> 2; + for(k = 0; k < table->num_entries; k++) { + table->mc_reg_table_entry[k].mc_data[j] = + (temp_reg & 0xffff0000) | + (table->mc_reg_table_entry[k].mc_data[i] & 0x0000ffff); + if (!pi->mem_gddr5) + table->mc_reg_table_entry[k].mc_data[j] |= 0x100; + } + j++; + if (j > SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE) + return -EINVAL; + break; + case MC_SEQ_RESERVE_M >> 2: + temp_reg = RREG32(MC_PMG_CMD_MRS1); + table->mc_reg_address[j].s1 = MC_PMG_CMD_MRS1 >> 2; + table->mc_reg_address[j].s0 = MC_SEQ_PMG_CMD_MRS1_LP >> 2; + for (k = 0; k < table->num_entries; k++) + table->mc_reg_table_entry[k].mc_data[j] = + (temp_reg & 0xffff0000) | + (table->mc_reg_table_entry[k].mc_data[i] & 0x0000ffff); + j++; + if (j > SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE) + return -EINVAL; + break; + default: + break; + } + } + + table->last = j; + + return 0; +} + +static bool ni_check_s0_mc_reg_index(u16 in_reg, u16 *out_reg) +{ + bool result = true; + + switch (in_reg) { + case MC_SEQ_RAS_TIMING >> 2: + *out_reg = MC_SEQ_RAS_TIMING_LP >> 2; + break; + case MC_SEQ_CAS_TIMING >> 2: + *out_reg = MC_SEQ_CAS_TIMING_LP >> 2; + break; + case MC_SEQ_MISC_TIMING >> 2: + *out_reg = MC_SEQ_MISC_TIMING_LP >> 2; + break; + case MC_SEQ_MISC_TIMING2 >> 2: + *out_reg = MC_SEQ_MISC_TIMING2_LP >> 2; + break; + case MC_SEQ_RD_CTL_D0 >> 2: + *out_reg = MC_SEQ_RD_CTL_D0_LP >> 2; + break; + case MC_SEQ_RD_CTL_D1 >> 2: + *out_reg = MC_SEQ_RD_CTL_D1_LP >> 2; + break; + case MC_SEQ_WR_CTL_D0 >> 2: + *out_reg = MC_SEQ_WR_CTL_D0_LP >> 2; + break; + case MC_SEQ_WR_CTL_D1 >> 2: + *out_reg = MC_SEQ_WR_CTL_D1_LP >> 2; + break; + case MC_PMG_CMD_EMRS >> 2: + *out_reg = MC_SEQ_PMG_CMD_EMRS_LP >> 2; + break; + case MC_PMG_CMD_MRS >> 2: + *out_reg = MC_SEQ_PMG_CMD_MRS_LP >> 2; + break; + case MC_PMG_CMD_MRS1 >> 2: + *out_reg = MC_SEQ_PMG_CMD_MRS1_LP >> 2; + break; + case MC_SEQ_PMG_TIMING >> 2: + *out_reg = MC_SEQ_PMG_TIMING_LP >> 2; + break; + case MC_PMG_CMD_MRS2 >> 2: + *out_reg = MC_SEQ_PMG_CMD_MRS2_LP >> 2; + break; + default: + result = false; + break; + } + + return result; +} + +static void ni_set_valid_flag(struct ni_mc_reg_table *table) +{ + u8 i, j; + + for (i = 0; i < table->last; i++) { + for (j = 1; j < table->num_entries; j++) { + if (table->mc_reg_table_entry[j-1].mc_data[i] != table->mc_reg_table_entry[j].mc_data[i]) { + table->valid_flag |= 1 << i; + break; + } + } + } +} + +static void ni_set_s0_mc_reg_index(struct ni_mc_reg_table *table) +{ + u32 i; + u16 address; + + for (i = 0; i < table->last; i++) + table->mc_reg_address[i].s0 = + ni_check_s0_mc_reg_index(table->mc_reg_address[i].s1, &address) ? + address : table->mc_reg_address[i].s1; +} + +static int ni_copy_vbios_mc_reg_table(struct atom_mc_reg_table *table, + struct ni_mc_reg_table *ni_table) +{ + u8 i, j; + + if (table->last > SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE) + return -EINVAL; + if (table->num_entries > MAX_AC_TIMING_ENTRIES) + return -EINVAL; + + for (i = 0; i < table->last; i++) + ni_table->mc_reg_address[i].s1 = table->mc_reg_address[i].s1; + ni_table->last = table->last; + + for (i = 0; i < table->num_entries; i++) { + ni_table->mc_reg_table_entry[i].mclk_max = + table->mc_reg_table_entry[i].mclk_max; + for (j = 0; j < table->last; j++) + ni_table->mc_reg_table_entry[i].mc_data[j] = + table->mc_reg_table_entry[i].mc_data[j]; + } + ni_table->num_entries = table->num_entries; + + return 0; +} + +static int ni_initialize_mc_reg_table(struct radeon_device *rdev) +{ + struct ni_power_info *ni_pi = ni_get_pi(rdev); + int ret; + struct atom_mc_reg_table *table; + struct ni_mc_reg_table *ni_table = &ni_pi->mc_reg_table; + u8 module_index = rv770_get_memory_module_index(rdev); + + table = kzalloc(sizeof(struct atom_mc_reg_table), GFP_KERNEL); + if (!table) + return -ENOMEM; + + WREG32(MC_SEQ_RAS_TIMING_LP, RREG32(MC_SEQ_RAS_TIMING)); + WREG32(MC_SEQ_CAS_TIMING_LP, RREG32(MC_SEQ_CAS_TIMING)); + WREG32(MC_SEQ_MISC_TIMING_LP, RREG32(MC_SEQ_MISC_TIMING)); + WREG32(MC_SEQ_MISC_TIMING2_LP, RREG32(MC_SEQ_MISC_TIMING2)); + WREG32(MC_SEQ_PMG_CMD_EMRS_LP, RREG32(MC_PMG_CMD_EMRS)); + WREG32(MC_SEQ_PMG_CMD_MRS_LP, RREG32(MC_PMG_CMD_MRS)); + WREG32(MC_SEQ_PMG_CMD_MRS1_LP, RREG32(MC_PMG_CMD_MRS1)); + WREG32(MC_SEQ_WR_CTL_D0_LP, RREG32(MC_SEQ_WR_CTL_D0)); + WREG32(MC_SEQ_WR_CTL_D1_LP, RREG32(MC_SEQ_WR_CTL_D1)); + WREG32(MC_SEQ_RD_CTL_D0_LP, RREG32(MC_SEQ_RD_CTL_D0)); + WREG32(MC_SEQ_RD_CTL_D1_LP, RREG32(MC_SEQ_RD_CTL_D1)); + WREG32(MC_SEQ_PMG_TIMING_LP, RREG32(MC_SEQ_PMG_TIMING)); + WREG32(MC_SEQ_PMG_CMD_MRS2_LP, RREG32(MC_PMG_CMD_MRS2)); + + ret = radeon_atom_init_mc_reg_table(rdev, module_index, table); + + if (ret) + goto init_mc_done; + + ret = ni_copy_vbios_mc_reg_table(table, ni_table); + + if (ret) + goto init_mc_done; + + ni_set_s0_mc_reg_index(ni_table); + + ret = ni_set_mc_special_registers(rdev, ni_table); + + if (ret) + goto init_mc_done; + + ni_set_valid_flag(ni_table); + +init_mc_done: + kfree(table); + + return ret; +} + +static void ni_populate_mc_reg_addresses(struct radeon_device *rdev, + SMC_NIslands_MCRegisters *mc_reg_table) +{ + struct ni_power_info *ni_pi = ni_get_pi(rdev); + u32 i, j; + + for (i = 0, j = 0; j < ni_pi->mc_reg_table.last; j++) { + if (ni_pi->mc_reg_table.valid_flag & (1 << j)) { + if (i >= SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE) + break; + mc_reg_table->address[i].s0 = + cpu_to_be16(ni_pi->mc_reg_table.mc_reg_address[j].s0); + mc_reg_table->address[i].s1 = + cpu_to_be16(ni_pi->mc_reg_table.mc_reg_address[j].s1); + i++; + } + } + mc_reg_table->last = (u8)i; +} + + +static void ni_convert_mc_registers(struct ni_mc_reg_entry *entry, + SMC_NIslands_MCRegisterSet *data, + u32 num_entries, u32 valid_flag) +{ + u32 i, j; + + for (i = 0, j = 0; j < num_entries; j++) { + if (valid_flag & (1 << j)) { + data->value[i] = cpu_to_be32(entry->mc_data[j]); + i++; + } + } +} + +static void ni_convert_mc_reg_table_entry_to_smc(struct radeon_device *rdev, + struct rv7xx_pl *pl, + SMC_NIslands_MCRegisterSet *mc_reg_table_data) +{ + struct ni_power_info *ni_pi = ni_get_pi(rdev); + u32 i = 0; + + for (i = 0; i < ni_pi->mc_reg_table.num_entries; i++) { + if (pl->mclk <= ni_pi->mc_reg_table.mc_reg_table_entry[i].mclk_max) + break; + } + + if ((i == ni_pi->mc_reg_table.num_entries) && (i > 0)) + --i; + + ni_convert_mc_registers(&ni_pi->mc_reg_table.mc_reg_table_entry[i], + mc_reg_table_data, + ni_pi->mc_reg_table.last, + ni_pi->mc_reg_table.valid_flag); +} + +static void ni_convert_mc_reg_table_to_smc(struct radeon_device *rdev, + struct radeon_ps *radeon_state, + SMC_NIslands_MCRegisters *mc_reg_table) +{ + struct ni_ps *state = ni_get_ps(radeon_state); + int i; + + for (i = 0; i < state->performance_level_count; i++) { + ni_convert_mc_reg_table_entry_to_smc(rdev, + &state->performance_levels[i], + &mc_reg_table->data[NISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT + i]); + } +} + +static int ni_populate_mc_reg_table(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct ni_power_info *ni_pi = ni_get_pi(rdev); + struct radeon_ps *radeon_boot_state = rdev->pm.dpm.boot_ps; + struct ni_ps *boot_state = ni_get_ps(radeon_boot_state); + SMC_NIslands_MCRegisters *mc_reg_table = &ni_pi->smc_mc_reg_table; + + memset(mc_reg_table, 0, sizeof(SMC_NIslands_MCRegisters)); + + rv770_write_smc_soft_register(rdev, NI_SMC_SOFT_REGISTER_seq_index, 1); + + ni_populate_mc_reg_addresses(rdev, mc_reg_table); + + ni_convert_mc_reg_table_entry_to_smc(rdev, &boot_state->performance_levels[0], + &mc_reg_table->data[0]); + + ni_convert_mc_registers(&ni_pi->mc_reg_table.mc_reg_table_entry[0], + &mc_reg_table->data[1], + ni_pi->mc_reg_table.last, + ni_pi->mc_reg_table.valid_flag); + + ni_convert_mc_reg_table_to_smc(rdev, radeon_boot_state, mc_reg_table); + + return rv770_copy_bytes_to_smc(rdev, eg_pi->mc_reg_table_start, + (u8 *)mc_reg_table, + sizeof(SMC_NIslands_MCRegisters), + pi->sram_end); +} + +static int ni_upload_mc_reg_table(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct ni_power_info *ni_pi = ni_get_pi(rdev); + struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; + struct ni_ps *ni_new_state = ni_get_ps(radeon_new_state); + SMC_NIslands_MCRegisters *mc_reg_table = &ni_pi->smc_mc_reg_table; + u16 address; + + memset(mc_reg_table, 0, sizeof(SMC_NIslands_MCRegisters)); + + ni_convert_mc_reg_table_to_smc(rdev, radeon_new_state, mc_reg_table); + + address = eg_pi->mc_reg_table_start + + (u16)offsetof(SMC_NIslands_MCRegisters, data[NISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT]); + + return rv770_copy_bytes_to_smc(rdev, address, + (u8 *)&mc_reg_table->data[NISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT], + sizeof(SMC_NIslands_MCRegisterSet) * ni_new_state->performance_level_count, + pi->sram_end); +} + +static int ni_init_driver_calculated_leakage_table(struct radeon_device *rdev, + PP_NIslands_CACTABLES *cac_tables) +{ + struct ni_power_info *ni_pi = ni_get_pi(rdev); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + u32 leakage = 0; + unsigned int i, j, table_size; + s32 t; + u32 smc_leakage, max_leakage = 0; + u32 scaling_factor; + + table_size = eg_pi->vddc_voltage_table.count; + + if (SMC_NISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES < table_size) + table_size = SMC_NISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES; + + scaling_factor = ni_get_smc_power_scaling_factor(rdev); + + for (i = 0; i < SMC_NISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES; i++) { + for (j = 0; j < table_size; j++) { + t = (1000 * ((i + 1) * 8)); + + if (t < ni_pi->cac_data.leakage_minimum_temperature) + t = ni_pi->cac_data.leakage_minimum_temperature; + + ni_calculate_leakage_for_v_and_t(rdev, + &ni_pi->cac_data.leakage_coefficients, + eg_pi->vddc_voltage_table.entries[j].value, + t, + ni_pi->cac_data.i_leakage, + &leakage); + + smc_leakage = ni_scale_power_for_smc(leakage, scaling_factor) / 1000; + if (smc_leakage > max_leakage) + max_leakage = smc_leakage; + + cac_tables->cac_lkge_lut[i][j] = cpu_to_be32(smc_leakage); + } + } + + for (j = table_size; j < SMC_NISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES; j++) { + for (i = 0; i < SMC_NISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES; i++) + cac_tables->cac_lkge_lut[i][j] = cpu_to_be32(max_leakage); + } + return 0; +} + +static int ni_init_simplified_leakage_table(struct radeon_device *rdev, + PP_NIslands_CACTABLES *cac_tables) +{ + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct radeon_cac_leakage_table *leakage_table = + &rdev->pm.dpm.dyn_state.cac_leakage_table; + u32 i, j, table_size; + u32 smc_leakage, max_leakage = 0; + u32 scaling_factor; + + if (!leakage_table) + return -EINVAL; + + table_size = leakage_table->count; + + if (eg_pi->vddc_voltage_table.count != table_size) + table_size = (eg_pi->vddc_voltage_table.count < leakage_table->count) ? + eg_pi->vddc_voltage_table.count : leakage_table->count; + + if (SMC_NISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES < table_size) + table_size = SMC_NISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES; + + if (table_size == 0) + return -EINVAL; + + scaling_factor = ni_get_smc_power_scaling_factor(rdev); + + for (j = 0; j < table_size; j++) { + smc_leakage = leakage_table->entries[j].leakage; + + if (smc_leakage > max_leakage) + max_leakage = smc_leakage; + + for (i = 0; i < SMC_NISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES; i++) + cac_tables->cac_lkge_lut[i][j] = + cpu_to_be32(ni_scale_power_for_smc(smc_leakage, scaling_factor)); + } + + for (j = table_size; j < SMC_NISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES; j++) { + for (i = 0; i < SMC_NISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES; i++) + cac_tables->cac_lkge_lut[i][j] = + cpu_to_be32(ni_scale_power_for_smc(max_leakage, scaling_factor)); + } + return 0; +} + +static int ni_initialize_smc_cac_tables(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct ni_power_info *ni_pi = ni_get_pi(rdev); + PP_NIslands_CACTABLES *cac_tables = NULL; + int i, ret; + u32 reg; + + if (ni_pi->enable_cac == false) + return 0; + + cac_tables = kzalloc(sizeof(PP_NIslands_CACTABLES), GFP_KERNEL); + if (!cac_tables) + return -ENOMEM; + + reg = RREG32(CG_CAC_CTRL) & ~(TID_CNT_MASK | TID_UNIT_MASK); + reg |= (TID_CNT(ni_pi->cac_weights->tid_cnt) | + TID_UNIT(ni_pi->cac_weights->tid_unit)); + WREG32(CG_CAC_CTRL, reg); + + for (i = 0; i < NISLANDS_DCCAC_MAX_LEVELS; i++) + ni_pi->dc_cac_table[i] = ni_pi->cac_weights->dc_cac[i]; + + for (i = 0; i < SMC_NISLANDS_BIF_LUT_NUM_OF_ENTRIES; i++) + cac_tables->cac_bif_lut[i] = ni_pi->cac_weights->pcie_cac[i]; + + ni_pi->cac_data.i_leakage = rdev->pm.dpm.cac_leakage; + ni_pi->cac_data.pwr_const = 0; + ni_pi->cac_data.dc_cac_value = ni_pi->dc_cac_table[NISLANDS_DCCAC_LEVEL_0]; + ni_pi->cac_data.bif_cac_value = 0; + ni_pi->cac_data.mc_wr_weight = ni_pi->cac_weights->mc_write_weight; + ni_pi->cac_data.mc_rd_weight = ni_pi->cac_weights->mc_read_weight; + ni_pi->cac_data.allow_ovrflw = 0; + ni_pi->cac_data.l2num_win_tdp = ni_pi->lta_window_size; + ni_pi->cac_data.num_win_tdp = 0; + ni_pi->cac_data.lts_truncate_n = ni_pi->lts_truncate; + + if (ni_pi->driver_calculate_cac_leakage) + ret = ni_init_driver_calculated_leakage_table(rdev, cac_tables); + else + ret = ni_init_simplified_leakage_table(rdev, cac_tables); + + if (ret) + goto done_free; + + cac_tables->pwr_const = cpu_to_be32(ni_pi->cac_data.pwr_const); + cac_tables->dc_cacValue = cpu_to_be32(ni_pi->cac_data.dc_cac_value); + cac_tables->bif_cacValue = cpu_to_be32(ni_pi->cac_data.bif_cac_value); + cac_tables->AllowOvrflw = ni_pi->cac_data.allow_ovrflw; + cac_tables->MCWrWeight = ni_pi->cac_data.mc_wr_weight; + cac_tables->MCRdWeight = ni_pi->cac_data.mc_rd_weight; + cac_tables->numWin_TDP = ni_pi->cac_data.num_win_tdp; + cac_tables->l2numWin_TDP = ni_pi->cac_data.l2num_win_tdp; + cac_tables->lts_truncate_n = ni_pi->cac_data.lts_truncate_n; + + ret = rv770_copy_bytes_to_smc(rdev, ni_pi->cac_table_start, (u8 *)cac_tables, + sizeof(PP_NIslands_CACTABLES), pi->sram_end); + +done_free: + if (ret) { + ni_pi->enable_cac = false; + ni_pi->enable_power_containment = false; + } + + kfree(cac_tables); + + return 0; +} + +static int ni_initialize_hardware_cac_manager(struct radeon_device *rdev) +{ + struct ni_power_info *ni_pi = ni_get_pi(rdev); + u32 reg; + + if (!ni_pi->enable_cac || + !ni_pi->cac_configuration_required) + return 0; + + if (ni_pi->cac_weights == NULL) + return -EINVAL; + + reg = RREG32_CG(CG_CAC_REGION_1_WEIGHT_0) & ~(WEIGHT_TCP_SIG0_MASK | + WEIGHT_TCP_SIG1_MASK | + WEIGHT_TA_SIG_MASK); + reg |= (WEIGHT_TCP_SIG0(ni_pi->cac_weights->weight_tcp_sig0) | + WEIGHT_TCP_SIG1(ni_pi->cac_weights->weight_tcp_sig1) | + WEIGHT_TA_SIG(ni_pi->cac_weights->weight_ta_sig)); + WREG32_CG(CG_CAC_REGION_1_WEIGHT_0, reg); + + reg = RREG32_CG(CG_CAC_REGION_1_WEIGHT_1) & ~(WEIGHT_TCC_EN0_MASK | + WEIGHT_TCC_EN1_MASK | + WEIGHT_TCC_EN2_MASK); + reg |= (WEIGHT_TCC_EN0(ni_pi->cac_weights->weight_tcc_en0) | + WEIGHT_TCC_EN1(ni_pi->cac_weights->weight_tcc_en1) | + WEIGHT_TCC_EN2(ni_pi->cac_weights->weight_tcc_en2)); + WREG32_CG(CG_CAC_REGION_1_WEIGHT_1, reg); + + reg = RREG32_CG(CG_CAC_REGION_2_WEIGHT_0) & ~(WEIGHT_CB_EN0_MASK | + WEIGHT_CB_EN1_MASK | + WEIGHT_CB_EN2_MASK | + WEIGHT_CB_EN3_MASK); + reg |= (WEIGHT_CB_EN0(ni_pi->cac_weights->weight_cb_en0) | + WEIGHT_CB_EN1(ni_pi->cac_weights->weight_cb_en1) | + WEIGHT_CB_EN2(ni_pi->cac_weights->weight_cb_en2) | + WEIGHT_CB_EN3(ni_pi->cac_weights->weight_cb_en3)); + WREG32_CG(CG_CAC_REGION_2_WEIGHT_0, reg); + + reg = RREG32_CG(CG_CAC_REGION_2_WEIGHT_1) & ~(WEIGHT_DB_SIG0_MASK | + WEIGHT_DB_SIG1_MASK | + WEIGHT_DB_SIG2_MASK | + WEIGHT_DB_SIG3_MASK); + reg |= (WEIGHT_DB_SIG0(ni_pi->cac_weights->weight_db_sig0) | + WEIGHT_DB_SIG1(ni_pi->cac_weights->weight_db_sig1) | + WEIGHT_DB_SIG2(ni_pi->cac_weights->weight_db_sig2) | + WEIGHT_DB_SIG3(ni_pi->cac_weights->weight_db_sig3)); + WREG32_CG(CG_CAC_REGION_2_WEIGHT_1, reg); + + reg = RREG32_CG(CG_CAC_REGION_2_WEIGHT_2) & ~(WEIGHT_SXM_SIG0_MASK | + WEIGHT_SXM_SIG1_MASK | + WEIGHT_SXM_SIG2_MASK | + WEIGHT_SXS_SIG0_MASK | + WEIGHT_SXS_SIG1_MASK); + reg |= (WEIGHT_SXM_SIG0(ni_pi->cac_weights->weight_sxm_sig0) | + WEIGHT_SXM_SIG1(ni_pi->cac_weights->weight_sxm_sig1) | + WEIGHT_SXM_SIG2(ni_pi->cac_weights->weight_sxm_sig2) | + WEIGHT_SXS_SIG0(ni_pi->cac_weights->weight_sxs_sig0) | + WEIGHT_SXS_SIG1(ni_pi->cac_weights->weight_sxs_sig1)); + WREG32_CG(CG_CAC_REGION_2_WEIGHT_2, reg); + + reg = RREG32_CG(CG_CAC_REGION_3_WEIGHT_0) & ~(WEIGHT_XBR_0_MASK | + WEIGHT_XBR_1_MASK | + WEIGHT_XBR_2_MASK | + WEIGHT_SPI_SIG0_MASK); + reg |= (WEIGHT_XBR_0(ni_pi->cac_weights->weight_xbr_0) | + WEIGHT_XBR_1(ni_pi->cac_weights->weight_xbr_1) | + WEIGHT_XBR_2(ni_pi->cac_weights->weight_xbr_2) | + WEIGHT_SPI_SIG0(ni_pi->cac_weights->weight_spi_sig0)); + WREG32_CG(CG_CAC_REGION_3_WEIGHT_0, reg); + + reg = RREG32_CG(CG_CAC_REGION_3_WEIGHT_1) & ~(WEIGHT_SPI_SIG1_MASK | + WEIGHT_SPI_SIG2_MASK | + WEIGHT_SPI_SIG3_MASK | + WEIGHT_SPI_SIG4_MASK | + WEIGHT_SPI_SIG5_MASK); + reg |= (WEIGHT_SPI_SIG1(ni_pi->cac_weights->weight_spi_sig1) | + WEIGHT_SPI_SIG2(ni_pi->cac_weights->weight_spi_sig2) | + WEIGHT_SPI_SIG3(ni_pi->cac_weights->weight_spi_sig3) | + WEIGHT_SPI_SIG4(ni_pi->cac_weights->weight_spi_sig4) | + WEIGHT_SPI_SIG5(ni_pi->cac_weights->weight_spi_sig5)); + WREG32_CG(CG_CAC_REGION_3_WEIGHT_1, reg); + + reg = RREG32_CG(CG_CAC_REGION_4_WEIGHT_0) & ~(WEIGHT_LDS_SIG0_MASK | + WEIGHT_LDS_SIG1_MASK | + WEIGHT_SC_MASK); + reg |= (WEIGHT_LDS_SIG0(ni_pi->cac_weights->weight_lds_sig0) | + WEIGHT_LDS_SIG1(ni_pi->cac_weights->weight_lds_sig1) | + WEIGHT_SC(ni_pi->cac_weights->weight_sc)); + WREG32_CG(CG_CAC_REGION_4_WEIGHT_0, reg); + + reg = RREG32_CG(CG_CAC_REGION_4_WEIGHT_1) & ~(WEIGHT_BIF_MASK | + WEIGHT_CP_MASK | + WEIGHT_PA_SIG0_MASK | + WEIGHT_PA_SIG1_MASK | + WEIGHT_VGT_SIG0_MASK); + reg |= (WEIGHT_BIF(ni_pi->cac_weights->weight_bif) | + WEIGHT_CP(ni_pi->cac_weights->weight_cp) | + WEIGHT_PA_SIG0(ni_pi->cac_weights->weight_pa_sig0) | + WEIGHT_PA_SIG1(ni_pi->cac_weights->weight_pa_sig1) | + WEIGHT_VGT_SIG0(ni_pi->cac_weights->weight_vgt_sig0)); + WREG32_CG(CG_CAC_REGION_4_WEIGHT_1, reg); + + reg = RREG32_CG(CG_CAC_REGION_4_WEIGHT_2) & ~(WEIGHT_VGT_SIG1_MASK | + WEIGHT_VGT_SIG2_MASK | + WEIGHT_DC_SIG0_MASK | + WEIGHT_DC_SIG1_MASK | + WEIGHT_DC_SIG2_MASK); + reg |= (WEIGHT_VGT_SIG1(ni_pi->cac_weights->weight_vgt_sig1) | + WEIGHT_VGT_SIG2(ni_pi->cac_weights->weight_vgt_sig2) | + WEIGHT_DC_SIG0(ni_pi->cac_weights->weight_dc_sig0) | + WEIGHT_DC_SIG1(ni_pi->cac_weights->weight_dc_sig1) | + WEIGHT_DC_SIG2(ni_pi->cac_weights->weight_dc_sig2)); + WREG32_CG(CG_CAC_REGION_4_WEIGHT_2, reg); + + reg = RREG32_CG(CG_CAC_REGION_4_WEIGHT_3) & ~(WEIGHT_DC_SIG3_MASK | + WEIGHT_UVD_SIG0_MASK | + WEIGHT_UVD_SIG1_MASK | + WEIGHT_SPARE0_MASK | + WEIGHT_SPARE1_MASK); + reg |= (WEIGHT_DC_SIG3(ni_pi->cac_weights->weight_dc_sig3) | + WEIGHT_UVD_SIG0(ni_pi->cac_weights->weight_uvd_sig0) | + WEIGHT_UVD_SIG1(ni_pi->cac_weights->weight_uvd_sig1) | + WEIGHT_SPARE0(ni_pi->cac_weights->weight_spare0) | + WEIGHT_SPARE1(ni_pi->cac_weights->weight_spare1)); + WREG32_CG(CG_CAC_REGION_4_WEIGHT_3, reg); + + reg = RREG32_CG(CG_CAC_REGION_5_WEIGHT_0) & ~(WEIGHT_SQ_VSP_MASK | + WEIGHT_SQ_VSP0_MASK); + reg |= (WEIGHT_SQ_VSP(ni_pi->cac_weights->weight_sq_vsp) | + WEIGHT_SQ_VSP0(ni_pi->cac_weights->weight_sq_vsp0)); + WREG32_CG(CG_CAC_REGION_5_WEIGHT_0, reg); + + reg = RREG32_CG(CG_CAC_REGION_5_WEIGHT_1) & ~(WEIGHT_SQ_GPR_MASK); + reg |= WEIGHT_SQ_GPR(ni_pi->cac_weights->weight_sq_gpr); + WREG32_CG(CG_CAC_REGION_5_WEIGHT_1, reg); + + reg = RREG32_CG(CG_CAC_REGION_4_OVERRIDE_4) & ~(OVR_MODE_SPARE_0_MASK | + OVR_VAL_SPARE_0_MASK | + OVR_MODE_SPARE_1_MASK | + OVR_VAL_SPARE_1_MASK); + reg |= (OVR_MODE_SPARE_0(ni_pi->cac_weights->ovr_mode_spare_0) | + OVR_VAL_SPARE_0(ni_pi->cac_weights->ovr_val_spare_0) | + OVR_MODE_SPARE_1(ni_pi->cac_weights->ovr_mode_spare_1) | + OVR_VAL_SPARE_1(ni_pi->cac_weights->ovr_val_spare_1)); + WREG32_CG(CG_CAC_REGION_4_OVERRIDE_4, reg); + + reg = RREG32(SQ_CAC_THRESHOLD) & ~(VSP_MASK | + VSP0_MASK | + GPR_MASK); + reg |= (VSP(ni_pi->cac_weights->vsp) | + VSP0(ni_pi->cac_weights->vsp0) | + GPR(ni_pi->cac_weights->gpr)); + WREG32(SQ_CAC_THRESHOLD, reg); + + reg = (MCDW_WR_ENABLE | + MCDX_WR_ENABLE | + MCDY_WR_ENABLE | + MCDZ_WR_ENABLE | + INDEX(0x09D4)); + WREG32(MC_CG_CONFIG, reg); + + reg = (READ_WEIGHT(ni_pi->cac_weights->mc_read_weight) | + WRITE_WEIGHT(ni_pi->cac_weights->mc_write_weight) | + ALLOW_OVERFLOW); + WREG32(MC_CG_DATAPORT, reg); + + return 0; +} + +static int ni_enable_smc_cac(struct radeon_device *rdev, bool enable) +{ + struct ni_power_info *ni_pi = ni_get_pi(rdev); + int ret = 0; + PPSMC_Result smc_result; + + if (ni_pi->enable_cac) { + if (enable) { + struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; + + if (!r600_is_uvd_state(radeon_new_state->class, radeon_new_state->class2)) { + smc_result = rv770_send_msg_to_smc(rdev, PPSMC_MSG_CollectCAC_PowerCorreln); + + if (ni_pi->support_cac_long_term_average) { + smc_result = rv770_send_msg_to_smc(rdev, PPSMC_CACLongTermAvgEnable); + if (PPSMC_Result_OK != smc_result) + ni_pi->support_cac_long_term_average = false; + } + + smc_result = rv770_send_msg_to_smc(rdev, PPSMC_MSG_EnableCac); + if (PPSMC_Result_OK != smc_result) + ret = -EINVAL; + + ni_pi->cac_enabled = (PPSMC_Result_OK == smc_result) ? true : false; + } + } else if (ni_pi->cac_enabled) { + smc_result = rv770_send_msg_to_smc(rdev, PPSMC_MSG_DisableCac); + + ni_pi->cac_enabled = false; + + if (ni_pi->support_cac_long_term_average) { + smc_result = rv770_send_msg_to_smc(rdev, PPSMC_CACLongTermAvgDisable); + if (PPSMC_Result_OK != smc_result) + ni_pi->support_cac_long_term_average = false; + } + } + } + + return ret; +} + +static int ni_pcie_performance_request(struct radeon_device *rdev, + u8 perf_req, bool advertise) +{ + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + +#if defined(CONFIG_ACPI) + if ((perf_req == PCIE_PERF_REQ_PECI_GEN1) || + (perf_req == PCIE_PERF_REQ_PECI_GEN2)) { + if (eg_pi->pcie_performance_request_registered == false) + radeon_acpi_pcie_notify_device_ready(rdev); + eg_pi->pcie_performance_request_registered = true; + return radeon_acpi_pcie_performance_request(rdev, perf_req, advertise); + } else if ((perf_req == PCIE_PERF_REQ_REMOVE_REGISTRY) && + eg_pi->pcie_performance_request_registered) { + eg_pi->pcie_performance_request_registered = false; + return radeon_acpi_pcie_performance_request(rdev, perf_req, advertise); + } +#endif + return 0; +} + +static int ni_advertise_gen2_capability(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + u32 tmp; + + tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL); + + if ((tmp & LC_OTHER_SIDE_EVER_SENT_GEN2) && + (tmp & LC_OTHER_SIDE_SUPPORTS_GEN2)) + pi->pcie_gen2 = true; + else + pi->pcie_gen2 = false; + + if (!pi->pcie_gen2) + ni_pcie_performance_request(rdev, PCIE_PERF_REQ_PECI_GEN2, true); + + return 0; +} + +static void ni_enable_bif_dynamic_pcie_gen2(struct radeon_device *rdev, + bool enable) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + u32 tmp, bif; + + tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL); + + if ((tmp & LC_OTHER_SIDE_EVER_SENT_GEN2) && + (tmp & LC_OTHER_SIDE_SUPPORTS_GEN2)) { + if (enable) { + if (!pi->boot_in_gen2) { + bif = RREG32(CG_BIF_REQ_AND_RSP) & ~CG_CLIENT_REQ_MASK; + bif |= CG_CLIENT_REQ(0xd); + WREG32(CG_BIF_REQ_AND_RSP, bif); + } + tmp &= ~LC_HW_VOLTAGE_IF_CONTROL_MASK; + tmp |= LC_HW_VOLTAGE_IF_CONTROL(1); + tmp |= LC_GEN2_EN_STRAP; + + tmp |= LC_CLR_FAILED_SPD_CHANGE_CNT; + WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp); + udelay(10); + tmp &= ~LC_CLR_FAILED_SPD_CHANGE_CNT; + WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp); + } else { + if (!pi->boot_in_gen2) { + bif = RREG32(CG_BIF_REQ_AND_RSP) & ~CG_CLIENT_REQ_MASK; + bif |= CG_CLIENT_REQ(0xd); + WREG32(CG_BIF_REQ_AND_RSP, bif); + + tmp &= ~LC_HW_VOLTAGE_IF_CONTROL_MASK; + tmp &= ~LC_GEN2_EN_STRAP; + } + WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp); + } + } +} + +static void ni_enable_dynamic_pcie_gen2(struct radeon_device *rdev, + bool enable) +{ + ni_enable_bif_dynamic_pcie_gen2(rdev, enable); + + if (enable) + WREG32_P(GENERAL_PWRMGT, ENABLE_GEN2PCIE, ~ENABLE_GEN2PCIE); + else + WREG32_P(GENERAL_PWRMGT, 0, ~ENABLE_GEN2PCIE); +} + +void ni_dpm_setup_asic(struct radeon_device *rdev) +{ + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + + ni_read_clock_registers(rdev); + btc_read_arb_registers(rdev); + rv770_get_memory_type(rdev); + if (eg_pi->pcie_performance_request) + ni_advertise_gen2_capability(rdev); + rv770_get_pcie_gen2_status(rdev); + rv770_enable_acpi_pm(rdev); +} + +int ni_dpm_enable(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + + if (pi->gfx_clock_gating) + ni_cg_clockgating_default(rdev); + if (btc_dpm_enabled(rdev)) + return -EINVAL; + if (pi->mg_clock_gating) + ni_mg_clockgating_default(rdev); + if (eg_pi->ls_clock_gating) + ni_ls_clockgating_default(rdev); + if (pi->voltage_control) { + rv770_enable_voltage_control(rdev, true); + cypress_construct_voltage_tables(rdev); + } + if (eg_pi->dynamic_ac_timing) + ni_initialize_mc_reg_table(rdev); + if (pi->dynamic_ss) + cypress_enable_spread_spectrum(rdev, true); + if (pi->thermal_protection) + rv770_enable_thermal_protection(rdev, true); + rv770_setup_bsp(rdev); + rv770_program_git(rdev); + rv770_program_tp(rdev); + rv770_program_tpp(rdev); + rv770_program_sstp(rdev); + cypress_enable_display_gap(rdev); + rv770_program_vc(rdev); + if (pi->dynamic_pcie_gen2) + ni_enable_dynamic_pcie_gen2(rdev, true); + if (rv770_upload_firmware(rdev)) + return -EINVAL; + ni_process_firmware_header(rdev); + ni_initial_switch_from_arb_f0_to_f1(rdev); + ni_init_smc_table(rdev); + ni_init_smc_spll_table(rdev); + ni_init_arb_table_index(rdev); + if (eg_pi->dynamic_ac_timing) + ni_populate_mc_reg_table(rdev); + ni_initialize_smc_cac_tables(rdev); + ni_initialize_hardware_cac_manager(rdev); + ni_populate_smc_tdp_limits(rdev); + ni_program_response_times(rdev); + r7xx_start_smc(rdev); + cypress_notify_smc_display_change(rdev, false); + cypress_enable_sclk_control(rdev, true); + if (eg_pi->memory_transition) + cypress_enable_mclk_control(rdev, true); + cypress_start_dpm(rdev); + if (pi->gfx_clock_gating) + ni_gfx_clockgating_enable(rdev, true); + if (pi->mg_clock_gating) + ni_mg_clockgating_enable(rdev, true); + if (eg_pi->ls_clock_gating) + ni_ls_clockgating_enable(rdev, true); + + if (rdev->irq.installed && + r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) { + PPSMC_Result result; + + rv770_set_thermal_temperature_range(rdev, R600_TEMP_RANGE_MIN, 0xff * 1000); + rdev->irq.dpm_thermal = true; + radeon_irq_set(rdev); + result = rv770_send_msg_to_smc(rdev, PPSMC_MSG_EnableThermalInterrupt); + + if (result != PPSMC_Result_OK) + DRM_DEBUG_KMS("Could not enable thermal interrupts.\n"); + } + + rv770_enable_auto_throttle_source(rdev, RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL, true); + + return 0; +} + +void ni_dpm_disable(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + + if (!btc_dpm_enabled(rdev)) + return; + rv770_clear_vc(rdev); + if (pi->thermal_protection) + rv770_enable_thermal_protection(rdev, false); + ni_enable_power_containment(rdev, false); + ni_enable_smc_cac(rdev, false); + cypress_enable_spread_spectrum(rdev, false); + rv770_enable_auto_throttle_source(rdev, RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL, false); + if (pi->dynamic_pcie_gen2) + ni_enable_dynamic_pcie_gen2(rdev, false); + + if (rdev->irq.installed && + r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) { + rdev->irq.dpm_thermal = false; + radeon_irq_set(rdev); + } + + if (pi->gfx_clock_gating) + ni_gfx_clockgating_enable(rdev, false); + if (pi->mg_clock_gating) + ni_mg_clockgating_enable(rdev, false); + if (eg_pi->ls_clock_gating) + ni_ls_clockgating_enable(rdev, false); + ni_stop_dpm(rdev); + btc_reset_to_default(rdev); + ni_stop_smc(rdev); + ni_force_switch_to_arb_f0(rdev); +} + +int ni_power_control_set_level(struct radeon_device *rdev) +{ + ni_restrict_performance_levels_before_switch(rdev); + rv770_halt_smc(rdev); + ni_populate_smc_tdp_limits(rdev); + rv770_resume_smc(rdev); + rv770_set_sw_state(rdev); + + return 0; +} + +int ni_dpm_set_power_state(struct radeon_device *rdev) +{ + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + int ret; + + ni_apply_state_adjust_rules(rdev); + + ni_restrict_performance_levels_before_switch(rdev); + ni_enable_power_containment(rdev, false); + ni_enable_smc_cac(rdev, false); + rv770_halt_smc(rdev); + if (eg_pi->smu_uvd_hs) + btc_notify_uvd_to_smc(rdev); + ni_upload_sw_state(rdev); + if (eg_pi->dynamic_ac_timing) + ni_upload_mc_reg_table(rdev); + ret = ni_program_memory_timing_parameters(rdev); + if (ret) + return ret; + rv770_resume_smc(rdev); + rv770_set_sw_state(rdev); + ni_enable_smc_cac(rdev, true); + ni_enable_power_containment(rdev, true); + + return 0; +} + +void ni_dpm_reset_asic(struct radeon_device *rdev) +{ + ni_restrict_performance_levels_before_switch(rdev); + rv770_set_boot_state(rdev); +} + +union power_info { + struct _ATOM_POWERPLAY_INFO info; + struct _ATOM_POWERPLAY_INFO_V2 info_2; + struct _ATOM_POWERPLAY_INFO_V3 info_3; + struct _ATOM_PPLIB_POWERPLAYTABLE pplib; + struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2; + struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3; +}; + +union pplib_clock_info { + struct _ATOM_PPLIB_R600_CLOCK_INFO r600; + struct _ATOM_PPLIB_RS780_CLOCK_INFO rs780; + struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO evergreen; + struct _ATOM_PPLIB_SUMO_CLOCK_INFO sumo; +}; + +union pplib_power_state { + struct _ATOM_PPLIB_STATE v1; + struct _ATOM_PPLIB_STATE_V2 v2; +}; + +static void ni_parse_pplib_non_clock_info(struct radeon_device *rdev, + struct radeon_ps *rps, + struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info, + u8 table_rev) +{ + rps->caps = le32_to_cpu(non_clock_info->ulCapsAndSettings); + rps->class = le16_to_cpu(non_clock_info->usClassification); + rps->class2 = le16_to_cpu(non_clock_info->usClassification2); + + if (ATOM_PPLIB_NONCLOCKINFO_VER1 < table_rev) { + rps->vclk = le32_to_cpu(non_clock_info->ulVCLK); + rps->dclk = le32_to_cpu(non_clock_info->ulDCLK); + } else if (r600_is_uvd_state(rps->class, rps->class2)) { + rps->vclk = RV770_DEFAULT_VCLK_FREQ; + rps->dclk = RV770_DEFAULT_DCLK_FREQ; + } else { + rps->vclk = 0; + rps->dclk = 0; + } + + if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT) + rdev->pm.dpm.boot_ps = rps; + if (rps->class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE) + rdev->pm.dpm.uvd_ps = rps; +} + +static void ni_parse_pplib_clock_info(struct radeon_device *rdev, + struct radeon_ps *rps, int index, + union pplib_clock_info *clock_info) +{ + struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct ni_ps *ps = ni_get_ps(rps); + u16 vddc; + struct rv7xx_pl *pl = &ps->performance_levels[index]; + + ps->performance_level_count = index + 1; + + pl->sclk = le16_to_cpu(clock_info->evergreen.usEngineClockLow); + pl->sclk |= clock_info->evergreen.ucEngineClockHigh << 16; + pl->mclk = le16_to_cpu(clock_info->evergreen.usMemoryClockLow); + pl->mclk |= clock_info->evergreen.ucMemoryClockHigh << 16; + + pl->vddc = le16_to_cpu(clock_info->evergreen.usVDDC); + pl->vddci = le16_to_cpu(clock_info->evergreen.usVDDCI); + pl->flags = le32_to_cpu(clock_info->evergreen.ulFlags); + + /* patch up vddc if necessary */ + if (pl->vddc == 0xff01) { + if (radeon_atom_get_max_vddc(rdev, 0, 0, &vddc) == 0) + pl->vddc = vddc; + } + + if (rps->class & ATOM_PPLIB_CLASSIFICATION_ACPI) { + pi->acpi_vddc = pl->vddc; + eg_pi->acpi_vddci = pl->vddci; + if (ps->performance_levels[0].flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) + pi->acpi_pcie_gen2 = true; + else + pi->acpi_pcie_gen2 = false; + } + + if (rps->class2 & ATOM_PPLIB_CLASSIFICATION2_ULV) { + eg_pi->ulv.supported = true; + eg_pi->ulv.pl = pl; + } + + if (pi->min_vddc_in_table > pl->vddc) + pi->min_vddc_in_table = pl->vddc; + + if (pi->max_vddc_in_table < pl->vddc) + pi->max_vddc_in_table = pl->vddc; + + /* patch up boot state */ + if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT) { + u16 vddc, vddci; + radeon_atombios_get_default_voltages(rdev, &vddc, &vddci); + pl->mclk = rdev->clock.default_mclk; + pl->sclk = rdev->clock.default_sclk; + pl->vddc = vddc; + pl->vddci = vddci; + } + + if ((rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) == + ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE) { + rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac.sclk = pl->sclk; + rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac.mclk = pl->mclk; + rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac.vddc = pl->vddc; + rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac.vddci = pl->vddci; + } +} + +static int ni_parse_power_table(struct radeon_device *rdev) +{ + struct radeon_mode_info *mode_info = &rdev->mode_info; + struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info; + union pplib_power_state *power_state; + int i, j; + union pplib_clock_info *clock_info; + union power_info *power_info; + int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo); + u16 data_offset; + u8 frev, crev; + struct ni_ps *ps; + + if (!atom_parse_data_header(mode_info->atom_context, index, NULL, + &frev, &crev, &data_offset)) + return -EINVAL; + power_info = (union power_info *)(mode_info->atom_context->bios + data_offset); + + rdev->pm.dpm.ps = kzalloc(sizeof(struct radeon_ps) * + power_info->pplib.ucNumStates, GFP_KERNEL); + if (!rdev->pm.dpm.ps) + return -ENOMEM; + rdev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps); + rdev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime); + rdev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime); + + for (i = 0; i < power_info->pplib.ucNumStates; i++) { + power_state = (union pplib_power_state *) + (mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib.usStateArrayOffset) + + i * power_info->pplib.ucStateEntrySize); + non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *) + (mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset) + + (power_state->v1.ucNonClockStateIndex * + power_info->pplib.ucNonClockSize)); + if (power_info->pplib.ucStateEntrySize - 1) { + ps = kzalloc(sizeof(struct ni_ps), GFP_KERNEL); + if (ps == NULL) { + kfree(rdev->pm.dpm.ps); + return -ENOMEM; + } + rdev->pm.dpm.ps[i].ps_priv = ps; + ni_parse_pplib_non_clock_info(rdev, &rdev->pm.dpm.ps[i], + non_clock_info, + power_info->pplib.ucNonClockSize); + for (j = 0; j < (power_info->pplib.ucStateEntrySize - 1); j++) { + clock_info = (union pplib_clock_info *) + (mode_info->atom_context->bios + data_offset + + le16_to_cpu(power_info->pplib.usClockInfoArrayOffset) + + (power_state->v1.ucClockStateIndices[j] * + power_info->pplib.ucClockInfoSize)); + ni_parse_pplib_clock_info(rdev, + &rdev->pm.dpm.ps[i], j, + clock_info); + } + } + } + rdev->pm.dpm.num_ps = power_info->pplib.ucNumStates; + return 0; +} + +int ni_dpm_init(struct radeon_device *rdev) +{ + struct rv7xx_power_info *pi; + struct evergreen_power_info *eg_pi; + struct ni_power_info *ni_pi; + int index = GetIndexIntoMasterTable(DATA, ASIC_InternalSS_Info); + u16 data_offset, size; + u8 frev, crev; + struct atom_clock_dividers dividers; + int ret; + + ni_pi = kzalloc(sizeof(struct ni_power_info), GFP_KERNEL); + if (ni_pi == NULL) + return -ENOMEM; + rdev->pm.dpm.priv = ni_pi; + eg_pi = &ni_pi->eg; + pi = &eg_pi->rv7xx; + + rv770_get_max_vddc(rdev); + + eg_pi->ulv.supported = false; + pi->acpi_vddc = 0; + eg_pi->acpi_vddci = 0; + pi->min_vddc_in_table = 0; + pi->max_vddc_in_table = 0; + + ret = ni_parse_power_table(rdev); + if (ret) + return ret; + ret = r600_parse_extended_power_table(rdev); + if (ret) + return ret; + + ni_patch_dependency_tables_based_on_leakage(rdev); + + if (rdev->pm.dpm.voltage_response_time == 0) + rdev->pm.dpm.voltage_response_time = R600_VOLTAGERESPONSETIME_DFLT; + if (rdev->pm.dpm.backbias_response_time == 0) + rdev->pm.dpm.backbias_response_time = R600_BACKBIASRESPONSETIME_DFLT; + + ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM, + 0, false, ÷rs); + if (ret) + pi->ref_div = dividers.ref_div + 1; + else + pi->ref_div = R600_REFERENCEDIVIDER_DFLT; + + pi->rlp = RV770_RLP_DFLT; + pi->rmp = RV770_RMP_DFLT; + pi->lhp = RV770_LHP_DFLT; + pi->lmp = RV770_LMP_DFLT; + + eg_pi->ats[0].rlp = RV770_RLP_DFLT; + eg_pi->ats[0].rmp = RV770_RMP_DFLT; + eg_pi->ats[0].lhp = RV770_LHP_DFLT; + eg_pi->ats[0].lmp = RV770_LMP_DFLT; + + eg_pi->ats[1].rlp = BTC_RLP_UVD_DFLT; + eg_pi->ats[1].rmp = BTC_RMP_UVD_DFLT; + eg_pi->ats[1].lhp = BTC_LHP_UVD_DFLT; + eg_pi->ats[1].lmp = BTC_LMP_UVD_DFLT; + + eg_pi->smu_uvd_hs = true; + + if (rdev->pdev->device == 0x6707) { + pi->mclk_strobe_mode_threshold = 55000; + pi->mclk_edc_enable_threshold = 55000; + eg_pi->mclk_edc_wr_enable_threshold = 55000; + } else { + pi->mclk_strobe_mode_threshold = 40000; + pi->mclk_edc_enable_threshold = 40000; + eg_pi->mclk_edc_wr_enable_threshold = 40000; + } + ni_pi->mclk_rtt_mode_threshold = eg_pi->mclk_edc_wr_enable_threshold; + + pi->voltage_control = + radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC); + + pi->mvdd_control = + radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_MVDDC); + + eg_pi->vddci_control = + radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDCI); + + if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size, + &frev, &crev, &data_offset)) { + pi->sclk_ss = true; + pi->mclk_ss = true; + pi->dynamic_ss = true; + } else { + pi->sclk_ss = false; + pi->mclk_ss = false; + pi->dynamic_ss = true; + } + + pi->asi = RV770_ASI_DFLT; + pi->pasi = CYPRESS_HASI_DFLT; + pi->vrc = CYPRESS_VRC_DFLT; + + pi->power_gating = false; + + pi->gfx_clock_gating = true; + + pi->mg_clock_gating = true; + pi->mgcgtssm = true; + eg_pi->ls_clock_gating = false; + eg_pi->sclk_deep_sleep = false; + + pi->dynamic_pcie_gen2 = true; + + if (pi->gfx_clock_gating && + (rdev->pm.int_thermal_type != THERMAL_TYPE_NONE)) + pi->thermal_protection = true; + else + pi->thermal_protection = false; + + pi->display_gap = true; + + pi->dcodt = true; + + pi->ulps = true; + + eg_pi->dynamic_ac_timing = true; + eg_pi->abm = true; + eg_pi->mcls = true; + eg_pi->light_sleep = true; + eg_pi->memory_transition = true; +#if defined(CONFIG_ACPI) + eg_pi->pcie_performance_request = + radeon_acpi_is_pcie_performance_request_supported(rdev); +#else + eg_pi->pcie_performance_request = false; +#endif + + eg_pi->dll_default_on = false; + + eg_pi->sclk_deep_sleep = false; + + pi->mclk_stutter_mode_threshold = 0; + + pi->sram_end = SMC_RAM_END; + + rdev->pm.dpm.dyn_state.mclk_sclk_ratio = 3; + rdev->pm.dpm.dyn_state.vddc_vddci_delta = 200; + rdev->pm.dpm.dyn_state.min_vddc_for_pcie_gen2 = 900; + rdev->pm.dpm.dyn_state.valid_sclk_values.count = ARRAY_SIZE(btc_valid_sclk); + rdev->pm.dpm.dyn_state.valid_sclk_values.values = btc_valid_sclk; + rdev->pm.dpm.dyn_state.valid_mclk_values.count = 0; + rdev->pm.dpm.dyn_state.valid_mclk_values.values = NULL; + rdev->pm.dpm.dyn_state.sclk_mclk_delta = 12500; + + ni_pi->cac_data.leakage_coefficients.at = 516; + ni_pi->cac_data.leakage_coefficients.bt = 18; + ni_pi->cac_data.leakage_coefficients.av = 51; + ni_pi->cac_data.leakage_coefficients.bv = 2957; + + switch (rdev->pdev->device) { + case 0x6700: + case 0x6701: + case 0x6702: + case 0x6703: + case 0x6718: + ni_pi->cac_weights = &cac_weights_cayman_xt; + break; + case 0x6705: + case 0x6719: + case 0x671D: + case 0x671C: + default: + ni_pi->cac_weights = &cac_weights_cayman_pro; + break; + case 0x6704: + case 0x6706: + case 0x6707: + case 0x6708: + case 0x6709: + ni_pi->cac_weights = &cac_weights_cayman_le; + break; + } + + if (ni_pi->cac_weights->enable_power_containment_by_default) { + ni_pi->enable_power_containment = true; + ni_pi->enable_cac = true; + ni_pi->enable_sq_ramping = true; + } else { + ni_pi->enable_power_containment = false; + ni_pi->enable_cac = false; + ni_pi->enable_sq_ramping = false; + } + + ni_pi->driver_calculate_cac_leakage = false; + ni_pi->cac_configuration_required = true; + + if (ni_pi->cac_configuration_required) { + ni_pi->support_cac_long_term_average = true; + ni_pi->lta_window_size = ni_pi->cac_weights->l2_lta_window_size; + ni_pi->lts_truncate = ni_pi->cac_weights->lts_truncate; + } else { + ni_pi->support_cac_long_term_average = false; + ni_pi->lta_window_size = 0; + ni_pi->lts_truncate = 0; + } + + ni_pi->use_power_boost_limit = true; + + return 0; +} + +void ni_dpm_fini(struct radeon_device *rdev) +{ + int i; + + for (i = 0; i < rdev->pm.dpm.num_ps; i++) { + kfree(rdev->pm.dpm.ps[i].ps_priv); + } + kfree(rdev->pm.dpm.ps); + kfree(rdev->pm.dpm.priv); + r600_free_extended_power_table(rdev); +} + +void ni_dpm_print_power_state(struct radeon_device *rdev, + struct radeon_ps *rps) +{ + struct ni_ps *ps = ni_get_ps(rps); + struct rv7xx_pl *pl; + int i; + + r600_dpm_print_class_info(rps->class, rps->class2); + r600_dpm_print_cap_info(rps->caps); + printk("\tuvd vclk: %d dclk: %d\n", rps->vclk, rps->dclk); + for (i = 0; i < ps->performance_level_count; i++) { + pl = &ps->performance_levels[i]; + printk("\t\tpower level 0 sclk: %u mclk: %u vddc: %u vddci: %u\n", + pl->sclk, pl->mclk, pl->vddc, pl->vddci); + } + r600_dpm_print_ps_status(rdev, rps); +} + +u32 ni_dpm_get_sclk(struct radeon_device *rdev, bool low) +{ + struct ni_ps *requested_state = ni_get_ps(rdev->pm.dpm.requested_ps); + + if (low) + return requested_state->performance_levels[0].sclk; + else + return requested_state->performance_levels[requested_state->performance_level_count - 1].sclk; +} + +u32 ni_dpm_get_mclk(struct radeon_device *rdev, bool low) +{ + struct ni_ps *requested_state = ni_get_ps(rdev->pm.dpm.requested_ps); + + if (low) + return requested_state->performance_levels[0].mclk; + else + return requested_state->performance_levels[requested_state->performance_level_count - 1].mclk; +} + diff --git a/drivers/gpu/drm/radeon/ni_dpm.h b/drivers/gpu/drm/radeon/ni_dpm.h new file mode 100644 index 0000000..e10f747 --- /dev/null +++ b/drivers/gpu/drm/radeon/ni_dpm.h @@ -0,0 +1,233 @@ +/* + * Copyright 2012 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ +#ifndef __NI_DPM_H__ +#define __NI_DPM_H__ + +#include "cypress_dpm.h" +#include "btc_dpm.h" +#include "nislands_smc.h" + +struct ni_clock_registers { + u32 cg_spll_func_cntl; + u32 cg_spll_func_cntl_2; + u32 cg_spll_func_cntl_3; + u32 cg_spll_func_cntl_4; + u32 cg_spll_spread_spectrum; + u32 cg_spll_spread_spectrum_2; + u32 mclk_pwrmgt_cntl; + u32 dll_cntl; + u32 mpll_ad_func_cntl; + u32 mpll_ad_func_cntl_2; + u32 mpll_dq_func_cntl; + u32 mpll_dq_func_cntl_2; + u32 mpll_ss1; + u32 mpll_ss2; +}; + +struct ni_mc_reg_entry { + u32 mclk_max; + u32 mc_data[SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE]; +}; + +struct ni_mc_reg_table { + u8 last; + u8 num_entries; + u16 valid_flag; + struct ni_mc_reg_entry mc_reg_table_entry[MAX_AC_TIMING_ENTRIES]; + SMC_NIslands_MCRegisterAddress mc_reg_address[SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE]; +}; + +#define NISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT 2 + +enum ni_dc_cac_level +{ + NISLANDS_DCCAC_LEVEL_0 = 0, + NISLANDS_DCCAC_LEVEL_1, + NISLANDS_DCCAC_LEVEL_2, + NISLANDS_DCCAC_LEVEL_3, + NISLANDS_DCCAC_LEVEL_4, + NISLANDS_DCCAC_LEVEL_5, + NISLANDS_DCCAC_LEVEL_6, + NISLANDS_DCCAC_LEVEL_7, + NISLANDS_DCCAC_MAX_LEVELS +}; + +struct ni_leakage_coeffients +{ + u32 at; + u32 bt; + u32 av; + u32 bv; + s32 t_slope; + s32 t_intercept; + u32 t_ref; +}; + +struct ni_cac_data +{ + struct ni_leakage_coeffients leakage_coefficients; + u32 i_leakage; + s32 leakage_minimum_temperature; + u32 pwr_const; + u32 dc_cac_value; + u32 bif_cac_value; + u32 lkge_pwr; + u8 mc_wr_weight; + u8 mc_rd_weight; + u8 allow_ovrflw; + u8 num_win_tdp; + u8 l2num_win_tdp; + u8 lts_truncate_n; +}; + +struct ni_cac_weights +{ + u32 weight_tcp_sig0; + u32 weight_tcp_sig1; + u32 weight_ta_sig; + u32 weight_tcc_en0; + u32 weight_tcc_en1; + u32 weight_tcc_en2; + u32 weight_cb_en0; + u32 weight_cb_en1; + u32 weight_cb_en2; + u32 weight_cb_en3; + u32 weight_db_sig0; + u32 weight_db_sig1; + u32 weight_db_sig2; + u32 weight_db_sig3; + u32 weight_sxm_sig0; + u32 weight_sxm_sig1; + u32 weight_sxm_sig2; + u32 weight_sxs_sig0; + u32 weight_sxs_sig1; + u32 weight_xbr_0; + u32 weight_xbr_1; + u32 weight_xbr_2; + u32 weight_spi_sig0; + u32 weight_spi_sig1; + u32 weight_spi_sig2; + u32 weight_spi_sig3; + u32 weight_spi_sig4; + u32 weight_spi_sig5; + u32 weight_lds_sig0; + u32 weight_lds_sig1; + u32 weight_sc; + u32 weight_bif; + u32 weight_cp; + u32 weight_pa_sig0; + u32 weight_pa_sig1; + u32 weight_vgt_sig0; + u32 weight_vgt_sig1; + u32 weight_vgt_sig2; + u32 weight_dc_sig0; + u32 weight_dc_sig1; + u32 weight_dc_sig2; + u32 weight_dc_sig3; + u32 weight_uvd_sig0; + u32 weight_uvd_sig1; + u32 weight_spare0; + u32 weight_spare1; + u32 weight_sq_vsp; + u32 weight_sq_vsp0; + u32 weight_sq_gpr; + u32 ovr_mode_spare_0; + u32 ovr_val_spare_0; + u32 ovr_mode_spare_1; + u32 ovr_val_spare_1; + u32 vsp; + u32 vsp0; + u32 gpr; + u8 mc_read_weight; + u8 mc_write_weight; + u32 tid_cnt; + u32 tid_unit; + u32 l2_lta_window_size; + u32 lts_truncate; + u32 dc_cac[NISLANDS_DCCAC_MAX_LEVELS]; + u32 pcie_cac[SMC_NISLANDS_BIF_LUT_NUM_OF_ENTRIES]; + bool enable_power_containment_by_default; +}; + +struct ni_ps { + u16 performance_level_count; + bool dc_compatible; + struct rv7xx_pl performance_levels[NISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE]; +}; + +struct ni_power_info { + /* must be first! */ + struct evergreen_power_info eg; + struct ni_clock_registers clock_registers; + struct ni_mc_reg_table mc_reg_table; + u32 mclk_rtt_mode_threshold; + /* flags */ + bool use_power_boost_limit; + bool support_cac_long_term_average; + bool cac_enabled; + bool cac_configuration_required; + bool driver_calculate_cac_leakage; + bool pc_enabled; + bool enable_power_containment; + bool enable_cac; + bool enable_sq_ramping; + /* smc offsets */ + u16 arb_table_start; + u16 fan_table_start; + u16 cac_table_start; + u16 spll_table_start; + /* CAC stuff */ + struct ni_cac_data cac_data; + u32 dc_cac_table[NISLANDS_DCCAC_MAX_LEVELS]; + const struct ni_cac_weights *cac_weights; + u8 lta_window_size; + u8 lts_truncate; + struct ni_ps hw_ps; + /* scratch structs */ + SMC_NIslands_MCRegisters smc_mc_reg_table; + NISLANDS_SMC_STATETABLE smc_statetable; +}; + +#define NISLANDS_INITIAL_STATE_ARB_INDEX 0 +#define NISLANDS_ACPI_STATE_ARB_INDEX 1 +#define NISLANDS_ULV_STATE_ARB_INDEX 2 +#define NISLANDS_DRIVER_STATE_ARB_INDEX 3 + +#define NISLANDS_DPM2_MAX_PULSE_SKIP 256 + +#define NISLANDS_DPM2_NEAR_TDP_DEC 10 +#define NISLANDS_DPM2_ABOVE_SAFE_INC 5 +#define NISLANDS_DPM2_BELOW_SAFE_INC 20 + +#define NISLANDS_DPM2_TDP_SAFE_LIMIT_PERCENT 80 + +#define NISLANDS_DPM2_MAXPS_PERCENT_H 90 +#define NISLANDS_DPM2_MAXPS_PERCENT_M 0 + +#define NISLANDS_DPM2_SQ_RAMP_MAX_POWER 0x3FFF +#define NISLANDS_DPM2_SQ_RAMP_MIN_POWER 0x12 +#define NISLANDS_DPM2_SQ_RAMP_MAX_POWER_DELTA 0x15 +#define NISLANDS_DPM2_SQ_RAMP_STI_SIZE 0x1E +#define NISLANDS_DPM2_SQ_RAMP_LTI_RATIO 0xF + +#endif diff --git a/drivers/gpu/drm/radeon/nid.h b/drivers/gpu/drm/radeon/nid.h index 7b8da52..1775043 100644 --- a/drivers/gpu/drm/radeon/nid.h +++ b/drivers/gpu/drm/radeon/nid.h @@ -492,6 +492,558 @@ /* TN SMU registers */ #define TN_CURRENT_GNB_TEMP 0x1F390
+/* pm registers */ +#define SMC_MSG 0x20c +#define HOST_SMC_MSG(x) ((x) << 0) +#define HOST_SMC_MSG_MASK (0xff << 0) +#define HOST_SMC_MSG_SHIFT 0 +#define HOST_SMC_RESP(x) ((x) << 8) +#define HOST_SMC_RESP_MASK (0xff << 8) +#define HOST_SMC_RESP_SHIFT 8 +#define SMC_HOST_MSG(x) ((x) << 16) +#define SMC_HOST_MSG_MASK (0xff << 16) +#define SMC_HOST_MSG_SHIFT 16 +#define SMC_HOST_RESP(x) ((x) << 24) +#define SMC_HOST_RESP_MASK (0xff << 24) +#define SMC_HOST_RESP_SHIFT 24 + +#define CG_SPLL_FUNC_CNTL 0x600 +#define SPLL_RESET (1 << 0) +#define SPLL_SLEEP (1 << 1) +#define SPLL_BYPASS_EN (1 << 3) +#define SPLL_REF_DIV(x) ((x) << 4) +#define SPLL_REF_DIV_MASK (0x3f << 4) +#define SPLL_PDIV_A(x) ((x) << 20) +#define SPLL_PDIV_A_MASK (0x7f << 20) +#define SPLL_PDIV_A_SHIFT 20 +#define CG_SPLL_FUNC_CNTL_2 0x604 +#define SCLK_MUX_SEL(x) ((x) << 0) +#define SCLK_MUX_SEL_MASK (0x1ff << 0) +#define CG_SPLL_FUNC_CNTL_3 0x608 +#define SPLL_FB_DIV(x) ((x) << 0) +#define SPLL_FB_DIV_MASK (0x3ffffff << 0) +#define SPLL_FB_DIV_SHIFT 0 +#define SPLL_DITHEN (1 << 28) + +#define MPLL_CNTL_MODE 0x61c +# define SS_SSEN (1 << 24) +# define SS_DSMODE_EN (1 << 25) + +#define MPLL_AD_FUNC_CNTL 0x624 +#define CLKF(x) ((x) << 0) +#define CLKF_MASK (0x7f << 0) +#define CLKR(x) ((x) << 7) +#define CLKR_MASK (0x1f << 7) +#define CLKFRAC(x) ((x) << 12) +#define CLKFRAC_MASK (0x1f << 12) +#define YCLK_POST_DIV(x) ((x) << 17) +#define YCLK_POST_DIV_MASK (3 << 17) +#define IBIAS(x) ((x) << 20) +#define IBIAS_MASK (0x3ff << 20) +#define RESET (1 << 30) +#define PDNB (1 << 31) +#define MPLL_AD_FUNC_CNTL_2 0x628 +#define BYPASS (1 << 19) +#define BIAS_GEN_PDNB (1 << 24) +#define RESET_EN (1 << 25) +#define VCO_MODE (1 << 29) +#define MPLL_DQ_FUNC_CNTL 0x62c +#define MPLL_DQ_FUNC_CNTL_2 0x630 + +#define GENERAL_PWRMGT 0x63c +# define GLOBAL_PWRMGT_EN (1 << 0) +# define STATIC_PM_EN (1 << 1) +# define THERMAL_PROTECTION_DIS (1 << 2) +# define THERMAL_PROTECTION_TYPE (1 << 3) +# define ENABLE_GEN2PCIE (1 << 4) +# define ENABLE_GEN2XSP (1 << 5) +# define SW_SMIO_INDEX(x) ((x) << 6) +# define SW_SMIO_INDEX_MASK (3 << 6) +# define SW_SMIO_INDEX_SHIFT 6 +# define LOW_VOLT_D2_ACPI (1 << 8) +# define LOW_VOLT_D3_ACPI (1 << 9) +# define VOLT_PWRMGT_EN (1 << 10) +# define BACKBIAS_PAD_EN (1 << 18) +# define BACKBIAS_VALUE (1 << 19) +# define DYN_SPREAD_SPECTRUM_EN (1 << 23) +# define AC_DC_SW (1 << 24) + +#define SCLK_PWRMGT_CNTL 0x644 +# define SCLK_PWRMGT_OFF (1 << 0) +# define SCLK_LOW_D1 (1 << 1) +# define FIR_RESET (1 << 4) +# define FIR_FORCE_TREND_SEL (1 << 5) +# define FIR_TREND_MODE (1 << 6) +# define DYN_GFX_CLK_OFF_EN (1 << 7) +# define GFX_CLK_FORCE_ON (1 << 8) +# define GFX_CLK_REQUEST_OFF (1 << 9) +# define GFX_CLK_FORCE_OFF (1 << 10) +# define GFX_CLK_OFF_ACPI_D1 (1 << 11) +# define GFX_CLK_OFF_ACPI_D2 (1 << 12) +# define GFX_CLK_OFF_ACPI_D3 (1 << 13) +# define DYN_LIGHT_SLEEP_EN (1 << 14) +#define MCLK_PWRMGT_CNTL 0x648 +# define DLL_SPEED(x) ((x) << 0) +# define DLL_SPEED_MASK (0x1f << 0) +# define MPLL_PWRMGT_OFF (1 << 5) +# define DLL_READY (1 << 6) +# define MC_INT_CNTL (1 << 7) +# define MRDCKA0_PDNB (1 << 8) +# define MRDCKA1_PDNB (1 << 9) +# define MRDCKB0_PDNB (1 << 10) +# define MRDCKB1_PDNB (1 << 11) +# define MRDCKC0_PDNB (1 << 12) +# define MRDCKC1_PDNB (1 << 13) +# define MRDCKD0_PDNB (1 << 14) +# define MRDCKD1_PDNB (1 << 15) +# define MRDCKA0_RESET (1 << 16) +# define MRDCKA1_RESET (1 << 17) +# define MRDCKB0_RESET (1 << 18) +# define MRDCKB1_RESET (1 << 19) +# define MRDCKC0_RESET (1 << 20) +# define MRDCKC1_RESET (1 << 21) +# define MRDCKD0_RESET (1 << 22) +# define MRDCKD1_RESET (1 << 23) +# define DLL_READY_READ (1 << 24) +# define USE_DISPLAY_GAP (1 << 25) +# define USE_DISPLAY_URGENT_NORMAL (1 << 26) +# define MPLL_TURNOFF_D2 (1 << 28) +#define DLL_CNTL 0x64c +# define MRDCKA0_BYPASS (1 << 24) +# define MRDCKA1_BYPASS (1 << 25) +# define MRDCKB0_BYPASS (1 << 26) +# define MRDCKB1_BYPASS (1 << 27) +# define MRDCKC0_BYPASS (1 << 28) +# define MRDCKC1_BYPASS (1 << 29) +# define MRDCKD0_BYPASS (1 << 30) +# define MRDCKD1_BYPASS (1 << 31) + +#define CG_AT 0x6d4 +# define CG_R(x) ((x) << 0) +# define CG_R_MASK (0xffff << 0) +# define CG_L(x) ((x) << 16) +# define CG_L_MASK (0xffff << 16) + +#define CG_BIF_REQ_AND_RSP 0x7f4 +#define CG_CLIENT_REQ(x) ((x) << 0) +#define CG_CLIENT_REQ_MASK (0xff << 0) +#define CG_CLIENT_REQ_SHIFT 0 +#define CG_CLIENT_RESP(x) ((x) << 8) +#define CG_CLIENT_RESP_MASK (0xff << 8) +#define CG_CLIENT_RESP_SHIFT 8 +#define CLIENT_CG_REQ(x) ((x) << 16) +#define CLIENT_CG_REQ_MASK (0xff << 16) +#define CLIENT_CG_REQ_SHIFT 16 +#define CLIENT_CG_RESP(x) ((x) << 24) +#define CLIENT_CG_RESP_MASK (0xff << 24) +#define CLIENT_CG_RESP_SHIFT 24 + +#define CG_SPLL_SPREAD_SPECTRUM 0x790 +#define SSEN (1 << 0) +#define CLK_S(x) ((x) << 4) +#define CLK_S_MASK (0xfff << 4) +#define CLK_S_SHIFT 4 +#define CG_SPLL_SPREAD_SPECTRUM_2 0x794 +#define CLK_V(x) ((x) << 0) +#define CLK_V_MASK (0x3ffffff << 0) +#define CLK_V_SHIFT 0 + +#define SMC_SCRATCH0 0x81c + +#define CG_SPLL_FUNC_CNTL_4 0x850 + +#define MPLL_SS1 0x85c +#define CLKV(x) ((x) << 0) +#define CLKV_MASK (0x3ffffff << 0) +#define MPLL_SS2 0x860 +#define CLKS(x) ((x) << 0) +#define CLKS_MASK (0xfff << 0) + +#define CG_CAC_CTRL 0x88c +#define TID_CNT(x) ((x) << 0) +#define TID_CNT_MASK (0x3fff << 0) +#define TID_UNIT(x) ((x) << 14) +#define TID_UNIT_MASK (0xf << 14) + +#define MC_CG_CONFIG 0x25bc +#define MCDW_WR_ENABLE (1 << 0) +#define MCDX_WR_ENABLE (1 << 1) +#define MCDY_WR_ENABLE (1 << 2) +#define MCDZ_WR_ENABLE (1 << 3) +#define MC_RD_ENABLE(x) ((x) << 4) +#define MC_RD_ENABLE_MASK (3 << 4) +#define INDEX(x) ((x) << 6) +#define INDEX_MASK (0xfff << 6) +#define INDEX_SHIFT 6 + +#define MC_ARB_CAC_CNTL 0x2750 +#define ENABLE (1 << 0) +#define READ_WEIGHT(x) ((x) << 1) +#define READ_WEIGHT_MASK (0x3f << 1) +#define READ_WEIGHT_SHIFT 1 +#define WRITE_WEIGHT(x) ((x) << 7) +#define WRITE_WEIGHT_MASK (0x3f << 7) +#define WRITE_WEIGHT_SHIFT 7 +#define ALLOW_OVERFLOW (1 << 13) + +#define MC_ARB_DRAM_TIMING 0x2774 +#define MC_ARB_DRAM_TIMING2 0x2778 + +#define MC_ARB_RFSH_RATE 0x27b0 +#define POWERMODE0(x) ((x) << 0) +#define POWERMODE0_MASK (0xff << 0) +#define POWERMODE0_SHIFT 0 +#define POWERMODE1(x) ((x) << 8) +#define POWERMODE1_MASK (0xff << 8) +#define POWERMODE1_SHIFT 8 +#define POWERMODE2(x) ((x) << 16) +#define POWERMODE2_MASK (0xff << 16) +#define POWERMODE2_SHIFT 16 +#define POWERMODE3(x) ((x) << 24) +#define POWERMODE3_MASK (0xff << 24) +#define POWERMODE3_SHIFT 24 + +#define MC_ARB_CG 0x27e8 +#define CG_ARB_REQ(x) ((x) << 0) +#define CG_ARB_REQ_MASK (0xff << 0) +#define CG_ARB_REQ_SHIFT 0 +#define CG_ARB_RESP(x) ((x) << 8) +#define CG_ARB_RESP_MASK (0xff << 8) +#define CG_ARB_RESP_SHIFT 8 +#define ARB_CG_REQ(x) ((x) << 16) +#define ARB_CG_REQ_MASK (0xff << 16) +#define ARB_CG_REQ_SHIFT 16 +#define ARB_CG_RESP(x) ((x) << 24) +#define ARB_CG_RESP_MASK (0xff << 24) +#define ARB_CG_RESP_SHIFT 24 + +#define MC_ARB_DRAM_TIMING_1 0x27f0 +#define MC_ARB_DRAM_TIMING_2 0x27f4 +#define MC_ARB_DRAM_TIMING_3 0x27f8 +#define MC_ARB_DRAM_TIMING2_1 0x27fc +#define MC_ARB_DRAM_TIMING2_2 0x2800 +#define MC_ARB_DRAM_TIMING2_3 0x2804 +#define MC_ARB_BURST_TIME 0x2808 +#define STATE0(x) ((x) << 0) +#define STATE0_MASK (0x1f << 0) +#define STATE0_SHIFT 0 +#define STATE1(x) ((x) << 5) +#define STATE1_MASK (0x1f << 5) +#define STATE1_SHIFT 5 +#define STATE2(x) ((x) << 10) +#define STATE2_MASK (0x1f << 10) +#define STATE2_SHIFT 10 +#define STATE3(x) ((x) << 15) +#define STATE3_MASK (0x1f << 15) +#define STATE3_SHIFT 15 + +#define MC_CG_DATAPORT 0x2884 + +#define MC_SEQ_RAS_TIMING 0x28a0 +#define MC_SEQ_CAS_TIMING 0x28a4 +#define MC_SEQ_MISC_TIMING 0x28a8 +#define MC_SEQ_MISC_TIMING2 0x28ac +#define MC_SEQ_PMG_TIMING 0x28b0 +#define MC_SEQ_RD_CTL_D0 0x28b4 +#define MC_SEQ_RD_CTL_D1 0x28b8 +#define MC_SEQ_WR_CTL_D0 0x28bc +#define MC_SEQ_WR_CTL_D1 0x28c0 + +#define MC_SEQ_MISC0 0x2a00 +#define MC_SEQ_MISC0_GDDR5_SHIFT 28 +#define MC_SEQ_MISC0_GDDR5_MASK 0xf0000000 +#define MC_SEQ_MISC0_GDDR5_VALUE 5 +#define MC_SEQ_MISC1 0x2a04 +#define MC_SEQ_RESERVE_M 0x2a08 +#define MC_PMG_CMD_EMRS 0x2a0c + +#define MC_SEQ_MISC3 0x2a2c + +#define MC_SEQ_MISC5 0x2a54 +#define MC_SEQ_MISC6 0x2a58 + +#define MC_SEQ_MISC7 0x2a64 + +#define MC_SEQ_RAS_TIMING_LP 0x2a6c +#define MC_SEQ_CAS_TIMING_LP 0x2a70 +#define MC_SEQ_MISC_TIMING_LP 0x2a74 +#define MC_SEQ_MISC_TIMING2_LP 0x2a78 +#define MC_SEQ_WR_CTL_D0_LP 0x2a7c +#define MC_SEQ_WR_CTL_D1_LP 0x2a80 +#define MC_SEQ_PMG_CMD_EMRS_LP 0x2a84 +#define MC_SEQ_PMG_CMD_MRS_LP 0x2a88 + +#define MC_PMG_CMD_MRS 0x2aac + +#define MC_SEQ_RD_CTL_D0_LP 0x2b1c +#define MC_SEQ_RD_CTL_D1_LP 0x2b20 + +#define MC_PMG_CMD_MRS1 0x2b44 +#define MC_SEQ_PMG_CMD_MRS1_LP 0x2b48 +#define MC_SEQ_PMG_TIMING_LP 0x2b4c + +#define MC_PMG_CMD_MRS2 0x2b5c +#define MC_SEQ_PMG_CMD_MRS2_LP 0x2b60 + +#define LB_SYNC_RESET_SEL 0x6b28 +#define LB_SYNC_RESET_SEL_MASK (3 << 0) +#define LB_SYNC_RESET_SEL_SHIFT 0 + +#define DC_STUTTER_CNTL 0x6b30 +#define DC_STUTTER_ENABLE_A (1 << 0) +#define DC_STUTTER_ENABLE_B (1 << 1) + +#define SQ_CAC_THRESHOLD 0x8e4c +#define VSP(x) ((x) << 0) +#define VSP_MASK (0xff << 0) +#define VSP_SHIFT 0 +#define VSP0(x) ((x) << 8) +#define VSP0_MASK (0xff << 8) +#define VSP0_SHIFT 8 +#define GPR(x) ((x) << 16) +#define GPR_MASK (0xff << 16) +#define GPR_SHIFT 16 + +#define SQ_POWER_THROTTLE 0x8e58 +#define MIN_POWER(x) ((x) << 0) +#define MIN_POWER_MASK (0x3fff << 0) +#define MIN_POWER_SHIFT 0 +#define MAX_POWER(x) ((x) << 16) +#define MAX_POWER_MASK (0x3fff << 16) +#define MAX_POWER_SHIFT 0 +#define SQ_POWER_THROTTLE2 0x8e5c +#define MAX_POWER_DELTA(x) ((x) << 0) +#define MAX_POWER_DELTA_MASK (0x3fff << 0) +#define MAX_POWER_DELTA_SHIFT 0 +#define STI_SIZE(x) ((x) << 16) +#define STI_SIZE_MASK (0x3ff << 16) +#define STI_SIZE_SHIFT 16 +#define LTI_RATIO(x) ((x) << 27) +#define LTI_RATIO_MASK (0xf << 27) +#define LTI_RATIO_SHIFT 27 + +/* CG indirect registers */ +#define CG_CAC_REGION_1_WEIGHT_0 0x83 +#define WEIGHT_TCP_SIG0(x) ((x) << 0) +#define WEIGHT_TCP_SIG0_MASK (0x3f << 0) +#define WEIGHT_TCP_SIG0_SHIFT 0 +#define WEIGHT_TCP_SIG1(x) ((x) << 6) +#define WEIGHT_TCP_SIG1_MASK (0x3f << 6) +#define WEIGHT_TCP_SIG1_SHIFT 6 +#define WEIGHT_TA_SIG(x) ((x) << 12) +#define WEIGHT_TA_SIG_MASK (0x3f << 12) +#define WEIGHT_TA_SIG_SHIFT 12 +#define CG_CAC_REGION_1_WEIGHT_1 0x84 +#define WEIGHT_TCC_EN0(x) ((x) << 0) +#define WEIGHT_TCC_EN0_MASK (0x3f << 0) +#define WEIGHT_TCC_EN0_SHIFT 0 +#define WEIGHT_TCC_EN1(x) ((x) << 6) +#define WEIGHT_TCC_EN1_MASK (0x3f << 6) +#define WEIGHT_TCC_EN1_SHIFT 6 +#define WEIGHT_TCC_EN2(x) ((x) << 12) +#define WEIGHT_TCC_EN2_MASK (0x3f << 12) +#define WEIGHT_TCC_EN2_SHIFT 12 +#define WEIGHT_TCC_EN3(x) ((x) << 18) +#define WEIGHT_TCC_EN3_MASK (0x3f << 18) +#define WEIGHT_TCC_EN3_SHIFT 18 +#define CG_CAC_REGION_2_WEIGHT_0 0x85 +#define WEIGHT_CB_EN0(x) ((x) << 0) +#define WEIGHT_CB_EN0_MASK (0x3f << 0) +#define WEIGHT_CB_EN0_SHIFT 0 +#define WEIGHT_CB_EN1(x) ((x) << 6) +#define WEIGHT_CB_EN1_MASK (0x3f << 6) +#define WEIGHT_CB_EN1_SHIFT 6 +#define WEIGHT_CB_EN2(x) ((x) << 12) +#define WEIGHT_CB_EN2_MASK (0x3f << 12) +#define WEIGHT_CB_EN2_SHIFT 12 +#define WEIGHT_CB_EN3(x) ((x) << 18) +#define WEIGHT_CB_EN3_MASK (0x3f << 18) +#define WEIGHT_CB_EN3_SHIFT 18 +#define CG_CAC_REGION_2_WEIGHT_1 0x86 +#define WEIGHT_DB_SIG0(x) ((x) << 0) +#define WEIGHT_DB_SIG0_MASK (0x3f << 0) +#define WEIGHT_DB_SIG0_SHIFT 0 +#define WEIGHT_DB_SIG1(x) ((x) << 6) +#define WEIGHT_DB_SIG1_MASK (0x3f << 6) +#define WEIGHT_DB_SIG1_SHIFT 6 +#define WEIGHT_DB_SIG2(x) ((x) << 12) +#define WEIGHT_DB_SIG2_MASK (0x3f << 12) +#define WEIGHT_DB_SIG2_SHIFT 12 +#define WEIGHT_DB_SIG3(x) ((x) << 18) +#define WEIGHT_DB_SIG3_MASK (0x3f << 18) +#define WEIGHT_DB_SIG3_SHIFT 18 +#define CG_CAC_REGION_2_WEIGHT_2 0x87 +#define WEIGHT_SXM_SIG0(x) ((x) << 0) +#define WEIGHT_SXM_SIG0_MASK (0x3f << 0) +#define WEIGHT_SXM_SIG0_SHIFT 0 +#define WEIGHT_SXM_SIG1(x) ((x) << 6) +#define WEIGHT_SXM_SIG1_MASK (0x3f << 6) +#define WEIGHT_SXM_SIG1_SHIFT 6 +#define WEIGHT_SXM_SIG2(x) ((x) << 12) +#define WEIGHT_SXM_SIG2_MASK (0x3f << 12) +#define WEIGHT_SXM_SIG2_SHIFT 12 +#define WEIGHT_SXS_SIG0(x) ((x) << 18) +#define WEIGHT_SXS_SIG0_MASK (0x3f << 18) +#define WEIGHT_SXS_SIG0_SHIFT 18 +#define WEIGHT_SXS_SIG1(x) ((x) << 24) +#define WEIGHT_SXS_SIG1_MASK (0x3f << 24) +#define WEIGHT_SXS_SIG1_SHIFT 24 +#define CG_CAC_REGION_3_WEIGHT_0 0x88 +#define WEIGHT_XBR_0(x) ((x) << 0) +#define WEIGHT_XBR_0_MASK (0x3f << 0) +#define WEIGHT_XBR_0_SHIFT 0 +#define WEIGHT_XBR_1(x) ((x) << 6) +#define WEIGHT_XBR_1_MASK (0x3f << 6) +#define WEIGHT_XBR_1_SHIFT 6 +#define WEIGHT_XBR_2(x) ((x) << 12) +#define WEIGHT_XBR_2_MASK (0x3f << 12) +#define WEIGHT_XBR_2_SHIFT 12 +#define WEIGHT_SPI_SIG0(x) ((x) << 18) +#define WEIGHT_SPI_SIG0_MASK (0x3f << 18) +#define WEIGHT_SPI_SIG0_SHIFT 18 +#define CG_CAC_REGION_3_WEIGHT_1 0x89 +#define WEIGHT_SPI_SIG1(x) ((x) << 0) +#define WEIGHT_SPI_SIG1_MASK (0x3f << 0) +#define WEIGHT_SPI_SIG1_SHIFT 0 +#define WEIGHT_SPI_SIG2(x) ((x) << 6) +#define WEIGHT_SPI_SIG2_MASK (0x3f << 6) +#define WEIGHT_SPI_SIG2_SHIFT 6 +#define WEIGHT_SPI_SIG3(x) ((x) << 12) +#define WEIGHT_SPI_SIG3_MASK (0x3f << 12) +#define WEIGHT_SPI_SIG3_SHIFT 12 +#define WEIGHT_SPI_SIG4(x) ((x) << 18) +#define WEIGHT_SPI_SIG4_MASK (0x3f << 18) +#define WEIGHT_SPI_SIG4_SHIFT 18 +#define WEIGHT_SPI_SIG5(x) ((x) << 24) +#define WEIGHT_SPI_SIG5_MASK (0x3f << 24) +#define WEIGHT_SPI_SIG5_SHIFT 24 +#define CG_CAC_REGION_4_WEIGHT_0 0x8a +#define WEIGHT_LDS_SIG0(x) ((x) << 0) +#define WEIGHT_LDS_SIG0_MASK (0x3f << 0) +#define WEIGHT_LDS_SIG0_SHIFT 0 +#define WEIGHT_LDS_SIG1(x) ((x) << 6) +#define WEIGHT_LDS_SIG1_MASK (0x3f << 6) +#define WEIGHT_LDS_SIG1_SHIFT 6 +#define WEIGHT_SC(x) ((x) << 24) +#define WEIGHT_SC_MASK (0x3f << 24) +#define WEIGHT_SC_SHIFT 24 +#define CG_CAC_REGION_4_WEIGHT_1 0x8b +#define WEIGHT_BIF(x) ((x) << 0) +#define WEIGHT_BIF_MASK (0x3f << 0) +#define WEIGHT_BIF_SHIFT 0 +#define WEIGHT_CP(x) ((x) << 6) +#define WEIGHT_CP_MASK (0x3f << 6) +#define WEIGHT_CP_SHIFT 6 +#define WEIGHT_PA_SIG0(x) ((x) << 12) +#define WEIGHT_PA_SIG0_MASK (0x3f << 12) +#define WEIGHT_PA_SIG0_SHIFT 12 +#define WEIGHT_PA_SIG1(x) ((x) << 18) +#define WEIGHT_PA_SIG1_MASK (0x3f << 18) +#define WEIGHT_PA_SIG1_SHIFT 18 +#define WEIGHT_VGT_SIG0(x) ((x) << 24) +#define WEIGHT_VGT_SIG0_MASK (0x3f << 24) +#define WEIGHT_VGT_SIG0_SHIFT 24 +#define CG_CAC_REGION_4_WEIGHT_2 0x8c +#define WEIGHT_VGT_SIG1(x) ((x) << 0) +#define WEIGHT_VGT_SIG1_MASK (0x3f << 0) +#define WEIGHT_VGT_SIG1_SHIFT 0 +#define WEIGHT_VGT_SIG2(x) ((x) << 6) +#define WEIGHT_VGT_SIG2_MASK (0x3f << 6) +#define WEIGHT_VGT_SIG2_SHIFT 6 +#define WEIGHT_DC_SIG0(x) ((x) << 12) +#define WEIGHT_DC_SIG0_MASK (0x3f << 12) +#define WEIGHT_DC_SIG0_SHIFT 12 +#define WEIGHT_DC_SIG1(x) ((x) << 18) +#define WEIGHT_DC_SIG1_MASK (0x3f << 18) +#define WEIGHT_DC_SIG1_SHIFT 18 +#define WEIGHT_DC_SIG2(x) ((x) << 24) +#define WEIGHT_DC_SIG2_MASK (0x3f << 24) +#define WEIGHT_DC_SIG2_SHIFT 24 +#define CG_CAC_REGION_4_WEIGHT_3 0x8d +#define WEIGHT_DC_SIG3(x) ((x) << 0) +#define WEIGHT_DC_SIG3_MASK (0x3f << 0) +#define WEIGHT_DC_SIG3_SHIFT 0 +#define WEIGHT_UVD_SIG0(x) ((x) << 6) +#define WEIGHT_UVD_SIG0_MASK (0x3f << 6) +#define WEIGHT_UVD_SIG0_SHIFT 6 +#define WEIGHT_UVD_SIG1(x) ((x) << 12) +#define WEIGHT_UVD_SIG1_MASK (0x3f << 12) +#define WEIGHT_UVD_SIG1_SHIFT 12 +#define WEIGHT_SPARE0(x) ((x) << 18) +#define WEIGHT_SPARE0_MASK (0x3f << 18) +#define WEIGHT_SPARE0_SHIFT 18 +#define WEIGHT_SPARE1(x) ((x) << 24) +#define WEIGHT_SPARE1_MASK (0x3f << 24) +#define WEIGHT_SPARE1_SHIFT 24 +#define CG_CAC_REGION_5_WEIGHT_0 0x8e +#define WEIGHT_SQ_VSP(x) ((x) << 0) +#define WEIGHT_SQ_VSP_MASK (0x3fff << 0) +#define WEIGHT_SQ_VSP_SHIFT 0 +#define WEIGHT_SQ_VSP0(x) ((x) << 14) +#define WEIGHT_SQ_VSP0_MASK (0x3fff << 14) +#define WEIGHT_SQ_VSP0_SHIFT 14 +#define CG_CAC_REGION_4_OVERRIDE_4 0xab +#define OVR_MODE_SPARE_0(x) ((x) << 16) +#define OVR_MODE_SPARE_0_MASK (0x1 << 16) +#define OVR_MODE_SPARE_0_SHIFT 16 +#define OVR_VAL_SPARE_0(x) ((x) << 17) +#define OVR_VAL_SPARE_0_MASK (0x1 << 17) +#define OVR_VAL_SPARE_0_SHIFT 17 +#define OVR_MODE_SPARE_1(x) ((x) << 18) +#define OVR_MODE_SPARE_1_MASK (0x3f << 18) +#define OVR_MODE_SPARE_1_SHIFT 18 +#define OVR_VAL_SPARE_1(x) ((x) << 19) +#define OVR_VAL_SPARE_1_MASK (0x3f << 19) +#define OVR_VAL_SPARE_1_SHIFT 19 +#define CG_CAC_REGION_5_WEIGHT_1 0xb7 +#define WEIGHT_SQ_GPR(x) ((x) << 0) +#define WEIGHT_SQ_GPR_MASK (0x3fff << 0) +#define WEIGHT_SQ_GPR_SHIFT 0 +#define WEIGHT_SQ_LDS(x) ((x) << 14) +#define WEIGHT_SQ_LDS_MASK (0x3fff << 14) +#define WEIGHT_SQ_LDS_SHIFT 14 + +/* PCIE link stuff */ +#define PCIE_LC_TRAINING_CNTL 0xa1 /* PCIE_P */ +#define PCIE_LC_LINK_WIDTH_CNTL 0xa2 /* PCIE_P */ +# define LC_LINK_WIDTH_SHIFT 0 +# define LC_LINK_WIDTH_MASK 0x7 +# define LC_LINK_WIDTH_X0 0 +# define LC_LINK_WIDTH_X1 1 +# define LC_LINK_WIDTH_X2 2 +# define LC_LINK_WIDTH_X4 3 +# define LC_LINK_WIDTH_X8 4 +# define LC_LINK_WIDTH_X16 6 +# define LC_LINK_WIDTH_RD_SHIFT 4 +# define LC_LINK_WIDTH_RD_MASK 0x70 +# define LC_RECONFIG_ARC_MISSING_ESCAPE (1 << 7) +# define LC_RECONFIG_NOW (1 << 8) +# define LC_RENEGOTIATION_SUPPORT (1 << 9) +# define LC_RENEGOTIATE_EN (1 << 10) +# define LC_SHORT_RECONFIG_EN (1 << 11) +# define LC_UPCONFIGURE_SUPPORT (1 << 12) +# define LC_UPCONFIGURE_DIS (1 << 13) +#define PCIE_LC_SPEED_CNTL 0xa4 /* PCIE_P */ +# define LC_GEN2_EN_STRAP (1 << 0) +# define LC_TARGET_LINK_SPEED_OVERRIDE_EN (1 << 1) +# define LC_FORCE_EN_HW_SPEED_CHANGE (1 << 5) +# define LC_FORCE_DIS_HW_SPEED_CHANGE (1 << 6) +# define LC_SPEED_CHANGE_ATTEMPTS_ALLOWED_MASK (0x3 << 8) +# define LC_SPEED_CHANGE_ATTEMPTS_ALLOWED_SHIFT 3 +# define LC_CURRENT_DATA_RATE (1 << 11) +# define LC_HW_VOLTAGE_IF_CONTROL(x) ((x) << 12) +# define LC_HW_VOLTAGE_IF_CONTROL_MASK (3 << 12) +# define LC_HW_VOLTAGE_IF_CONTROL_SHIFT 12 +# define LC_VOLTAGE_TIMER_SEL_MASK (0xf << 14) +# define LC_CLR_FAILED_SPD_CHANGE_CNT (1 << 21) +# define LC_OTHER_SIDE_EVER_SENT_GEN2 (1 << 23) +# define LC_OTHER_SIDE_SUPPORTS_GEN2 (1 << 24) +#define MM_CFGREGS_CNTL 0x544c +# define MM_WR_TO_CFG_EN (1 << 3) +#define LINK_CNTL2 0x88 /* F0 */ +# define TARGET_LINK_SPEED_MASK (0xf << 0) +# define SELECTABLE_DEEMPHASIS (1 << 6) + /* * UVD */ diff --git a/drivers/gpu/drm/radeon/nislands_smc.h b/drivers/gpu/drm/radeon/nislands_smc.h new file mode 100644 index 0000000..3cf8fc0 --- /dev/null +++ b/drivers/gpu/drm/radeon/nislands_smc.h @@ -0,0 +1,329 @@ +/* + * Copyright 2012 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ +#ifndef __NISLANDS_SMC_H__ +#define __NISLANDS_SMC_H__ + +#pragma pack(push, 1) + +#define NISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE 16 + +struct PP_NIslands_Dpm2PerfLevel +{ + uint8_t MaxPS; + uint8_t TgtAct; + uint8_t MaxPS_StepInc; + uint8_t MaxPS_StepDec; + uint8_t PSST; + uint8_t NearTDPDec; + uint8_t AboveSafeInc; + uint8_t BelowSafeInc; + uint8_t PSDeltaLimit; + uint8_t PSDeltaWin; + uint8_t Reserved[6]; +}; + +typedef struct PP_NIslands_Dpm2PerfLevel PP_NIslands_Dpm2PerfLevel; + +struct PP_NIslands_DPM2Parameters +{ + uint32_t TDPLimit; + uint32_t NearTDPLimit; + uint32_t SafePowerLimit; + uint32_t PowerBoostLimit; +}; +typedef struct PP_NIslands_DPM2Parameters PP_NIslands_DPM2Parameters; + +struct NISLANDS_SMC_SCLK_VALUE +{ + uint32_t vCG_SPLL_FUNC_CNTL; + uint32_t vCG_SPLL_FUNC_CNTL_2; + uint32_t vCG_SPLL_FUNC_CNTL_3; + uint32_t vCG_SPLL_FUNC_CNTL_4; + uint32_t vCG_SPLL_SPREAD_SPECTRUM; + uint32_t vCG_SPLL_SPREAD_SPECTRUM_2; + uint32_t sclk_value; +}; + +typedef struct NISLANDS_SMC_SCLK_VALUE NISLANDS_SMC_SCLK_VALUE; + +struct NISLANDS_SMC_MCLK_VALUE +{ + uint32_t vMPLL_FUNC_CNTL; + uint32_t vMPLL_FUNC_CNTL_1; + uint32_t vMPLL_FUNC_CNTL_2; + uint32_t vMPLL_AD_FUNC_CNTL; + uint32_t vMPLL_AD_FUNC_CNTL_2; + uint32_t vMPLL_DQ_FUNC_CNTL; + uint32_t vMPLL_DQ_FUNC_CNTL_2; + uint32_t vMCLK_PWRMGT_CNTL; + uint32_t vDLL_CNTL; + uint32_t vMPLL_SS; + uint32_t vMPLL_SS2; + uint32_t mclk_value; +}; + +typedef struct NISLANDS_SMC_MCLK_VALUE NISLANDS_SMC_MCLK_VALUE; + +struct NISLANDS_SMC_VOLTAGE_VALUE +{ + uint16_t value; + uint8_t index; + uint8_t padding; +}; + +typedef struct NISLANDS_SMC_VOLTAGE_VALUE NISLANDS_SMC_VOLTAGE_VALUE; + +struct NISLANDS_SMC_HW_PERFORMANCE_LEVEL +{ + uint8_t arbValue; + uint8_t ACIndex; + uint8_t displayWatermark; + uint8_t gen2PCIE; + uint8_t reserved1; + uint8_t reserved2; + uint8_t strobeMode; + uint8_t mcFlags; + uint32_t aT; + uint32_t bSP; + NISLANDS_SMC_SCLK_VALUE sclk; + NISLANDS_SMC_MCLK_VALUE mclk; + NISLANDS_SMC_VOLTAGE_VALUE vddc; + NISLANDS_SMC_VOLTAGE_VALUE mvdd; + NISLANDS_SMC_VOLTAGE_VALUE vddci; + NISLANDS_SMC_VOLTAGE_VALUE std_vddc; + uint32_t powergate_en; + uint8_t hUp; + uint8_t hDown; + uint8_t stateFlags; + uint8_t arbRefreshState; + uint32_t SQPowerThrottle; + uint32_t SQPowerThrottle_2; + uint32_t reserved[2]; + PP_NIslands_Dpm2PerfLevel dpm2; +}; + +#define NISLANDS_SMC_STROBE_RATIO 0x0F +#define NISLANDS_SMC_STROBE_ENABLE 0x10 + +#define NISLANDS_SMC_MC_EDC_RD_FLAG 0x01 +#define NISLANDS_SMC_MC_EDC_WR_FLAG 0x02 +#define NISLANDS_SMC_MC_RTT_ENABLE 0x04 +#define NISLANDS_SMC_MC_STUTTER_EN 0x08 + +typedef struct NISLANDS_SMC_HW_PERFORMANCE_LEVEL NISLANDS_SMC_HW_PERFORMANCE_LEVEL; + +struct NISLANDS_SMC_SWSTATE +{ + uint8_t flags; + uint8_t levelCount; + uint8_t padding2; + uint8_t padding3; + NISLANDS_SMC_HW_PERFORMANCE_LEVEL levels[1]; +}; + +typedef struct NISLANDS_SMC_SWSTATE NISLANDS_SMC_SWSTATE; + +#define NISLANDS_SMC_VOLTAGEMASK_VDDC 0 +#define NISLANDS_SMC_VOLTAGEMASK_MVDD 1 +#define NISLANDS_SMC_VOLTAGEMASK_VDDCI 2 +#define NISLANDS_SMC_VOLTAGEMASK_MAX 4 + +struct NISLANDS_SMC_VOLTAGEMASKTABLE +{ + uint8_t highMask[NISLANDS_SMC_VOLTAGEMASK_MAX]; + uint32_t lowMask[NISLANDS_SMC_VOLTAGEMASK_MAX]; +}; + +typedef struct NISLANDS_SMC_VOLTAGEMASKTABLE NISLANDS_SMC_VOLTAGEMASKTABLE; + +#define NISLANDS_MAX_NO_VREG_STEPS 32 + +struct NISLANDS_SMC_STATETABLE +{ + uint8_t thermalProtectType; + uint8_t systemFlags; + uint8_t maxVDDCIndexInPPTable; + uint8_t extraFlags; + uint8_t highSMIO[NISLANDS_MAX_NO_VREG_STEPS]; + uint32_t lowSMIO[NISLANDS_MAX_NO_VREG_STEPS]; + NISLANDS_SMC_VOLTAGEMASKTABLE voltageMaskTable; + PP_NIslands_DPM2Parameters dpm2Params; + NISLANDS_SMC_SWSTATE initialState; + NISLANDS_SMC_SWSTATE ACPIState; + NISLANDS_SMC_SWSTATE ULVState; + NISLANDS_SMC_SWSTATE driverState; + NISLANDS_SMC_HW_PERFORMANCE_LEVEL dpmLevels[NISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE - 1]; +}; + +typedef struct NISLANDS_SMC_STATETABLE NISLANDS_SMC_STATETABLE; + +#define NI_SMC_SOFT_REGISTERS_START 0x108 + +#define NI_SMC_SOFT_REGISTER_mclk_chg_timeout 0x0 +#define NI_SMC_SOFT_REGISTER_delay_bbias 0xC +#define NI_SMC_SOFT_REGISTER_delay_vreg 0x10 +#define NI_SMC_SOFT_REGISTER_delay_acpi 0x2C +#define NI_SMC_SOFT_REGISTER_seq_index 0x64 +#define NI_SMC_SOFT_REGISTER_mvdd_chg_time 0x68 +#define NI_SMC_SOFT_REGISTER_mclk_switch_lim 0x78 +#define NI_SMC_SOFT_REGISTER_watermark_threshold 0x80 +#define NI_SMC_SOFT_REGISTER_mc_block_delay 0x84 +#define NI_SMC_SOFT_REGISTER_uvd_enabled 0x98 + +#define SMC_NISLANDS_MC_TPP_CAC_NUM_OF_ENTRIES 16 +#define SMC_NISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES 16 +#define SMC_NISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES 16 +#define SMC_NISLANDS_BIF_LUT_NUM_OF_ENTRIES 4 + +struct SMC_NISLANDS_MC_TPP_CAC_TABLE +{ + uint32_t tpp[SMC_NISLANDS_MC_TPP_CAC_NUM_OF_ENTRIES]; + uint32_t cacValue[SMC_NISLANDS_MC_TPP_CAC_NUM_OF_ENTRIES]; +}; + +typedef struct SMC_NISLANDS_MC_TPP_CAC_TABLE SMC_NISLANDS_MC_TPP_CAC_TABLE; + + +struct PP_NIslands_CACTABLES +{ + uint32_t cac_bif_lut[SMC_NISLANDS_BIF_LUT_NUM_OF_ENTRIES]; + uint32_t cac_lkge_lut[SMC_NISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES][SMC_NISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES]; + + uint32_t pwr_const; + + uint32_t dc_cacValue; + uint32_t bif_cacValue; + uint32_t lkge_pwr; + + uint8_t cac_width; + uint8_t window_size_p2; + + uint8_t num_drop_lsb; + uint8_t padding_0; + + uint32_t last_power; + + uint8_t AllowOvrflw; + uint8_t MCWrWeight; + uint8_t MCRdWeight; + uint8_t padding_1[9]; + + uint8_t enableWinAvg; + uint8_t numWin_TDP; + uint8_t l2numWin_TDP; + uint8_t WinIndex; + + uint32_t dynPwr_TDP[4]; + uint32_t lkgePwr_TDP[4]; + uint32_t power_TDP[4]; + uint32_t avg_dynPwr_TDP; + uint32_t avg_lkgePwr_TDP; + uint32_t avg_power_TDP; + uint32_t lts_power_TDP; + uint8_t lts_truncate_n; + uint8_t padding_2[7]; +}; + +typedef struct PP_NIslands_CACTABLES PP_NIslands_CACTABLES; + +#define SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE 32 +#define SMC_NISLANDS_MC_REGISTER_ARRAY_SET_COUNT 20 + +struct SMC_NIslands_MCRegisterAddress +{ + uint16_t s0; + uint16_t s1; +}; + +typedef struct SMC_NIslands_MCRegisterAddress SMC_NIslands_MCRegisterAddress; + + +struct SMC_NIslands_MCRegisterSet +{ + uint32_t value[SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE]; +}; + +typedef struct SMC_NIslands_MCRegisterSet SMC_NIslands_MCRegisterSet; + +struct SMC_NIslands_MCRegisters +{ + uint8_t last; + uint8_t reserved[3]; + SMC_NIslands_MCRegisterAddress address[SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE]; + SMC_NIslands_MCRegisterSet data[SMC_NISLANDS_MC_REGISTER_ARRAY_SET_COUNT]; +}; + +typedef struct SMC_NIslands_MCRegisters SMC_NIslands_MCRegisters; + +struct SMC_NIslands_MCArbDramTimingRegisterSet +{ + uint32_t mc_arb_dram_timing; + uint32_t mc_arb_dram_timing2; + uint8_t mc_arb_rfsh_rate; + uint8_t padding[3]; +}; + +typedef struct SMC_NIslands_MCArbDramTimingRegisterSet SMC_NIslands_MCArbDramTimingRegisterSet; + +struct SMC_NIslands_MCArbDramTimingRegisters +{ + uint8_t arb_current; + uint8_t reserved[3]; + SMC_NIslands_MCArbDramTimingRegisterSet data[20]; +}; + +typedef struct SMC_NIslands_MCArbDramTimingRegisters SMC_NIslands_MCArbDramTimingRegisters; + +struct SMC_NISLANDS_SPLL_DIV_TABLE +{ + uint32_t freq[256]; + uint32_t ss[256]; +}; + +#define SMC_NISLANDS_SPLL_DIV_TABLE_FBDIV_MASK 0x01ffffff +#define SMC_NISLANDS_SPLL_DIV_TABLE_FBDIV_SHIFT 0 +#define SMC_NISLANDS_SPLL_DIV_TABLE_PDIV_MASK 0xfe000000 +#define SMC_NISLANDS_SPLL_DIV_TABLE_PDIV_SHIFT 25 +#define SMC_NISLANDS_SPLL_DIV_TABLE_CLKV_MASK 0x000fffff +#define SMC_NISLANDS_SPLL_DIV_TABLE_CLKV_SHIFT 0 +#define SMC_NISLANDS_SPLL_DIV_TABLE_CLKS_MASK 0xfff00000 +#define SMC_NISLANDS_SPLL_DIV_TABLE_CLKS_SHIFT 20 + +typedef struct SMC_NISLANDS_SPLL_DIV_TABLE SMC_NISLANDS_SPLL_DIV_TABLE; + +#define NISLANDS_SMC_FIRMWARE_HEADER_LOCATION 0x100 + +#define NISLANDS_SMC_FIRMWARE_HEADER_version 0x0 +#define NISLANDS_SMC_FIRMWARE_HEADER_flags 0x4 +#define NISLANDS_SMC_FIRMWARE_HEADER_softRegisters 0x8 +#define NISLANDS_SMC_FIRMWARE_HEADER_stateTable 0xC +#define NISLANDS_SMC_FIRMWARE_HEADER_fanTable 0x10 +#define NISLANDS_SMC_FIRMWARE_HEADER_cacTable 0x14 +#define NISLANDS_SMC_FIRMWARE_HEADER_mcRegisterTable 0x20 +#define NISLANDS_SMC_FIRMWARE_HEADER_mcArbDramAutoRefreshTable 0x2C +#define NISLANDS_SMC_FIRMWARE_HEADER_spllTable 0x30 + +#pragma pack(pop) + +#endif + diff --git a/drivers/gpu/drm/radeon/ppsmc.h b/drivers/gpu/drm/radeon/ppsmc.h index 3d0786f..607982a 100644 --- a/drivers/gpu/drm/radeon/ppsmc.h +++ b/drivers/gpu/drm/radeon/ppsmc.h @@ -46,6 +46,7 @@ #define PPSMC_DISPLAY_WATERMARK_HIGH 1
#define PPSMC_STATEFLAG_AUTO_PULSE_SKIP 0x01 +#define PPSMC_STATEFLAG_POWERBOOST 0x02
#define PPSMC_Result_OK ((uint8_t)0x01) #define PPSMC_Result_Failed ((uint8_t)0xFF) @@ -56,17 +57,28 @@ typedef uint8_t PPSMC_Result; #define PPSMC_MSG_Resume ((uint8_t)0x11) #define PPSMC_MSG_TwoLevelsDisabled ((uint8_t)0x15) #define PPSMC_MSG_EnableThermalInterrupt ((uint8_t)0x16) +#define PPSMC_MSG_RunningOnAC ((uint8_t)0x17) #define PPSMC_MSG_SwitchToSwState ((uint8_t)0x20) #define PPSMC_MSG_SwitchToInitialState ((uint8_t)0x40) #define PPSMC_MSG_NoForcedLevel ((uint8_t)0x41) #define PPSMC_MSG_SwitchToMinimumPower ((uint8_t)0x51) #define PPSMC_MSG_ResumeFromMinimumPower ((uint8_t)0x52) +#define PPSMC_MSG_EnableCac ((uint8_t)0x53) +#define PPSMC_MSG_DisableCac ((uint8_t)0x54) +#define PPSMC_TDPClampingActive ((uint8_t)0x59) +#define PPSMC_TDPClampingInactive ((uint8_t)0x5A) #define PPSMC_MSG_NoDisplay ((uint8_t)0x5D) #define PPSMC_MSG_HasDisplay ((uint8_t)0x5E) +#define PPSMC_MSG_UVDPowerOFF ((uint8_t)0x60) +#define PPSMC_MSG_UVDPowerON ((uint8_t)0x61) #define PPSMC_MSG_EnableULV ((uint8_t)0x62) #define PPSMC_MSG_DisableULV ((uint8_t)0x63) #define PPSMC_MSG_EnterULV ((uint8_t)0x64) #define PPSMC_MSG_ExitULV ((uint8_t)0x65) +#define PPSMC_CACLongTermAvgEnable ((uint8_t)0x6E) +#define PPSMC_CACLongTermAvgDisable ((uint8_t)0x6F) +#define PPSMC_MSG_CollectCAC_PowerCorreln ((uint8_t)0x7A) +#define PPSMC_MSG_SetEnabledLevels ((uint8_t)0x82) #define PPSMC_MSG_ResetToDefaults ((uint8_t)0x84)
/* TN */ diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index 5dcbb28..e6703ef 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1804,6 +1804,18 @@ static struct radeon_asic cayman_asic = { .set_uvd_clocks = &evergreen_set_uvd_clocks, .get_temperature = &evergreen_get_temp, }, + .dpm = { + .init = &ni_dpm_init, + .setup_asic = &ni_dpm_setup_asic, + .enable = &ni_dpm_enable, + .disable = &ni_dpm_disable, + .set_power_state = &ni_dpm_set_power_state, + .display_configuration_changed = &cypress_dpm_display_configuration_changed, + .fini = &ni_dpm_fini, + .get_sclk = &ni_dpm_get_sclk, + .get_mclk = &ni_dpm_get_mclk, + .print_power_state = &ni_dpm_print_power_state, + }, .pflip = { .pre_page_flip = &evergreen_pre_page_flip, .page_flip = &evergreen_page_flip, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 4d3f93d..2fef64d 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -581,6 +581,16 @@ bool cayman_gfx_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring); bool cayman_dma_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring); void cayman_dma_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm);
+int ni_dpm_init(struct radeon_device *rdev); +void ni_dpm_setup_asic(struct radeon_device *rdev); +int ni_dpm_enable(struct radeon_device *rdev); +void ni_dpm_disable(struct radeon_device *rdev); +int ni_dpm_set_power_state(struct radeon_device *rdev); +void ni_dpm_fini(struct radeon_device *rdev); +u32 ni_dpm_get_sclk(struct radeon_device *rdev, bool low); +u32 ni_dpm_get_mclk(struct radeon_device *rdev, bool low); +void ni_dpm_print_power_state(struct radeon_device *rdev, + struct radeon_ps *ps); int trinity_dpm_init(struct radeon_device *rdev); int trinity_dpm_enable(struct radeon_device *rdev); void trinity_dpm_disable(struct radeon_device *rdev); diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 2eb41be..01ff18b 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -1100,6 +1100,7 @@ int radeon_pm_init(struct radeon_device *rdev) case CHIP_BARTS: case CHIP_TURKS: case CHIP_CAICOS: + case CHIP_CAYMAN: case CHIP_ARUBA: if (radeon_dpm == 1) rdev->pm.pm_method = PM_METHOD_DPM; diff --git a/drivers/gpu/drm/radeon/radeon_ucode.h b/drivers/gpu/drm/radeon/radeon_ucode.h index e592e27..51beb4c 100644 --- a/drivers/gpu/drm/radeon/radeon_ucode.h +++ b/drivers/gpu/drm/radeon/radeon_ucode.h @@ -100,4 +100,9 @@ #define CAICOS_SMC_INT_VECTOR_START 0xffc0 #define CAICOS_SMC_INT_VECTOR_SIZE 0x0040
+#define CAYMAN_SMC_UCODE_START 0x0100 +#define CAYMAN_SMC_UCODE_SIZE 0x79ec +#define CAYMAN_SMC_INT_VECTOR_START 0xffc0 +#define CAYMAN_SMC_INT_VECTOR_SIZE 0x0040 + #endif diff --git a/drivers/gpu/drm/radeon/rv770_smc.c b/drivers/gpu/drm/radeon/rv770_smc.c index 0078c59..ab95da5 100644 --- a/drivers/gpu/drm/radeon/rv770_smc.c +++ b/drivers/gpu/drm/radeon/rv770_smc.c @@ -254,6 +254,26 @@ static const u8 caicos_smc_int_vectors[] = 0x05, 0x0A, 0x05, 0x0A };
+static const u8 cayman_smc_int_vectors[] = +{ + 0x12, 0x05, 0x12, 0x05, + 0x12, 0x05, 0x12, 0x05, + 0x12, 0x05, 0x12, 0x05, + 0x12, 0x05, 0x12, 0x05, + 0x12, 0x05, 0x12, 0x05, + 0x12, 0x05, 0x12, 0x05, + 0x12, 0x05, 0x12, 0x05, + 0x12, 0x05, 0x12, 0x05, + 0x12, 0x05, 0x12, 0x05, + 0x12, 0x05, 0x12, 0x05, + 0x12, 0x05, 0x12, 0x05, + 0x12, 0x05, 0x12, 0x05, + 0x12, 0x05, 0x18, 0xEA, + 0x12, 0x20, 0x1C, 0x34, + 0x1C, 0x34, 0x08, 0x72, + 0x08, 0x72, 0x08, 0x72 +}; + int rv770_set_smc_sram_address(struct radeon_device *rdev, u16 smc_address, u16 limit) { @@ -544,6 +564,13 @@ int rv770_load_smc_ucode(struct radeon_device *rdev, int_vect_start_address = CAICOS_SMC_INT_VECTOR_START; int_vect_size = CAICOS_SMC_INT_VECTOR_SIZE; break; + case CHIP_CAYMAN: + ucode_start_address = CAYMAN_SMC_UCODE_START; + ucode_size = CAYMAN_SMC_UCODE_SIZE; + int_vect = (const u8 *)&cayman_smc_int_vectors; + int_vect_start_address = CAYMAN_SMC_INT_VECTOR_START; + int_vect_size = CAYMAN_SMC_INT_VECTOR_SIZE; + break; default: DRM_ERROR("unknown asic in smc ucode loader\n"); BUG();
On Wed, Jun 26, 2013 at 09:22:53AM -0400, alexdeucher@gmail.com wrote:
From: Alex Deucher alexander.deucher@amd.com
This adds dpm support for cayman asics. This includes:
- clockgating
- dynamic engine clock scaling
- dynamic memory clock scaling
- dynamic voltage scaling
- dynamic pcie gen1/gen2 switching (requires additional acpi support)
- power containment
- shader power scaling
Set radeon.dpm=1 to enable.
Signed-off-by: Alex Deucher alexander.deucher@amd.com
Reviewed-by: Jerome Glisse jglisse@redhat.com
drivers/gpu/drm/radeon/Makefile | 2 +- drivers/gpu/drm/radeon/btc_dpm.c | 36 +- drivers/gpu/drm/radeon/btc_dpm.h | 20 +- drivers/gpu/drm/radeon/cypress_dpm.c | 11 +- drivers/gpu/drm/radeon/cypress_dpm.h | 4 + drivers/gpu/drm/radeon/ni.c | 4 +- drivers/gpu/drm/radeon/ni_dpm.c | 4093 +++++++++++++++++++++++++++++++++ drivers/gpu/drm/radeon/ni_dpm.h | 233 ++ drivers/gpu/drm/radeon/nid.h | 552 +++++ drivers/gpu/drm/radeon/nislands_smc.h | 329 +++ drivers/gpu/drm/radeon/ppsmc.h | 12 + drivers/gpu/drm/radeon/radeon_asic.c | 12 + drivers/gpu/drm/radeon/radeon_asic.h | 10 + drivers/gpu/drm/radeon/radeon_pm.c | 1 + drivers/gpu/drm/radeon/radeon_ucode.h | 5 + drivers/gpu/drm/radeon/rv770_smc.c | 27 + 16 files changed, 5323 insertions(+), 28 deletions(-) create mode 100644 drivers/gpu/drm/radeon/ni_dpm.c create mode 100644 drivers/gpu/drm/radeon/ni_dpm.h create mode 100644 drivers/gpu/drm/radeon/nislands_smc.h
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index 2239ec2..32d1c7f 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile @@ -79,7 +79,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \ si_blit_shaders.o radeon_prime.o radeon_uvd.o cik.o cik_blit_shaders.o \ r600_dpm.o rs780_dpm.o rv6xx_dpm.o rv770_dpm.o rv730_dpm.o rv740_dpm.o \ rv770_smc.o cypress_dpm.o btc_dpm.o sumo_dpm.o sumo_smc.o trinity_dpm.o \
- trinity_smc.o
- trinity_smc.o ni_dpm.o
radeon-$(CONFIG_COMPAT) += radeon_ioc32.o radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o diff --git a/drivers/gpu/drm/radeon/btc_dpm.c b/drivers/gpu/drm/radeon/btc_dpm.c index 989592e..79f5ed4 100644 --- a/drivers/gpu/drm/radeon/btc_dpm.c +++ b/drivers/gpu/drm/radeon/btc_dpm.c @@ -1152,7 +1152,7 @@ static const u32 turks_sysls_enable[] =
#endif
-u32 btc_valid_sclk[] = +u32 btc_valid_sclk[40] = { 5000, 10000, 15000, 20000, 25000, 30000, 35000, 40000, 45000, 50000, 55000, 60000, 65000, 70000, 75000, 80000, 85000, 90000, 95000, 100000, @@ -1168,8 +1168,8 @@ static const struct radeon_blacklist_clocks btc_blacklist_clocks[] = { 25000, 30000, RADEON_SCLK_UP } };
-static void btc_apply_voltage_dependency_rules(struct radeon_clock_voltage_dependency_table *table,
u32 clock, u16 max_voltage, u16 *voltage)
+void btc_apply_voltage_dependency_rules(struct radeon_clock_voltage_dependency_table *table,
u32 clock, u16 max_voltage, u16 *voltage)
{ u32 i;
@@ -1219,9 +1219,9 @@ static u32 btc_get_valid_sclk(struct radeon_device *rdev, max_sclk, requested_sclk); }
-static void btc_skip_blacklist_clocks(struct radeon_device *rdev,
const u32 max_sclk, const u32 max_mclk,
u32 *sclk, u32 *mclk)
+void btc_skip_blacklist_clocks(struct radeon_device *rdev,
const u32 max_sclk, const u32 max_mclk,
u32 *sclk, u32 *mclk)
{ int i, num_blacklist_clocks;
@@ -1246,9 +1246,9 @@ static void btc_skip_blacklist_clocks(struct radeon_device *rdev, } }
-static void btc_adjust_clock_combinations(struct radeon_device *rdev,
const struct radeon_clock_and_voltage_limits *max_limits,
struct rv7xx_pl *pl)
+void btc_adjust_clock_combinations(struct radeon_device *rdev,
const struct radeon_clock_and_voltage_limits *max_limits,
struct rv7xx_pl *pl)
{
if ((pl->mclk == 0) || (pl->sclk == 0)) @@ -1285,9 +1285,9 @@ static u16 btc_find_voltage(struct atom_voltage_table *table, u16 voltage) return table->entries[table->count - 1].value; }
-static void btc_apply_voltage_delta_rules(struct radeon_device *rdev,
u16 max_vddc, u16 max_vddci,
u16 *vddc, u16 *vddci)
+void btc_apply_voltage_delta_rules(struct radeon_device *rdev,
u16 max_vddc, u16 max_vddci,
u16 *vddc, u16 *vddci)
{ struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); u16 new_voltage; @@ -1417,8 +1417,8 @@ static int btc_populate_smc_acpi_state(struct radeon_device *rdev, return ret; }
-static void btc_program_mgcg_hw_sequence(struct radeon_device *rdev,
const u32 *sequence, u32 count)
+void btc_program_mgcg_hw_sequence(struct radeon_device *rdev,
const u32 *sequence, u32 count)
{ u32 i, length = count * 3; u32 tmp; @@ -1596,7 +1596,7 @@ static void btc_ls_clock_gating_enable(struct radeon_device *rdev, btc_program_mgcg_hw_sequence(rdev, p, count); }
-static bool btc_dpm_enabled(struct radeon_device *rdev) +bool btc_dpm_enabled(struct radeon_device *rdev) { if (rv770_is_smc_running(rdev)) return true; @@ -1692,7 +1692,7 @@ static void btc_set_at_for_uvd(struct radeon_device *rdev)
}
-static void btc_notify_uvd_to_smc(struct radeon_device *rdev) +void btc_notify_uvd_to_smc(struct radeon_device *rdev) { struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); @@ -1708,7 +1708,7 @@ static void btc_notify_uvd_to_smc(struct radeon_device *rdev) } }
-static int btc_reset_to_default(struct radeon_device *rdev) +int btc_reset_to_default(struct radeon_device *rdev) { if (rv770_send_msg_to_smc(rdev, PPSMC_MSG_ResetToDefaults) != PPSMC_Result_OK) return -EINVAL; @@ -1730,7 +1730,7 @@ static void btc_stop_smc(struct radeon_device *rdev) r7xx_stop_smc(rdev); }
-static void btc_read_arb_registers(struct radeon_device *rdev) +void btc_read_arb_registers(struct radeon_device *rdev) { struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); struct evergreen_arb_registers *arb_registers = diff --git a/drivers/gpu/drm/radeon/btc_dpm.h b/drivers/gpu/drm/radeon/btc_dpm.h index 807024d..c22d39f 100644 --- a/drivers/gpu/drm/radeon/btc_dpm.h +++ b/drivers/gpu/drm/radeon/btc_dpm.h @@ -33,6 +33,24 @@ #define BTC_CGULVPARAMETER_DFLT 0x00040035 #define BTC_CGULVCONTROL_DFLT 0x00001450
-extern u32 btc_valid_sclk[]; +extern u32 btc_valid_sclk[40];
+void btc_read_arb_registers(struct radeon_device *rdev); +void btc_program_mgcg_hw_sequence(struct radeon_device *rdev,
const u32 *sequence, u32 count);
+void btc_skip_blacklist_clocks(struct radeon_device *rdev,
const u32 max_sclk, const u32 max_mclk,
u32 *sclk, u32 *mclk);
+void btc_adjust_clock_combinations(struct radeon_device *rdev,
const struct radeon_clock_and_voltage_limits *max_limits,
struct rv7xx_pl *pl);
+void btc_apply_voltage_dependency_rules(struct radeon_clock_voltage_dependency_table *table,
u32 clock, u16 max_voltage, u16 *voltage);
+void btc_apply_voltage_delta_rules(struct radeon_device *rdev,
u16 max_vddc, u16 max_vddci,
u16 *vddc, u16 *vddci);
+bool btc_dpm_enabled(struct radeon_device *rdev); +int btc_reset_to_default(struct radeon_device *rdev); +void btc_notify_uvd_to_smc(struct radeon_device *rdev);
#endif diff --git a/drivers/gpu/drm/radeon/cypress_dpm.c b/drivers/gpu/drm/radeon/cypress_dpm.c index 403ee15..22297b1 100644 --- a/drivers/gpu/drm/radeon/cypress_dpm.c +++ b/drivers/gpu/drm/radeon/cypress_dpm.c @@ -45,9 +45,6 @@ struct rv7xx_ps *rv770_get_ps(struct radeon_ps *rps); struct rv7xx_power_info *rv770_get_pi(struct radeon_device *rdev); struct evergreen_power_info *evergreen_get_pi(struct radeon_device *rdev);
-static u8 cypress_get_mclk_frequency_ratio(struct radeon_device *rdev,
u32 memory_clock, bool strobe_mode);
static void cypress_enable_bif_dynamic_pcie_gen2(struct radeon_device *rdev, bool enable) { @@ -416,7 +413,7 @@ static int cypress_populate_voltage_value(struct radeon_device *rdev, return 0; }
-static u8 cypress_get_strobe_mode_settings(struct radeon_device *rdev, u32 mclk) +u8 cypress_get_strobe_mode_settings(struct radeon_device *rdev, u32 mclk) { struct rv7xx_power_info *pi = rv770_get_pi(rdev); u8 result = 0; @@ -434,7 +431,7 @@ static u8 cypress_get_strobe_mode_settings(struct radeon_device *rdev, u32 mclk) return result; }
-static u32 cypress_map_clkf_to_ibias(struct radeon_device *rdev, u32 clkf) +u32 cypress_map_clkf_to_ibias(struct radeon_device *rdev, u32 clkf) { u32 ref_clk = rdev->clock.mpll.reference_freq; u32 vco = clkf * ref_clk; @@ -603,8 +600,8 @@ static int cypress_populate_mclk_value(struct radeon_device *rdev, return 0; }
-static u8 cypress_get_mclk_frequency_ratio(struct radeon_device *rdev,
u32 memory_clock, bool strobe_mode)
+u8 cypress_get_mclk_frequency_ratio(struct radeon_device *rdev,
u32 memory_clock, bool strobe_mode)
{ u8 mc_para_index;
diff --git a/drivers/gpu/drm/radeon/cypress_dpm.h b/drivers/gpu/drm/radeon/cypress_dpm.h index 9e24d7a..9b6198e 100644 --- a/drivers/gpu/drm/radeon/cypress_dpm.h +++ b/drivers/gpu/drm/radeon/cypress_dpm.h @@ -141,5 +141,9 @@ void cypress_enable_mclk_control(struct radeon_device *rdev, bool enable); void cypress_start_dpm(struct radeon_device *rdev); void cypress_advertise_gen2_capability(struct radeon_device *rdev); +u32 cypress_map_clkf_to_ibias(struct radeon_device *rdev, u32 clkf); +u8 cypress_get_mclk_frequency_ratio(struct radeon_device *rdev,
u32 memory_clock, bool strobe_mode);
+u8 cypress_get_strobe_mode_settings(struct radeon_device *rdev, u32 mclk);
#endif diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c index 7407762..ad65143 100644 --- a/drivers/gpu/drm/radeon/ni.c +++ b/drivers/gpu/drm/radeon/ni.c @@ -194,6 +194,7 @@ MODULE_FIRMWARE("radeon/CAYMAN_pfp.bin"); MODULE_FIRMWARE("radeon/CAYMAN_me.bin"); MODULE_FIRMWARE("radeon/CAYMAN_mc.bin"); MODULE_FIRMWARE("radeon/CAYMAN_rlc.bin"); +MODULE_FIRMWARE("radeon/CAYMAN_smc.bin"); MODULE_FIRMWARE("radeon/ARUBA_pfp.bin"); MODULE_FIRMWARE("radeon/ARUBA_me.bin"); MODULE_FIRMWARE("radeon/ARUBA_rlc.bin"); @@ -734,6 +735,7 @@ int ni_init_microcode(struct radeon_device *rdev) me_req_size = CAYMAN_PM4_UCODE_SIZE * 4; rlc_req_size = CAYMAN_RLC_UCODE_SIZE * 4; mc_req_size = CAYMAN_MC_UCODE_SIZE * 4;
break; case CHIP_ARUBA: chip_name = "ARUBA";smc_req_size = ALIGN(CAYMAN_SMC_UCODE_SIZE, 4);
@@ -797,7 +799,7 @@ int ni_init_microcode(struct radeon_device *rdev) } }
- if ((rdev->family >= CHIP_BARTS) && (rdev->family <= CHIP_CAICOS)) {
- if ((rdev->family >= CHIP_BARTS) && (rdev->family <= CHIP_CAYMAN)) { snprintf(fw_name, sizeof(fw_name), "radeon/%s_smc.bin", chip_name); err = request_firmware(&rdev->smc_fw, fw_name, &pdev->dev); if (err)
diff --git a/drivers/gpu/drm/radeon/ni_dpm.c b/drivers/gpu/drm/radeon/ni_dpm.c new file mode 100644 index 0000000..635bf04 --- /dev/null +++ b/drivers/gpu/drm/radeon/ni_dpm.c @@ -0,0 +1,4093 @@ +/*
- Copyright 2012 Advanced Micro Devices, 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.
- */
+#include "drmP.h" +#include "radeon.h" +#include "nid.h" +#include "r600_dpm.h" +#include "ni_dpm.h" +#include "atom.h"
+#define MC_CG_ARB_FREQ_F0 0x0a +#define MC_CG_ARB_FREQ_F1 0x0b +#define MC_CG_ARB_FREQ_F2 0x0c +#define MC_CG_ARB_FREQ_F3 0x0d
+#define SMC_RAM_END 0xC000
+static const struct ni_cac_weights cac_weights_cayman_xt = +{
- 0x15,
- 0x2,
- 0x19,
- 0x2,
- 0x8,
- 0x14,
- 0x2,
- 0x16,
- 0xE,
- 0x17,
- 0x13,
- 0x2B,
- 0x10,
- 0x7,
- 0x5,
- 0x5,
- 0x5,
- 0x2,
- 0x3,
- 0x9,
- 0x10,
- 0x10,
- 0x2B,
- 0xA,
- 0x9,
- 0x4,
- 0xD,
- 0xD,
- 0x3E,
- 0x18,
- 0x14,
- 0,
- 0x3,
- 0x3,
- 0x5,
- 0,
- 0x2,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0x1CC,
- 0,
- 0x164,
- 1,
- 1,
- 1,
- 1,
- 12,
- 12,
- 12,
- 0x12,
- 0x1F,
- 132,
- 5,
- 7,
- 0,
- { 0, 0, 0, 0, 0, 0, 0, 0 },
- { 0, 0, 0, 0 },
- true
+};
+static const struct ni_cac_weights cac_weights_cayman_pro = +{
- 0x16,
- 0x4,
- 0x10,
- 0x2,
- 0xA,
- 0x16,
- 0x2,
- 0x18,
- 0x10,
- 0x1A,
- 0x16,
- 0x2D,
- 0x12,
- 0xA,
- 0x6,
- 0x6,
- 0x6,
- 0x2,
- 0x4,
- 0xB,
- 0x11,
- 0x11,
- 0x2D,
- 0xC,
- 0xC,
- 0x7,
- 0x10,
- 0x10,
- 0x3F,
- 0x1A,
- 0x16,
- 0,
- 0x7,
- 0x4,
- 0x6,
- 1,
- 0x2,
- 0x1,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0x30,
- 0,
- 0x1CF,
- 0,
- 0x166,
- 1,
- 1,
- 1,
- 1,
- 12,
- 12,
- 12,
- 0x15,
- 0x1F,
- 132,
- 6,
- 6,
- 0,
- { 0, 0, 0, 0, 0, 0, 0, 0 },
- { 0, 0, 0, 0 },
- true
+};
+static const struct ni_cac_weights cac_weights_cayman_le = +{
- 0x7,
- 0xE,
- 0x1,
- 0xA,
- 0x1,
- 0x3F,
- 0x2,
- 0x18,
- 0x10,
- 0x1A,
- 0x1,
- 0x3F,
- 0x1,
- 0xE,
- 0x6,
- 0x6,
- 0x6,
- 0x2,
- 0x4,
- 0x9,
- 0x1A,
- 0x1A,
- 0x2C,
- 0xA,
- 0x11,
- 0x8,
- 0x19,
- 0x19,
- 0x1,
- 0x1,
- 0x1A,
- 0,
- 0x8,
- 0x5,
- 0x8,
- 0x1,
- 0x3,
- 0x1,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0x38,
- 0x38,
- 0x239,
- 0x3,
- 0x18A,
- 1,
- 1,
- 1,
- 1,
- 12,
- 12,
- 12,
- 0x15,
- 0x22,
- 132,
- 6,
- 6,
- 0,
- { 0, 0, 0, 0, 0, 0, 0, 0 },
- { 0, 0, 0, 0 },
- true
+};
+#define NISLANDS_MGCG_SEQUENCE 300
+static const u32 cayman_cgcg_cgls_default[] = +{
- 0x000008f8, 0x00000010, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000011, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000012, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000013, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000014, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000015, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000016, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000017, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000018, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000019, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x0000001a, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x0000001b, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000020, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000021, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000022, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000023, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000024, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000025, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000026, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000027, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000028, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000029, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x0000002a, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x0000002b, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff
+}; +#define CAYMAN_CGCG_CGLS_DEFAULT_LENGTH sizeof(cayman_cgcg_cgls_default) / (3 * sizeof(u32))
+static const u32 cayman_cgcg_cgls_disable[] = +{
- 0x000008f8, 0x00000010, 0xffffffff,
- 0x000008fc, 0xffffffff, 0xffffffff,
- 0x000008f8, 0x00000011, 0xffffffff,
- 0x000008fc, 0xffffffff, 0xffffffff,
- 0x000008f8, 0x00000012, 0xffffffff,
- 0x000008fc, 0xffffffff, 0xffffffff,
- 0x000008f8, 0x00000013, 0xffffffff,
- 0x000008fc, 0xffffffff, 0xffffffff,
- 0x000008f8, 0x00000014, 0xffffffff,
- 0x000008fc, 0xffffffff, 0xffffffff,
- 0x000008f8, 0x00000015, 0xffffffff,
- 0x000008fc, 0xffffffff, 0xffffffff,
- 0x000008f8, 0x00000016, 0xffffffff,
- 0x000008fc, 0xffffffff, 0xffffffff,
- 0x000008f8, 0x00000017, 0xffffffff,
- 0x000008fc, 0xffffffff, 0xffffffff,
- 0x000008f8, 0x00000018, 0xffffffff,
- 0x000008fc, 0xffffffff, 0xffffffff,
- 0x000008f8, 0x00000019, 0xffffffff,
- 0x000008fc, 0xffffffff, 0xffffffff,
- 0x000008f8, 0x0000001a, 0xffffffff,
- 0x000008fc, 0xffffffff, 0xffffffff,
- 0x000008f8, 0x0000001b, 0xffffffff,
- 0x000008fc, 0xffffffff, 0xffffffff,
- 0x000008f8, 0x00000020, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000021, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000022, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000023, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000024, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000025, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000026, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000027, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000028, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000029, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x0000002a, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x0000002b, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x00000644, 0x000f7902, 0x001f4180,
- 0x00000644, 0x000f3802, 0x001f4180
+}; +#define CAYMAN_CGCG_CGLS_DISABLE_LENGTH sizeof(cayman_cgcg_cgls_disable) / (3 * sizeof(u32))
+static const u32 cayman_cgcg_cgls_enable[] = +{
- 0x00000644, 0x000f7882, 0x001f4080,
- 0x000008f8, 0x00000010, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000011, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000012, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000013, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000014, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000015, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000016, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000017, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000018, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000019, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x0000001a, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x0000001b, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000020, 0xffffffff,
- 0x000008fc, 0xffffffff, 0xffffffff,
- 0x000008f8, 0x00000021, 0xffffffff,
- 0x000008fc, 0xffffffff, 0xffffffff,
- 0x000008f8, 0x00000022, 0xffffffff,
- 0x000008fc, 0xffffffff, 0xffffffff,
- 0x000008f8, 0x00000023, 0xffffffff,
- 0x000008fc, 0xffffffff, 0xffffffff,
- 0x000008f8, 0x00000024, 0xffffffff,
- 0x000008fc, 0xffffffff, 0xffffffff,
- 0x000008f8, 0x00000025, 0xffffffff,
- 0x000008fc, 0xffffffff, 0xffffffff,
- 0x000008f8, 0x00000026, 0xffffffff,
- 0x000008fc, 0xffffffff, 0xffffffff,
- 0x000008f8, 0x00000027, 0xffffffff,
- 0x000008fc, 0xffffffff, 0xffffffff,
- 0x000008f8, 0x00000028, 0xffffffff,
- 0x000008fc, 0xffffffff, 0xffffffff,
- 0x000008f8, 0x00000029, 0xffffffff,
- 0x000008fc, 0xffffffff, 0xffffffff,
- 0x000008f8, 0x0000002a, 0xffffffff,
- 0x000008fc, 0xffffffff, 0xffffffff,
- 0x000008f8, 0x0000002b, 0xffffffff,
- 0x000008fc, 0xffffffff, 0xffffffff
+}; +#define CAYMAN_CGCG_CGLS_ENABLE_LENGTH sizeof(cayman_cgcg_cgls_enable) / (3 * sizeof(u32))
+static const u32 cayman_mgcg_default[] = +{
- 0x0000802c, 0xc0000000, 0xffffffff,
- 0x00003fc4, 0xc0000000, 0xffffffff,
- 0x00005448, 0x00000100, 0xffffffff,
- 0x000055e4, 0x00000100, 0xffffffff,
- 0x0000160c, 0x00000100, 0xffffffff,
- 0x00008984, 0x06000100, 0xffffffff,
- 0x0000c164, 0x00000100, 0xffffffff,
- 0x00008a18, 0x00000100, 0xffffffff,
- 0x0000897c, 0x06000100, 0xffffffff,
- 0x00008b28, 0x00000100, 0xffffffff,
- 0x00009144, 0x00800200, 0xffffffff,
- 0x00009a60, 0x00000100, 0xffffffff,
- 0x00009868, 0x00000100, 0xffffffff,
- 0x00008d58, 0x00000100, 0xffffffff,
- 0x00009510, 0x00000100, 0xffffffff,
- 0x0000949c, 0x00000100, 0xffffffff,
- 0x00009654, 0x00000100, 0xffffffff,
- 0x00009030, 0x00000100, 0xffffffff,
- 0x00009034, 0x00000100, 0xffffffff,
- 0x00009038, 0x00000100, 0xffffffff,
- 0x0000903c, 0x00000100, 0xffffffff,
- 0x00009040, 0x00000100, 0xffffffff,
- 0x0000a200, 0x00000100, 0xffffffff,
- 0x0000a204, 0x00000100, 0xffffffff,
- 0x0000a208, 0x00000100, 0xffffffff,
- 0x0000a20c, 0x00000100, 0xffffffff,
- 0x00009744, 0x00000100, 0xffffffff,
- 0x00003f80, 0x00000100, 0xffffffff,
- 0x0000a210, 0x00000100, 0xffffffff,
- 0x0000a214, 0x00000100, 0xffffffff,
- 0x000004d8, 0x00000100, 0xffffffff,
- 0x00009664, 0x00000100, 0xffffffff,
- 0x00009698, 0x00000100, 0xffffffff,
- 0x000004d4, 0x00000200, 0xffffffff,
- 0x000004d0, 0x00000000, 0xffffffff,
- 0x000030cc, 0x00000104, 0xffffffff,
- 0x0000d0c0, 0x00000100, 0xffffffff,
- 0x0000d8c0, 0x00000100, 0xffffffff,
- 0x0000802c, 0x40000000, 0xffffffff,
- 0x00003fc4, 0x40000000, 0xffffffff,
- 0x0000915c, 0x00010000, 0xffffffff,
- 0x00009160, 0x00030002, 0xffffffff,
- 0x00009164, 0x00050004, 0xffffffff,
- 0x00009168, 0x00070006, 0xffffffff,
- 0x00009178, 0x00070000, 0xffffffff,
- 0x0000917c, 0x00030002, 0xffffffff,
- 0x00009180, 0x00050004, 0xffffffff,
- 0x0000918c, 0x00010006, 0xffffffff,
- 0x00009190, 0x00090008, 0xffffffff,
- 0x00009194, 0x00070000, 0xffffffff,
- 0x00009198, 0x00030002, 0xffffffff,
- 0x0000919c, 0x00050004, 0xffffffff,
- 0x000091a8, 0x00010006, 0xffffffff,
- 0x000091ac, 0x00090008, 0xffffffff,
- 0x000091b0, 0x00070000, 0xffffffff,
- 0x000091b4, 0x00030002, 0xffffffff,
- 0x000091b8, 0x00050004, 0xffffffff,
- 0x000091c4, 0x00010006, 0xffffffff,
- 0x000091c8, 0x00090008, 0xffffffff,
- 0x000091cc, 0x00070000, 0xffffffff,
- 0x000091d0, 0x00030002, 0xffffffff,
- 0x000091d4, 0x00050004, 0xffffffff,
- 0x000091e0, 0x00010006, 0xffffffff,
- 0x000091e4, 0x00090008, 0xffffffff,
- 0x000091e8, 0x00000000, 0xffffffff,
- 0x000091ec, 0x00070000, 0xffffffff,
- 0x000091f0, 0x00030002, 0xffffffff,
- 0x000091f4, 0x00050004, 0xffffffff,
- 0x00009200, 0x00010006, 0xffffffff,
- 0x00009204, 0x00090008, 0xffffffff,
- 0x00009208, 0x00070000, 0xffffffff,
- 0x0000920c, 0x00030002, 0xffffffff,
- 0x00009210, 0x00050004, 0xffffffff,
- 0x0000921c, 0x00010006, 0xffffffff,
- 0x00009220, 0x00090008, 0xffffffff,
- 0x00009224, 0x00070000, 0xffffffff,
- 0x00009228, 0x00030002, 0xffffffff,
- 0x0000922c, 0x00050004, 0xffffffff,
- 0x00009238, 0x00010006, 0xffffffff,
- 0x0000923c, 0x00090008, 0xffffffff,
- 0x00009240, 0x00070000, 0xffffffff,
- 0x00009244, 0x00030002, 0xffffffff,
- 0x00009248, 0x00050004, 0xffffffff,
- 0x00009254, 0x00010006, 0xffffffff,
- 0x00009258, 0x00090008, 0xffffffff,
- 0x0000925c, 0x00070000, 0xffffffff,
- 0x00009260, 0x00030002, 0xffffffff,
- 0x00009264, 0x00050004, 0xffffffff,
- 0x00009270, 0x00010006, 0xffffffff,
- 0x00009274, 0x00090008, 0xffffffff,
- 0x00009278, 0x00070000, 0xffffffff,
- 0x0000927c, 0x00030002, 0xffffffff,
- 0x00009280, 0x00050004, 0xffffffff,
- 0x0000928c, 0x00010006, 0xffffffff,
- 0x00009290, 0x00090008, 0xffffffff,
- 0x000092a8, 0x00070000, 0xffffffff,
- 0x000092ac, 0x00030002, 0xffffffff,
- 0x000092b0, 0x00050004, 0xffffffff,
- 0x000092bc, 0x00010006, 0xffffffff,
- 0x000092c0, 0x00090008, 0xffffffff,
- 0x000092c4, 0x00070000, 0xffffffff,
- 0x000092c8, 0x00030002, 0xffffffff,
- 0x000092cc, 0x00050004, 0xffffffff,
- 0x000092d8, 0x00010006, 0xffffffff,
- 0x000092dc, 0x00090008, 0xffffffff,
- 0x00009294, 0x00000000, 0xffffffff,
- 0x0000802c, 0x40010000, 0xffffffff,
- 0x00003fc4, 0x40010000, 0xffffffff,
- 0x0000915c, 0x00010000, 0xffffffff,
- 0x00009160, 0x00030002, 0xffffffff,
- 0x00009164, 0x00050004, 0xffffffff,
- 0x00009168, 0x00070006, 0xffffffff,
- 0x00009178, 0x00070000, 0xffffffff,
- 0x0000917c, 0x00030002, 0xffffffff,
- 0x00009180, 0x00050004, 0xffffffff,
- 0x0000918c, 0x00010006, 0xffffffff,
- 0x00009190, 0x00090008, 0xffffffff,
- 0x00009194, 0x00070000, 0xffffffff,
- 0x00009198, 0x00030002, 0xffffffff,
- 0x0000919c, 0x00050004, 0xffffffff,
- 0x000091a8, 0x00010006, 0xffffffff,
- 0x000091ac, 0x00090008, 0xffffffff,
- 0x000091b0, 0x00070000, 0xffffffff,
- 0x000091b4, 0x00030002, 0xffffffff,
- 0x000091b8, 0x00050004, 0xffffffff,
- 0x000091c4, 0x00010006, 0xffffffff,
- 0x000091c8, 0x00090008, 0xffffffff,
- 0x000091cc, 0x00070000, 0xffffffff,
- 0x000091d0, 0x00030002, 0xffffffff,
- 0x000091d4, 0x00050004, 0xffffffff,
- 0x000091e0, 0x00010006, 0xffffffff,
- 0x000091e4, 0x00090008, 0xffffffff,
- 0x000091e8, 0x00000000, 0xffffffff,
- 0x000091ec, 0x00070000, 0xffffffff,
- 0x000091f0, 0x00030002, 0xffffffff,
- 0x000091f4, 0x00050004, 0xffffffff,
- 0x00009200, 0x00010006, 0xffffffff,
- 0x00009204, 0x00090008, 0xffffffff,
- 0x00009208, 0x00070000, 0xffffffff,
- 0x0000920c, 0x00030002, 0xffffffff,
- 0x00009210, 0x00050004, 0xffffffff,
- 0x0000921c, 0x00010006, 0xffffffff,
- 0x00009220, 0x00090008, 0xffffffff,
- 0x00009224, 0x00070000, 0xffffffff,
- 0x00009228, 0x00030002, 0xffffffff,
- 0x0000922c, 0x00050004, 0xffffffff,
- 0x00009238, 0x00010006, 0xffffffff,
- 0x0000923c, 0x00090008, 0xffffffff,
- 0x00009240, 0x00070000, 0xffffffff,
- 0x00009244, 0x00030002, 0xffffffff,
- 0x00009248, 0x00050004, 0xffffffff,
- 0x00009254, 0x00010006, 0xffffffff,
- 0x00009258, 0x00090008, 0xffffffff,
- 0x0000925c, 0x00070000, 0xffffffff,
- 0x00009260, 0x00030002, 0xffffffff,
- 0x00009264, 0x00050004, 0xffffffff,
- 0x00009270, 0x00010006, 0xffffffff,
- 0x00009274, 0x00090008, 0xffffffff,
- 0x00009278, 0x00070000, 0xffffffff,
- 0x0000927c, 0x00030002, 0xffffffff,
- 0x00009280, 0x00050004, 0xffffffff,
- 0x0000928c, 0x00010006, 0xffffffff,
- 0x00009290, 0x00090008, 0xffffffff,
- 0x000092a8, 0x00070000, 0xffffffff,
- 0x000092ac, 0x00030002, 0xffffffff,
- 0x000092b0, 0x00050004, 0xffffffff,
- 0x000092bc, 0x00010006, 0xffffffff,
- 0x000092c0, 0x00090008, 0xffffffff,
- 0x000092c4, 0x00070000, 0xffffffff,
- 0x000092c8, 0x00030002, 0xffffffff,
- 0x000092cc, 0x00050004, 0xffffffff,
- 0x000092d8, 0x00010006, 0xffffffff,
- 0x000092dc, 0x00090008, 0xffffffff,
- 0x00009294, 0x00000000, 0xffffffff,
- 0x0000802c, 0xc0000000, 0xffffffff,
- 0x00003fc4, 0xc0000000, 0xffffffff,
- 0x000008f8, 0x00000010, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000011, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000012, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000013, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000014, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000015, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000016, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000017, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000018, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000019, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x0000001a, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x0000001b, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff
+}; +#define CAYMAN_MGCG_DEFAULT_LENGTH sizeof(cayman_mgcg_default) / (3 * sizeof(u32))
+static const u32 cayman_mgcg_disable[] = +{
- 0x0000802c, 0xc0000000, 0xffffffff,
- 0x000008f8, 0x00000000, 0xffffffff,
- 0x000008fc, 0xffffffff, 0xffffffff,
- 0x000008f8, 0x00000001, 0xffffffff,
- 0x000008fc, 0xffffffff, 0xffffffff,
- 0x000008f8, 0x00000002, 0xffffffff,
- 0x000008fc, 0xffffffff, 0xffffffff,
- 0x000008f8, 0x00000003, 0xffffffff,
- 0x000008fc, 0xffffffff, 0xffffffff,
- 0x00009150, 0x00600000, 0xffffffff
+}; +#define CAYMAN_MGCG_DISABLE_LENGTH sizeof(cayman_mgcg_disable) / (3 * sizeof(u32))
+static const u32 cayman_mgcg_enable[] = +{
- 0x0000802c, 0xc0000000, 0xffffffff,
- 0x000008f8, 0x00000000, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000001, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x000008f8, 0x00000002, 0xffffffff,
- 0x000008fc, 0x00600000, 0xffffffff,
- 0x000008f8, 0x00000003, 0xffffffff,
- 0x000008fc, 0x00000000, 0xffffffff,
- 0x00009150, 0x96944200, 0xffffffff
+};
+#define CAYMAN_MGCG_ENABLE_LENGTH sizeof(cayman_mgcg_enable) / (3 * sizeof(u32))
+#define NISLANDS_SYSLS_SEQUENCE 100
+static const u32 cayman_sysls_default[] = +{
- /* Register, Value, Mask bits */
- 0x000055e8, 0x00000000, 0xffffffff,
- 0x0000d0bc, 0x00000000, 0xffffffff,
- 0x0000d8bc, 0x00000000, 0xffffffff,
- 0x000015c0, 0x000c1401, 0xffffffff,
- 0x0000264c, 0x000c0400, 0xffffffff,
- 0x00002648, 0x000c0400, 0xffffffff,
- 0x00002650, 0x000c0400, 0xffffffff,
- 0x000020b8, 0x000c0400, 0xffffffff,
- 0x000020bc, 0x000c0400, 0xffffffff,
- 0x000020c0, 0x000c0c80, 0xffffffff,
- 0x0000f4a0, 0x000000c0, 0xffffffff,
- 0x0000f4a4, 0x00680fff, 0xffffffff,
- 0x00002f50, 0x00000404, 0xffffffff,
- 0x000004c8, 0x00000001, 0xffffffff,
- 0x000064ec, 0x00000000, 0xffffffff,
- 0x00000c7c, 0x00000000, 0xffffffff,
- 0x00008dfc, 0x00000000, 0xffffffff
+}; +#define CAYMAN_SYSLS_DEFAULT_LENGTH sizeof(cayman_sysls_default) / (3 * sizeof(u32))
+static const u32 cayman_sysls_disable[] = +{
- /* Register, Value, Mask bits */
- 0x0000d0c0, 0x00000000, 0xffffffff,
- 0x0000d8c0, 0x00000000, 0xffffffff,
- 0x000055e8, 0x00000000, 0xffffffff,
- 0x0000d0bc, 0x00000000, 0xffffffff,
- 0x0000d8bc, 0x00000000, 0xffffffff,
- 0x000015c0, 0x00041401, 0xffffffff,
- 0x0000264c, 0x00040400, 0xffffffff,
- 0x00002648, 0x00040400, 0xffffffff,
- 0x00002650, 0x00040400, 0xffffffff,
- 0x000020b8, 0x00040400, 0xffffffff,
- 0x000020bc, 0x00040400, 0xffffffff,
- 0x000020c0, 0x00040c80, 0xffffffff,
- 0x0000f4a0, 0x000000c0, 0xffffffff,
- 0x0000f4a4, 0x00680000, 0xffffffff,
- 0x00002f50, 0x00000404, 0xffffffff,
- 0x000004c8, 0x00000001, 0xffffffff,
- 0x000064ec, 0x00007ffd, 0xffffffff,
- 0x00000c7c, 0x0000ff00, 0xffffffff,
- 0x00008dfc, 0x0000007f, 0xffffffff
+}; +#define CAYMAN_SYSLS_DISABLE_LENGTH sizeof(cayman_sysls_disable) / (3 * sizeof(u32))
+static const u32 cayman_sysls_enable[] = +{
- /* Register, Value, Mask bits */
- 0x000055e8, 0x00000001, 0xffffffff,
- 0x0000d0bc, 0x00000100, 0xffffffff,
- 0x0000d8bc, 0x00000100, 0xffffffff,
- 0x000015c0, 0x000c1401, 0xffffffff,
- 0x0000264c, 0x000c0400, 0xffffffff,
- 0x00002648, 0x000c0400, 0xffffffff,
- 0x00002650, 0x000c0400, 0xffffffff,
- 0x000020b8, 0x000c0400, 0xffffffff,
- 0x000020bc, 0x000c0400, 0xffffffff,
- 0x000020c0, 0x000c0c80, 0xffffffff,
- 0x0000f4a0, 0x000000c0, 0xffffffff,
- 0x0000f4a4, 0x00680fff, 0xffffffff,
- 0x00002f50, 0x00000903, 0xffffffff,
- 0x000004c8, 0x00000000, 0xffffffff,
- 0x000064ec, 0x00000000, 0xffffffff,
- 0x00000c7c, 0x00000000, 0xffffffff,
- 0x00008dfc, 0x00000000, 0xffffffff
+}; +#define CAYMAN_SYSLS_ENABLE_LENGTH sizeof(cayman_sysls_enable) / (3 * sizeof(u32))
+struct rv7xx_power_info *rv770_get_pi(struct radeon_device *rdev); +struct evergreen_power_info *evergreen_get_pi(struct radeon_device *rdev);
+static struct ni_power_info *ni_get_pi(struct radeon_device *rdev) +{
struct ni_power_info *pi = rdev->pm.dpm.priv;
return pi;
+}
+struct ni_ps *ni_get_ps(struct radeon_ps *rps) +{
- struct ni_ps *ps = rps->ps_priv;
- return ps;
+}
+/* XXX: fix for kernel use */ +#if 0 +static double ni_exp(double x) +{
- int count = 1;
- double sum = 1.0, term, tolerance = 0.000000001, y = x;
- if (x < 0)
y = -1 * x;
- term = y;
- while (term >= tolerance) {
sum = sum + term;
count = count + 1;
term = term * (y / count);
- }
- if (x < 0)
sum = 1.0 / sum;
- return sum;
+} +#endif
+static void ni_calculate_leakage_for_v_and_t_formula(const struct ni_leakage_coeffients *coeff,
u16 v, s32 t,
u32 ileakage,
u32 *leakage)
+{ +/* XXX: fix for kernel use */ +#if 0
- double kt, kv, leakage_w, i_leakage, vddc, temperature;
- i_leakage = ((double)ileakage) / 1000;
- vddc = ((double)v) / 1000;
- temperature = ((double)t) / 1000;
- kt = (((double)(coeff->at)) / 1000) * ni_exp((((double)(coeff->bt)) / 1000) * temperature);
- kv = (((double)(coeff->av)) / 1000) * ni_exp((((double)(coeff->bv)) / 1000) * vddc);
- leakage_w = i_leakage * kt * kv * vddc;
- *leakage = (u32)(leakage_w * 1000);
+#endif +}
+static void ni_calculate_leakage_for_v_and_t(struct radeon_device *rdev,
const struct ni_leakage_coeffients *coeff,
u16 v,
s32 t,
u32 i_leakage,
u32 *leakage)
+{
- ni_calculate_leakage_for_v_and_t_formula(coeff, v, t, i_leakage, leakage);
+}
+static void ni_apply_state_adjust_rules(struct radeon_device *rdev) +{
- struct ni_power_info *ni_pi = ni_get_pi(rdev);
- struct radeon_ps *rps = rdev->pm.dpm.requested_ps;
- struct ni_ps *ps = ni_get_ps(rps);
- struct radeon_clock_and_voltage_limits *max_limits;
- bool disable_mclk_switching;
- u32 mclk, sclk;
- u16 vddc, vddci;
- int i;
- /* point to the hw copy since this function will modify the ps */
- ni_pi->hw_ps = *ps;
- rdev->pm.dpm.hw_ps.ps_priv = &ni_pi->hw_ps;
- ps = &ni_pi->hw_ps;
- if (rdev->pm.dpm.new_active_crtc_count > 1)
disable_mclk_switching = true;
- else
disable_mclk_switching = false;
- if (rdev->pm.dpm.ac_power)
max_limits = &rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac;
- else
max_limits = &rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc;
- if (rdev->pm.dpm.ac_power == false) {
for (i = 0; i < ps->performance_level_count; i++) {
if (ps->performance_levels[i].mclk > max_limits->mclk)
ps->performance_levels[i].mclk = max_limits->mclk;
if (ps->performance_levels[i].sclk > max_limits->sclk)
ps->performance_levels[i].sclk = max_limits->sclk;
if (ps->performance_levels[i].vddc > max_limits->vddc)
ps->performance_levels[i].vddc = max_limits->vddc;
if (ps->performance_levels[i].vddci > max_limits->vddci)
ps->performance_levels[i].vddci = max_limits->vddci;
}
- }
- /* XXX validate the min clocks required for display */
- if (disable_mclk_switching) {
mclk = ps->performance_levels[ps->performance_level_count - 1].mclk;
sclk = ps->performance_levels[0].sclk;
vddc = ps->performance_levels[0].vddc;
vddci = ps->performance_levels[ps->performance_level_count - 1].vddci;
- } else {
sclk = ps->performance_levels[0].sclk;
mclk = ps->performance_levels[0].mclk;
vddc = ps->performance_levels[0].vddc;
vddci = ps->performance_levels[0].vddci;
- }
- /* adjusted low state */
- ps->performance_levels[0].sclk = sclk;
- ps->performance_levels[0].mclk = mclk;
- ps->performance_levels[0].vddc = vddc;
- ps->performance_levels[0].vddci = vddci;
- btc_skip_blacklist_clocks(rdev, max_limits->sclk, max_limits->mclk,
&ps->performance_levels[0].sclk,
&ps->performance_levels[0].mclk);
- for (i = 1; i < ps->performance_level_count; i++) {
if (ps->performance_levels[i].sclk < ps->performance_levels[i - 1].sclk)
ps->performance_levels[i].sclk = ps->performance_levels[i - 1].sclk;
if (ps->performance_levels[i].vddc < ps->performance_levels[i - 1].vddc)
ps->performance_levels[i].vddc = ps->performance_levels[i - 1].vddc;
- }
- if (disable_mclk_switching) {
mclk = ps->performance_levels[0].mclk;
for (i = 1; i < ps->performance_level_count; i++) {
if (mclk < ps->performance_levels[i].mclk)
mclk = ps->performance_levels[i].mclk;
}
for (i = 0; i < ps->performance_level_count; i++) {
ps->performance_levels[i].mclk = mclk;
ps->performance_levels[i].vddci = vddci;
}
- } else {
for (i = 1; i < ps->performance_level_count; i++) {
if (ps->performance_levels[i].mclk < ps->performance_levels[i - 1].mclk)
ps->performance_levels[i].mclk = ps->performance_levels[i - 1].mclk;
if (ps->performance_levels[i].vddci < ps->performance_levels[i - 1].vddci)
ps->performance_levels[i].vddci = ps->performance_levels[i - 1].vddci;
}
- }
- for (i = 1; i < ps->performance_level_count; i++)
btc_skip_blacklist_clocks(rdev, max_limits->sclk, max_limits->mclk,
&ps->performance_levels[i].sclk,
&ps->performance_levels[i].mclk);
- for (i = 0; i < ps->performance_level_count; i++)
btc_adjust_clock_combinations(rdev, max_limits,
&ps->performance_levels[i]);
- for (i = 0; i < ps->performance_level_count; i++) {
btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk,
ps->performance_levels[i].sclk,
max_limits->vddc, &ps->performance_levels[i].vddc);
btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk,
ps->performance_levels[i].mclk,
max_limits->vddci, &ps->performance_levels[i].vddci);
btc_apply_voltage_dependency_rules(&rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk,
ps->performance_levels[i].mclk,
max_limits->vddc, &ps->performance_levels[i].vddc);
/* XXX validate the voltage required for display */
- }
- for (i = 0; i < ps->performance_level_count; i++) {
btc_apply_voltage_delta_rules(rdev,
max_limits->vddc, max_limits->vddci,
&ps->performance_levels[i].vddc,
&ps->performance_levels[i].vddci);
- }
- ps->dc_compatible = true;
- for (i = 0; i < ps->performance_level_count; i++) {
if (ps->performance_levels[i].vddc > rdev->pm.dpm.dyn_state.max_clock_voltage_on_dc.vddc)
ps->dc_compatible = false;
if (ps->performance_levels[i].vddc < rdev->pm.dpm.dyn_state.min_vddc_for_pcie_gen2)
ps->performance_levels[i].flags &= ~ATOM_PPLIB_R600_FLAGS_PCIEGEN2;
- }
+}
+static void ni_cg_clockgating_default(struct radeon_device *rdev) +{
- u32 count;
- const u32 *ps = NULL;
- ps = (const u32 *)&cayman_cgcg_cgls_default;
- count = CAYMAN_CGCG_CGLS_DEFAULT_LENGTH;
- btc_program_mgcg_hw_sequence(rdev, ps, count);
+}
+static void ni_gfx_clockgating_enable(struct radeon_device *rdev,
bool enable)
+{
- u32 count;
- const u32 *ps = NULL;
- if (enable) {
ps = (const u32 *)&cayman_cgcg_cgls_enable;
count = CAYMAN_CGCG_CGLS_ENABLE_LENGTH;
- } else {
ps = (const u32 *)&cayman_cgcg_cgls_disable;
count = CAYMAN_CGCG_CGLS_DISABLE_LENGTH;
- }
- btc_program_mgcg_hw_sequence(rdev, ps, count);
+}
+static void ni_mg_clockgating_default(struct radeon_device *rdev) +{
- u32 count;
- const u32 *ps = NULL;
- ps = (const u32 *)&cayman_mgcg_default;
- count = CAYMAN_MGCG_DEFAULT_LENGTH;
- btc_program_mgcg_hw_sequence(rdev, ps, count);
+}
+static void ni_mg_clockgating_enable(struct radeon_device *rdev,
bool enable)
+{
- u32 count;
- const u32 *ps = NULL;
- if (enable) {
ps = (const u32 *)&cayman_mgcg_enable;
count = CAYMAN_MGCG_ENABLE_LENGTH;
- } else {
ps = (const u32 *)&cayman_mgcg_disable;
count = CAYMAN_MGCG_DISABLE_LENGTH;
- }
- btc_program_mgcg_hw_sequence(rdev, ps, count);
+}
+static void ni_ls_clockgating_default(struct radeon_device *rdev) +{
- u32 count;
- const u32 *ps = NULL;
- ps = (const u32 *)&cayman_sysls_default;
- count = CAYMAN_SYSLS_DEFAULT_LENGTH;
- btc_program_mgcg_hw_sequence(rdev, ps, count);
+}
+static void ni_ls_clockgating_enable(struct radeon_device *rdev,
bool enable)
+{
- u32 count;
- const u32 *ps = NULL;
- if (enable) {
ps = (const u32 *)&cayman_sysls_enable;
count = CAYMAN_SYSLS_ENABLE_LENGTH;
- } else {
ps = (const u32 *)&cayman_sysls_disable;
count = CAYMAN_SYSLS_DISABLE_LENGTH;
- }
- btc_program_mgcg_hw_sequence(rdev, ps, count);
+}
+static int ni_patch_single_dependency_table_based_on_leakage(struct radeon_device *rdev,
struct radeon_clock_voltage_dependency_table *table)
+{
- struct rv7xx_power_info *pi = rv770_get_pi(rdev);
- u32 i;
- if (table) {
for (i = 0; i < table->count; i++) {
if (0xff01 == table->entries[i].v) {
if (pi->max_vddc == 0)
return -EINVAL;
table->entries[i].v = pi->max_vddc;
}
}
- }
- return 0;
+}
+static int ni_patch_dependency_tables_based_on_leakage(struct radeon_device *rdev) +{
- int ret = 0;
- ret = ni_patch_single_dependency_table_based_on_leakage(rdev,
&rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk);
- ret = ni_patch_single_dependency_table_based_on_leakage(rdev,
&rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk);
- return ret;
+}
+static void ni_stop_dpm(struct radeon_device *rdev) +{
- WREG32_P(GENERAL_PWRMGT, 0, ~GLOBAL_PWRMGT_EN);
+}
+#if 0 +static int ni_notify_hw_of_power_source(struct radeon_device *rdev,
bool ac_power)
+{
- if (ac_power)
return (rv770_send_msg_to_smc(rdev, PPSMC_MSG_RunningOnAC) == PPSMC_Result_OK) ?
0 : -EINVAL;
- return 0;
+} +#endif
+static PPSMC_Result ni_send_msg_to_smc_with_parameter(struct radeon_device *rdev,
PPSMC_Msg msg, u32 parameter)
+{
- WREG32(SMC_SCRATCH0, parameter);
- return rv770_send_msg_to_smc(rdev, msg);
+}
+static int ni_restrict_performance_levels_before_switch(struct radeon_device *rdev) +{
- if (rv770_send_msg_to_smc(rdev, PPSMC_MSG_NoForcedLevel) != PPSMC_Result_OK)
return -EINVAL;
- return (ni_send_msg_to_smc_with_parameter(rdev, PPSMC_MSG_SetEnabledLevels, 1) == PPSMC_Result_OK) ?
0 : -EINVAL;
+}
+static void ni_stop_smc(struct radeon_device *rdev) +{
- u32 tmp;
- int i;
- for (i = 0; i < rdev->usec_timeout; i++) {
tmp = RREG32(LB_SYNC_RESET_SEL) & LB_SYNC_RESET_SEL_MASK;
if (tmp != 1)
break;
udelay(1);
- }
- udelay(100);
- r7xx_stop_smc(rdev);
+}
+static int ni_process_firmware_header(struct radeon_device *rdev) +{
struct rv7xx_power_info *pi = rv770_get_pi(rdev);
struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
struct ni_power_info *ni_pi = ni_get_pi(rdev);
- u32 tmp;
- int ret;
- ret = rv770_read_smc_sram_dword(rdev,
NISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
NISLANDS_SMC_FIRMWARE_HEADER_stateTable,
&tmp, pi->sram_end);
- if (ret)
return ret;
- pi->state_table_start = (u16)tmp;
- ret = rv770_read_smc_sram_dword(rdev,
NISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
NISLANDS_SMC_FIRMWARE_HEADER_softRegisters,
&tmp, pi->sram_end);
- if (ret)
return ret;
- pi->soft_regs_start = (u16)tmp;
- ret = rv770_read_smc_sram_dword(rdev,
NISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
NISLANDS_SMC_FIRMWARE_HEADER_mcRegisterTable,
&tmp, pi->sram_end);
- if (ret)
return ret;
- eg_pi->mc_reg_table_start = (u16)tmp;
- ret = rv770_read_smc_sram_dword(rdev,
NISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
NISLANDS_SMC_FIRMWARE_HEADER_fanTable,
&tmp, pi->sram_end);
- if (ret)
return ret;
- ni_pi->fan_table_start = (u16)tmp;
- ret = rv770_read_smc_sram_dword(rdev,
NISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
NISLANDS_SMC_FIRMWARE_HEADER_mcArbDramAutoRefreshTable,
&tmp, pi->sram_end);
- if (ret)
return ret;
- ni_pi->arb_table_start = (u16)tmp;
- ret = rv770_read_smc_sram_dword(rdev,
NISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
NISLANDS_SMC_FIRMWARE_HEADER_cacTable,
&tmp, pi->sram_end);
- if (ret)
return ret;
- ni_pi->cac_table_start = (u16)tmp;
- ret = rv770_read_smc_sram_dword(rdev,
NISLANDS_SMC_FIRMWARE_HEADER_LOCATION +
NISLANDS_SMC_FIRMWARE_HEADER_spllTable,
&tmp, pi->sram_end);
- if (ret)
return ret;
- ni_pi->spll_table_start = (u16)tmp;
- return ret;
+}
+static void ni_read_clock_registers(struct radeon_device *rdev) +{
- struct ni_power_info *ni_pi = ni_get_pi(rdev);
- ni_pi->clock_registers.cg_spll_func_cntl = RREG32(CG_SPLL_FUNC_CNTL);
- ni_pi->clock_registers.cg_spll_func_cntl_2 = RREG32(CG_SPLL_FUNC_CNTL_2);
- ni_pi->clock_registers.cg_spll_func_cntl_3 = RREG32(CG_SPLL_FUNC_CNTL_3);
- ni_pi->clock_registers.cg_spll_func_cntl_4 = RREG32(CG_SPLL_FUNC_CNTL_4);
- ni_pi->clock_registers.cg_spll_spread_spectrum = RREG32(CG_SPLL_SPREAD_SPECTRUM);
- ni_pi->clock_registers.cg_spll_spread_spectrum_2 = RREG32(CG_SPLL_SPREAD_SPECTRUM_2);
- ni_pi->clock_registers.mpll_ad_func_cntl = RREG32(MPLL_AD_FUNC_CNTL);
- ni_pi->clock_registers.mpll_ad_func_cntl_2 = RREG32(MPLL_AD_FUNC_CNTL_2);
- ni_pi->clock_registers.mpll_dq_func_cntl = RREG32(MPLL_DQ_FUNC_CNTL);
- ni_pi->clock_registers.mpll_dq_func_cntl_2 = RREG32(MPLL_DQ_FUNC_CNTL_2);
- ni_pi->clock_registers.mclk_pwrmgt_cntl = RREG32(MCLK_PWRMGT_CNTL);
- ni_pi->clock_registers.dll_cntl = RREG32(DLL_CNTL);
- ni_pi->clock_registers.mpll_ss1 = RREG32(MPLL_SS1);
- ni_pi->clock_registers.mpll_ss2 = RREG32(MPLL_SS2);
+}
+#if 0 +static int ni_enter_ulp_state(struct radeon_device *rdev) +{
- struct rv7xx_power_info *pi = rv770_get_pi(rdev);
- if (pi->gfx_clock_gating) {
WREG32_P(SCLK_PWRMGT_CNTL, 0, ~DYN_GFX_CLK_OFF_EN);
WREG32_P(SCLK_PWRMGT_CNTL, GFX_CLK_FORCE_ON, ~GFX_CLK_FORCE_ON);
WREG32_P(SCLK_PWRMGT_CNTL, 0, ~GFX_CLK_FORCE_ON);
RREG32(GB_ADDR_CONFIG);
}
- WREG32_P(SMC_MSG, HOST_SMC_MSG(PPSMC_MSG_SwitchToMinimumPower),
~HOST_SMC_MSG_MASK);
- udelay(25000);
- return 0;
+} +#endif
+static void ni_program_response_times(struct radeon_device *rdev) +{
- u32 voltage_response_time, backbias_response_time, acpi_delay_time, vbi_time_out;
- u32 vddc_dly, bb_dly, acpi_dly, vbi_dly, mclk_switch_limit;
- u32 reference_clock;
- rv770_write_smc_soft_register(rdev, NI_SMC_SOFT_REGISTER_mvdd_chg_time, 1);
voltage_response_time = (u32)rdev->pm.dpm.voltage_response_time;
backbias_response_time = (u32)rdev->pm.dpm.backbias_response_time;
if (voltage_response_time == 0)
voltage_response_time = 1000;
if (backbias_response_time == 0)
backbias_response_time = 1000;
^ Broken indentation.
- acpi_delay_time = 15000;
- vbi_time_out = 100000;
- reference_clock = radeon_get_xclk(rdev);
- vddc_dly = (voltage_response_time * reference_clock) / 1600;
- bb_dly = (backbias_response_time * reference_clock) / 1600;
- acpi_dly = (acpi_delay_time * reference_clock) / 1600;
- vbi_dly = (vbi_time_out * reference_clock) / 1600;
- mclk_switch_limit = (460 * reference_clock) / 100;
- rv770_write_smc_soft_register(rdev, NI_SMC_SOFT_REGISTER_delay_vreg, vddc_dly);
- rv770_write_smc_soft_register(rdev, NI_SMC_SOFT_REGISTER_delay_bbias, bb_dly);
- rv770_write_smc_soft_register(rdev, NI_SMC_SOFT_REGISTER_delay_acpi, acpi_dly);
- rv770_write_smc_soft_register(rdev, NI_SMC_SOFT_REGISTER_mclk_chg_timeout, vbi_dly);
- rv770_write_smc_soft_register(rdev, NI_SMC_SOFT_REGISTER_mc_block_delay, 0xAA);
- rv770_write_smc_soft_register(rdev, NI_SMC_SOFT_REGISTER_mclk_switch_lim, mclk_switch_limit);
+}
+static void ni_populate_smc_voltage_table(struct radeon_device *rdev,
struct atom_voltage_table *voltage_table,
NISLANDS_SMC_STATETABLE *table)
+{
- unsigned int i;
- for (i = 0; i < voltage_table->count; i++) {
table->highSMIO[i] = 0;
table->lowSMIO[i] |= cpu_to_be32(voltage_table->entries[i].smio_low);
- }
+}
+static void ni_populate_smc_voltage_tables(struct radeon_device *rdev,
NISLANDS_SMC_STATETABLE *table)
+{
- struct rv7xx_power_info *pi = rv770_get_pi(rdev);
struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
^ Broken indentation.
- unsigned char i;
- if (eg_pi->vddc_voltage_table.count) {
ni_populate_smc_voltage_table(rdev, &eg_pi->vddc_voltage_table, table);
table->voltageMaskTable.highMask[NISLANDS_SMC_VOLTAGEMASK_VDDC] = 0;
table->voltageMaskTable.lowMask[NISLANDS_SMC_VOLTAGEMASK_VDDC] =
cpu_to_be32(eg_pi->vddc_voltage_table.mask_low);
for (i = 0; i < eg_pi->vddc_voltage_table.count; i++) {
if (pi->max_vddc_in_table <= eg_pi->vddc_voltage_table.entries[i].value) {
table->maxVDDCIndexInPPTable = i;
break;
}
}
- }
- if (eg_pi->vddci_voltage_table.count) {
ni_populate_smc_voltage_table(rdev, &eg_pi->vddci_voltage_table, table);
table->voltageMaskTable.highMask[NISLANDS_SMC_VOLTAGEMASK_VDDCI] = 0;
table->voltageMaskTable.lowMask[NISLANDS_SMC_VOLTAGEMASK_VDDCI] =
cpu_to_be32(eg_pi->vddc_voltage_table.mask_low);
- }
+}
+static int ni_populate_voltage_value(struct radeon_device *rdev,
struct atom_voltage_table *table,
u16 value,
NISLANDS_SMC_VOLTAGE_VALUE *voltage)
+{
- unsigned int i;
- for (i = 0; i < table->count; i++) {
if (value <= table->entries[i].value) {
voltage->index = (u8)i;
voltage->value = cpu_to_be16(table->entries[i].value);
break;
}
- }
- if (i >= table->count)
return -EINVAL;
- return 0;
+}
+static void ni_populate_mvdd_value(struct radeon_device *rdev,
u32 mclk,
NISLANDS_SMC_VOLTAGE_VALUE *voltage)
+{
struct rv7xx_power_info *pi = rv770_get_pi(rdev);
struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
^ Broken indentation.
- if (!pi->mvdd_control) {
voltage->index = eg_pi->mvdd_high_index;
voltage->value = cpu_to_be16(MVDD_HIGH_VALUE);
return;
}
if (mclk <= pi->mvdd_split_frequency) {
voltage->index = eg_pi->mvdd_low_index;
voltage->value = cpu_to_be16(MVDD_LOW_VALUE);
^ Broken indentation.
- } else {
voltage->index = eg_pi->mvdd_high_index;
voltage->value = cpu_to_be16(MVDD_HIGH_VALUE);
}
^ Broken indentation.
+}
+static int ni_get_std_voltage_value(struct radeon_device *rdev,
NISLANDS_SMC_VOLTAGE_VALUE *voltage,
u16 *std_voltage)
+{
- if (rdev->pm.dpm.dyn_state.cac_leakage_table.entries &&
((u32)voltage->index < rdev->pm.dpm.dyn_state.cac_leakage_table.count))
*std_voltage = rdev->pm.dpm.dyn_state.cac_leakage_table.entries[voltage->index].vddc;
- else
*std_voltage = be16_to_cpu(voltage->value);
- return 0;
+}
+static void ni_populate_std_voltage_value(struct radeon_device *rdev,
u16 value, u8 index,
NISLANDS_SMC_VOLTAGE_VALUE *voltage)
+{
- voltage->index = index;
- voltage->value = cpu_to_be16(value);
+}
+static u32 ni_get_smc_power_scaling_factor(struct radeon_device *rdev) +{
- u32 xclk_period;
- u32 xclk = radeon_get_xclk(rdev);
- u32 tmp = RREG32(CG_CAC_CTRL) & TID_CNT_MASK;
- xclk_period = (1000000000UL / xclk);
- xclk_period /= 10000UL;
- return tmp * xclk_period;
+}
+static u32 ni_scale_power_for_smc(u32 power_in_watts, u32 scaling_factor) +{
- return (power_in_watts * scaling_factor) << 2;
+}
+static u32 ni_calculate_power_boost_limit(struct radeon_device *rdev,
struct radeon_ps *radeon_state,
u32 near_tdp_limit)
+{
- struct ni_ps *state = ni_get_ps(radeon_state);
- struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
- struct ni_power_info *ni_pi = ni_get_pi(rdev);
- u32 power_boost_limit = 0;
- int ret;
- if (ni_pi->enable_power_containment &&
ni_pi->use_power_boost_limit) {
NISLANDS_SMC_VOLTAGE_VALUE vddc;
u16 std_vddc_med;
u16 std_vddc_high;
u64 tmp;
if (state->performance_level_count < 3)
return 0;
ret = ni_populate_voltage_value(rdev, &eg_pi->vddc_voltage_table,
state->performance_levels[state->performance_level_count - 2].vddc,
&vddc);
if (ret)
return 0;
ret = ni_get_std_voltage_value(rdev, &vddc, &std_vddc_med);
if (ret)
return 0;
ret = ni_populate_voltage_value(rdev, &eg_pi->vddc_voltage_table,
state->performance_levels[state->performance_level_count - 1].vddc,
&vddc);
if (ret)
return 0;
ret = ni_get_std_voltage_value(rdev, &vddc, &std_vddc_high);
if (ret)
return 0;
tmp = ((u64)near_tdp_limit * ((u64)std_vddc_med * (u64)std_vddc_med) * 90) /
((u64)std_vddc_high * (u64)std_vddc_high * 100);
if (tmp >> 32)
return 0;
power_boost_limit = (u32)tmp;
- }
- return power_boost_limit;
+}
+static int ni_calculate_adjusted_tdp_limits(struct radeon_device *rdev,
bool adjust_polarity,
u32 tdp_adjustment,
u32 *tdp_limit,
u32 *near_tdp_limit)
+{
- if (tdp_adjustment > (u32)rdev->pm.dpm.tdp_od_limit)
return -EINVAL;
- if (adjust_polarity) {
*tdp_limit = ((100 + tdp_adjustment) * rdev->pm.dpm.tdp_limit) / 100;
*near_tdp_limit = rdev->pm.dpm.near_tdp_limit + (*tdp_limit - rdev->pm.dpm.tdp_limit);
- } else {
*tdp_limit = ((100 - tdp_adjustment) * rdev->pm.dpm.tdp_limit) / 100;
*near_tdp_limit = rdev->pm.dpm.near_tdp_limit - (rdev->pm.dpm.tdp_limit - *tdp_limit);
- }
- return 0;
+}
+static int ni_populate_smc_tdp_limits(struct radeon_device *rdev) +{
- struct rv7xx_power_info *pi = rv770_get_pi(rdev);
- struct ni_power_info *ni_pi = ni_get_pi(rdev);
- if (ni_pi->enable_power_containment) {
struct radeon_ps *radeon_state = rdev->pm.dpm.requested_ps;
NISLANDS_SMC_STATETABLE *smc_table = &ni_pi->smc_statetable;
u32 scaling_factor = ni_get_smc_power_scaling_factor(rdev);
u32 tdp_limit;
u32 near_tdp_limit;
u32 power_boost_limit;
int ret;
if (scaling_factor == 0)
return -EINVAL;
memset(smc_table, 0, sizeof(NISLANDS_SMC_STATETABLE));
ret = ni_calculate_adjusted_tdp_limits(rdev,
false, /* ??? */
rdev->pm.dpm.tdp_adjustment,
&tdp_limit,
&near_tdp_limit);
if (ret)
return ret;
power_boost_limit = ni_calculate_power_boost_limit(rdev, radeon_state,
near_tdp_limit);
smc_table->dpm2Params.TDPLimit =
cpu_to_be32(ni_scale_power_for_smc(tdp_limit, scaling_factor));
smc_table->dpm2Params.NearTDPLimit =
cpu_to_be32(ni_scale_power_for_smc(near_tdp_limit, scaling_factor));
smc_table->dpm2Params.SafePowerLimit =
cpu_to_be32(ni_scale_power_for_smc((near_tdp_limit * NISLANDS_DPM2_TDP_SAFE_LIMIT_PERCENT) / 100,
scaling_factor));
smc_table->dpm2Params.PowerBoostLimit =
cpu_to_be32(ni_scale_power_for_smc(power_boost_limit, scaling_factor));
ret = rv770_copy_bytes_to_smc(rdev,
(u16)(pi->state_table_start + offsetof(NISLANDS_SMC_STATETABLE, dpm2Params) +
offsetof(PP_NIslands_DPM2Parameters, TDPLimit)),
(u8 *)(&smc_table->dpm2Params.TDPLimit),
sizeof(u32) * 4, pi->sram_end);
if (ret)
return ret;
- }
- return 0;
+}
+static int ni_copy_and_switch_arb_sets(struct radeon_device *rdev,
u32 arb_freq_src, u32 arb_freq_dest)
+{
- u32 mc_arb_dram_timing;
- u32 mc_arb_dram_timing2;
- u32 burst_time;
- u32 mc_cg_config;
- switch (arb_freq_src) {
case MC_CG_ARB_FREQ_F0:
mc_arb_dram_timing = RREG32(MC_ARB_DRAM_TIMING);
mc_arb_dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2);
burst_time = (RREG32(MC_ARB_BURST_TIME) & STATE0_MASK) >> STATE0_SHIFT;
break;
case MC_CG_ARB_FREQ_F1:
mc_arb_dram_timing = RREG32(MC_ARB_DRAM_TIMING_1);
mc_arb_dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2_1);
burst_time = (RREG32(MC_ARB_BURST_TIME) & STATE1_MASK) >> STATE1_SHIFT;
break;
case MC_CG_ARB_FREQ_F2:
mc_arb_dram_timing = RREG32(MC_ARB_DRAM_TIMING_2);
mc_arb_dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2_2);
burst_time = (RREG32(MC_ARB_BURST_TIME) & STATE2_MASK) >> STATE2_SHIFT;
break;
case MC_CG_ARB_FREQ_F3:
mc_arb_dram_timing = RREG32(MC_ARB_DRAM_TIMING_3);
mc_arb_dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2_3);
burst_time = (RREG32(MC_ARB_BURST_TIME) & STATE3_MASK) >> STATE3_SHIFT;
break;
default:
return -EINVAL;
- }
- switch (arb_freq_dest) {
case MC_CG_ARB_FREQ_F0:
WREG32(MC_ARB_DRAM_TIMING, mc_arb_dram_timing);
WREG32(MC_ARB_DRAM_TIMING2, mc_arb_dram_timing2);
WREG32_P(MC_ARB_BURST_TIME, STATE0(burst_time), ~STATE0_MASK);
break;
case MC_CG_ARB_FREQ_F1:
WREG32(MC_ARB_DRAM_TIMING_1, mc_arb_dram_timing);
WREG32(MC_ARB_DRAM_TIMING2_1, mc_arb_dram_timing2);
WREG32_P(MC_ARB_BURST_TIME, STATE1(burst_time), ~STATE1_MASK);
break;
case MC_CG_ARB_FREQ_F2:
WREG32(MC_ARB_DRAM_TIMING_2, mc_arb_dram_timing);
WREG32(MC_ARB_DRAM_TIMING2_2, mc_arb_dram_timing2);
WREG32_P(MC_ARB_BURST_TIME, STATE2(burst_time), ~STATE2_MASK);
break;
case MC_CG_ARB_FREQ_F3:
^ Broken indentation.
WREG32(MC_ARB_DRAM_TIMING_3, mc_arb_dram_timing);
WREG32(MC_ARB_DRAM_TIMING2_3, mc_arb_dram_timing2);
WREG32_P(MC_ARB_BURST_TIME, STATE3(burst_time), ~STATE3_MASK);
break;
default:
^ Broken indentation.
return -EINVAL;
- }
- mc_cg_config = RREG32(MC_CG_CONFIG) | 0x0000000F;
- WREG32(MC_CG_CONFIG, mc_cg_config);
- WREG32_P(MC_ARB_CG, CG_ARB_REQ(arb_freq_dest), ~CG_ARB_REQ_MASK);
- return 0;
+}
+static int ni_init_arb_table_index(struct radeon_device *rdev) +{
- struct rv7xx_power_info *pi = rv770_get_pi(rdev);
- struct ni_power_info *ni_pi = ni_get_pi(rdev);
- u32 tmp;
- int ret;
- ret = rv770_read_smc_sram_dword(rdev, ni_pi->arb_table_start,
&tmp, pi->sram_end);
- if (ret)
return ret;
- tmp &= 0x00FFFFFF;
- tmp |= ((u32)MC_CG_ARB_FREQ_F1) << 24;
- return rv770_write_smc_sram_dword(rdev, ni_pi->arb_table_start,
tmp, pi->sram_end);
+}
+static int ni_initial_switch_from_arb_f0_to_f1(struct radeon_device *rdev) +{
- return ni_copy_and_switch_arb_sets(rdev, MC_CG_ARB_FREQ_F0, MC_CG_ARB_FREQ_F1);
+}
+static int ni_force_switch_to_arb_f0(struct radeon_device *rdev) +{
- struct rv7xx_power_info *pi = rv770_get_pi(rdev);
- struct ni_power_info *ni_pi = ni_get_pi(rdev);
- u32 tmp;
- int ret;
- ret = rv770_read_smc_sram_dword(rdev, ni_pi->arb_table_start,
&tmp, pi->sram_end);
- if (ret)
return ret;
- tmp = (tmp >> 24) & 0xff;
- if (tmp == MC_CG_ARB_FREQ_F0)
return 0;
- return ni_copy_and_switch_arb_sets(rdev, tmp, MC_CG_ARB_FREQ_F0);
+}
+static int ni_populate_memory_timing_parameters(struct radeon_device *rdev,
struct rv7xx_pl *pl,
SMC_NIslands_MCArbDramTimingRegisterSet *arb_regs)
+{
- u32 dram_timing;
- u32 dram_timing2;
- arb_regs->mc_arb_rfsh_rate =
(u8)rv770_calculate_memory_refresh_rate(rdev, pl->sclk);
- radeon_atom_set_engine_dram_timings(rdev,
pl->sclk,
pl->mclk);
- dram_timing = RREG32(MC_ARB_DRAM_TIMING);
- dram_timing2 = RREG32(MC_ARB_DRAM_TIMING2);
- arb_regs->mc_arb_dram_timing = cpu_to_be32(dram_timing);
- arb_regs->mc_arb_dram_timing2 = cpu_to_be32(dram_timing2);
- return 0;
+}
+static int ni_do_program_memory_timing_parameters(struct radeon_device *rdev,
struct radeon_ps *radeon_state,
unsigned int first_arb_set)
+{
- struct rv7xx_power_info *pi = rv770_get_pi(rdev);
- struct ni_power_info *ni_pi = ni_get_pi(rdev);
- struct ni_ps *state = ni_get_ps(radeon_state);
- SMC_NIslands_MCArbDramTimingRegisterSet arb_regs = { 0 };
- int i, ret = 0;
- for (i = 0; i < state->performance_level_count; i++) {
ret = ni_populate_memory_timing_parameters(rdev, &state->performance_levels[i], &arb_regs);
if (ret)
break;
ret = rv770_copy_bytes_to_smc(rdev,
(u16)(ni_pi->arb_table_start +
offsetof(SMC_NIslands_MCArbDramTimingRegisters, data) +
sizeof(SMC_NIslands_MCArbDramTimingRegisterSet) * (first_arb_set + i)),
(u8 *)&arb_regs,
(u16)sizeof(SMC_NIslands_MCArbDramTimingRegisterSet),
pi->sram_end);
if (ret)
break;
- }
- return ret;
+}
+static int ni_program_memory_timing_parameters(struct radeon_device *rdev) +{
- struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps;
- return ni_do_program_memory_timing_parameters(rdev, radeon_new_state,
NISLANDS_DRIVER_STATE_ARB_INDEX);
+}
+static void ni_populate_initial_mvdd_value(struct radeon_device *rdev,
struct NISLANDS_SMC_VOLTAGE_VALUE *voltage)
+{
- struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
- voltage->index = eg_pi->mvdd_high_index;
- voltage->value = cpu_to_be16(MVDD_HIGH_VALUE);
+}
+static int ni_populate_smc_initial_state(struct radeon_device *rdev,
struct radeon_ps *radeon_initial_state,
NISLANDS_SMC_STATETABLE *table)
+{
- struct ni_ps *initial_state = ni_get_ps(radeon_initial_state);
- struct rv7xx_power_info *pi = rv770_get_pi(rdev);
- struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
- struct ni_power_info *ni_pi = ni_get_pi(rdev);
- u32 reg;
- int ret;
- table->initialState.levels[0].mclk.vMPLL_AD_FUNC_CNTL =
cpu_to_be32(ni_pi->clock_registers.mpll_ad_func_cntl);
- table->initialState.levels[0].mclk.vMPLL_AD_FUNC_CNTL_2 =
cpu_to_be32(ni_pi->clock_registers.mpll_ad_func_cntl_2);
- table->initialState.levels[0].mclk.vMPLL_DQ_FUNC_CNTL =
cpu_to_be32(ni_pi->clock_registers.mpll_dq_func_cntl);
- table->initialState.levels[0].mclk.vMPLL_DQ_FUNC_CNTL_2 =
cpu_to_be32(ni_pi->clock_registers.mpll_dq_func_cntl_2);
- table->initialState.levels[0].mclk.vMCLK_PWRMGT_CNTL =
cpu_to_be32(ni_pi->clock_registers.mclk_pwrmgt_cntl);
- table->initialState.levels[0].mclk.vDLL_CNTL =
cpu_to_be32(ni_pi->clock_registers.dll_cntl);
- table->initialState.levels[0].mclk.vMPLL_SS =
cpu_to_be32(ni_pi->clock_registers.mpll_ss1);
- table->initialState.levels[0].mclk.vMPLL_SS2 =
cpu_to_be32(ni_pi->clock_registers.mpll_ss2);
- table->initialState.levels[0].mclk.mclk_value =
cpu_to_be32(initial_state->performance_levels[0].mclk);
- table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL =
cpu_to_be32(ni_pi->clock_registers.cg_spll_func_cntl);
- table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_2 =
cpu_to_be32(ni_pi->clock_registers.cg_spll_func_cntl_2);
- table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_3 =
cpu_to_be32(ni_pi->clock_registers.cg_spll_func_cntl_3);
- table->initialState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_4 =
cpu_to_be32(ni_pi->clock_registers.cg_spll_func_cntl_4);
- table->initialState.levels[0].sclk.vCG_SPLL_SPREAD_SPECTRUM =
cpu_to_be32(ni_pi->clock_registers.cg_spll_spread_spectrum);
- table->initialState.levels[0].sclk.vCG_SPLL_SPREAD_SPECTRUM_2 =
cpu_to_be32(ni_pi->clock_registers.cg_spll_spread_spectrum_2);
- table->initialState.levels[0].sclk.sclk_value =
cpu_to_be32(initial_state->performance_levels[0].sclk);
- table->initialState.levels[0].arbRefreshState =
NISLANDS_INITIAL_STATE_ARB_INDEX;
- table->initialState.levels[0].ACIndex = 0;
- ret = ni_populate_voltage_value(rdev, &eg_pi->vddc_voltage_table,
initial_state->performance_levels[0].vddc,
&table->initialState.levels[0].vddc);
- if (!ret) {
u16 std_vddc;
ret = ni_get_std_voltage_value(rdev,
&table->initialState.levels[0].vddc,
&std_vddc);
if (!ret)
ni_populate_std_voltage_value(rdev, std_vddc,
table->initialState.levels[0].vddc.index,
&table->initialState.levels[0].std_vddc);
- }
- if (eg_pi->vddci_control)
ni_populate_voltage_value(rdev,
&eg_pi->vddci_voltage_table,
initial_state->performance_levels[0].vddci,
&table->initialState.levels[0].vddci);
- ni_populate_initial_mvdd_value(rdev, &table->initialState.levels[0].mvdd);
- reg = CG_R(0xffff) | CG_L(0);
- table->initialState.levels[0].aT = cpu_to_be32(reg);
- table->initialState.levels[0].bSP = cpu_to_be32(pi->dsp);
- if (pi->boot_in_gen2)
table->initialState.levels[0].gen2PCIE = 1;
- else
table->initialState.levels[0].gen2PCIE = 0;
- if (pi->mem_gddr5) {
table->initialState.levels[0].strobeMode =
cypress_get_strobe_mode_settings(rdev,
initial_state->performance_levels[0].mclk);
if (initial_state->performance_levels[0].mclk > pi->mclk_edc_enable_threshold)
table->initialState.levels[0].mcFlags = NISLANDS_SMC_MC_EDC_RD_FLAG | NISLANDS_SMC_MC_EDC_WR_FLAG;
else
table->initialState.levels[0].mcFlags = 0;
- }
- table->initialState.levelCount = 1;
- table->initialState.flags |= PPSMC_SWSTATE_FLAG_DC;
- table->initialState.levels[0].dpm2.MaxPS = 0;
- table->initialState.levels[0].dpm2.NearTDPDec = 0;
- table->initialState.levels[0].dpm2.AboveSafeInc = 0;
- table->initialState.levels[0].dpm2.BelowSafeInc = 0;
- reg = MIN_POWER_MASK | MAX_POWER_MASK;
- table->initialState.levels[0].SQPowerThrottle = cpu_to_be32(reg);
- reg = MAX_POWER_DELTA_MASK | STI_SIZE_MASK | LTI_RATIO_MASK;
- table->initialState.levels[0].SQPowerThrottle_2 = cpu_to_be32(reg);
- return 0;
+}
+static int ni_populate_smc_acpi_state(struct radeon_device *rdev,
NISLANDS_SMC_STATETABLE *table)
+{
- struct rv7xx_power_info *pi = rv770_get_pi(rdev);
- struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
- struct ni_power_info *ni_pi = ni_get_pi(rdev);
- u32 mpll_ad_func_cntl = ni_pi->clock_registers.mpll_ad_func_cntl;
- u32 mpll_ad_func_cntl_2 = ni_pi->clock_registers.mpll_ad_func_cntl_2;
- u32 mpll_dq_func_cntl = ni_pi->clock_registers.mpll_dq_func_cntl;
- u32 mpll_dq_func_cntl_2 = ni_pi->clock_registers.mpll_dq_func_cntl_2;
- u32 spll_func_cntl = ni_pi->clock_registers.cg_spll_func_cntl;
- u32 spll_func_cntl_2 = ni_pi->clock_registers.cg_spll_func_cntl_2;
- u32 spll_func_cntl_3 = ni_pi->clock_registers.cg_spll_func_cntl_3;
- u32 spll_func_cntl_4 = ni_pi->clock_registers.cg_spll_func_cntl_4;
- u32 mclk_pwrmgt_cntl = ni_pi->clock_registers.mclk_pwrmgt_cntl;
- u32 dll_cntl = ni_pi->clock_registers.dll_cntl;
- u32 reg;
- int ret;
- table->ACPIState = table->initialState;
- table->ACPIState.flags &= ~PPSMC_SWSTATE_FLAG_DC;
- if (pi->acpi_vddc) {
ret = ni_populate_voltage_value(rdev,
&eg_pi->vddc_voltage_table,
pi->acpi_vddc, &table->ACPIState.levels[0].vddc);
if (!ret) {
u16 std_vddc;
ret = ni_get_std_voltage_value(rdev,
&table->ACPIState.levels[0].vddc, &std_vddc);
if (!ret)
ni_populate_std_voltage_value(rdev, std_vddc,
table->ACPIState.levels[0].vddc.index,
&table->ACPIState.levels[0].std_vddc);
}
if (pi->pcie_gen2) {
if (pi->acpi_pcie_gen2)
table->ACPIState.levels[0].gen2PCIE = 1;
else
table->ACPIState.levels[0].gen2PCIE = 0;
} else {
table->ACPIState.levels[0].gen2PCIE = 0;
}
- } else {
ret = ni_populate_voltage_value(rdev,
&eg_pi->vddc_voltage_table,
pi->min_vddc_in_table,
&table->ACPIState.levels[0].vddc);
if (!ret) {
u16 std_vddc;
ret = ni_get_std_voltage_value(rdev,
&table->ACPIState.levels[0].vddc,
&std_vddc);
if (!ret)
ni_populate_std_voltage_value(rdev, std_vddc,
table->ACPIState.levels[0].vddc.index,
&table->ACPIState.levels[0].std_vddc);
}
table->ACPIState.levels[0].gen2PCIE = 0;
- }
- if (eg_pi->acpi_vddci) {
if (eg_pi->vddci_control)
ni_populate_voltage_value(rdev,
&eg_pi->vddci_voltage_table,
eg_pi->acpi_vddci,
&table->ACPIState.levels[0].vddci);
- }
- mpll_ad_func_cntl &= ~PDNB;
- mpll_ad_func_cntl_2 |= BIAS_GEN_PDNB | RESET_EN;
if (pi->mem_gddr5)
mpll_dq_func_cntl &= ~PDNB;
mpll_dq_func_cntl_2 |= BIAS_GEN_PDNB | RESET_EN | BYPASS;
mclk_pwrmgt_cntl |= (MRDCKA0_RESET |
MRDCKA1_RESET |
MRDCKB0_RESET |
MRDCKB1_RESET |
MRDCKC0_RESET |
MRDCKC1_RESET |
MRDCKD0_RESET |
MRDCKD1_RESET);
^ Broken indentation.
mclk_pwrmgt_cntl &= ~(MRDCKA0_PDNB |
MRDCKA1_PDNB |
MRDCKB0_PDNB |
MRDCKB1_PDNB |
MRDCKC0_PDNB |
MRDCKC1_PDNB |
MRDCKD0_PDNB |
MRDCKD1_PDNB);
- dll_cntl |= (MRDCKA0_BYPASS |
MRDCKA1_BYPASS |
MRDCKB0_BYPASS |
MRDCKB1_BYPASS |
MRDCKC0_BYPASS |
MRDCKC1_BYPASS |
MRDCKD0_BYPASS |
MRDCKD1_BYPASS);
^ Broken indentation.
spll_func_cntl_2 &= ~SCLK_MUX_SEL_MASK;
- spll_func_cntl_2 |= SCLK_MUX_SEL(4);
- table->ACPIState.levels[0].mclk.vMPLL_AD_FUNC_CNTL = cpu_to_be32(mpll_ad_func_cntl);
- table->ACPIState.levels[0].mclk.vMPLL_AD_FUNC_CNTL_2 = cpu_to_be32(mpll_ad_func_cntl_2);
- table->ACPIState.levels[0].mclk.vMPLL_DQ_FUNC_CNTL = cpu_to_be32(mpll_dq_func_cntl);
- table->ACPIState.levels[0].mclk.vMPLL_DQ_FUNC_CNTL_2 = cpu_to_be32(mpll_dq_func_cntl_2);
- table->ACPIState.levels[0].mclk.vMCLK_PWRMGT_CNTL = cpu_to_be32(mclk_pwrmgt_cntl);
- table->ACPIState.levels[0].mclk.vDLL_CNTL = cpu_to_be32(dll_cntl);
- table->ACPIState.levels[0].mclk.mclk_value = 0;
- table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL = cpu_to_be32(spll_func_cntl);
- table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_2 = cpu_to_be32(spll_func_cntl_2);
- table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_3 = cpu_to_be32(spll_func_cntl_3);
- table->ACPIState.levels[0].sclk.vCG_SPLL_FUNC_CNTL_4 = cpu_to_be32(spll_func_cntl_4);
- table->ACPIState.levels[0].sclk.sclk_value = 0;
- ni_populate_mvdd_value(rdev, 0, &table->ACPIState.levels[0].mvdd);
- if (eg_pi->dynamic_ac_timing)
table->ACPIState.levels[0].ACIndex = 1;
- table->ACPIState.levels[0].dpm2.MaxPS = 0;
- table->ACPIState.levels[0].dpm2.NearTDPDec = 0;
- table->ACPIState.levels[0].dpm2.AboveSafeInc = 0;
- table->ACPIState.levels[0].dpm2.BelowSafeInc = 0;
- reg = MIN_POWER_MASK | MAX_POWER_MASK;
- table->ACPIState.levels[0].SQPowerThrottle = cpu_to_be32(reg);
- reg = MAX_POWER_DELTA_MASK | STI_SIZE_MASK | LTI_RATIO_MASK;
- table->ACPIState.levels[0].SQPowerThrottle_2 = cpu_to_be32(reg);
- return 0;
+}
+static int ni_init_smc_table(struct radeon_device *rdev) +{
- struct rv7xx_power_info *pi = rv770_get_pi(rdev);
- struct ni_power_info *ni_pi = ni_get_pi(rdev);
- int ret;
- struct radeon_ps *radeon_boot_state = rdev->pm.dpm.boot_ps;
- NISLANDS_SMC_STATETABLE *table = &ni_pi->smc_statetable;
- memset(table, 0, sizeof(NISLANDS_SMC_STATETABLE));
- ni_populate_smc_voltage_tables(rdev, table);
switch (rdev->pm.int_thermal_type) {
case THERMAL_TYPE_NI:
case THERMAL_TYPE_EMC2103_WITH_INTERNAL:
table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_INTERNAL;
break;
case THERMAL_TYPE_NONE:
table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_NONE;
^ Broken indentation.
break;
default:
table->thermalProtectType = PPSMC_THERMAL_PROTECT_TYPE_EXTERNAL;
break;
}
- if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_HARDWAREDC)
table->systemFlags |= PPSMC_SYSTEMFLAG_GPIO_DC;
- if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_REGULATOR_HOT)
table->systemFlags |= PPSMC_SYSTEMFLAG_REGULATOR_HOT;
- if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_STEPVDDC)
table->systemFlags |= PPSMC_SYSTEMFLAG_STEPVDDC;
- if (pi->mem_gddr5)
table->systemFlags |= PPSMC_SYSTEMFLAG_GDDR5;
- ret = ni_populate_smc_initial_state(rdev, radeon_boot_state, table);
- if (ret)
return ret;
- ret = ni_populate_smc_acpi_state(rdev, table);
- if (ret)
return ret;
- table->driverState = table->initialState;
- table->ULVState = table->initialState;
- ret = ni_do_program_memory_timing_parameters(rdev, radeon_boot_state,
NISLANDS_INITIAL_STATE_ARB_INDEX);
- if (ret)
return ret;
- return rv770_copy_bytes_to_smc(rdev, pi->state_table_start, (u8 *)table,
sizeof(NISLANDS_SMC_STATETABLE), pi->sram_end);
+}
+static int ni_calculate_sclk_params(struct radeon_device *rdev,
u32 engine_clock,
NISLANDS_SMC_SCLK_VALUE *sclk)
+{
struct rv7xx_power_info *pi = rv770_get_pi(rdev);
^ Broken indentation.
- struct ni_power_info *ni_pi = ni_get_pi(rdev);
- struct atom_clock_dividers dividers;
- u32 spll_func_cntl = ni_pi->clock_registers.cg_spll_func_cntl;
- u32 spll_func_cntl_2 = ni_pi->clock_registers.cg_spll_func_cntl_2;
- u32 spll_func_cntl_3 = ni_pi->clock_registers.cg_spll_func_cntl_3;
- u32 spll_func_cntl_4 = ni_pi->clock_registers.cg_spll_func_cntl_4;
- u32 cg_spll_spread_spectrum = ni_pi->clock_registers.cg_spll_spread_spectrum;
- u32 cg_spll_spread_spectrum_2 = ni_pi->clock_registers.cg_spll_spread_spectrum_2;
- u64 tmp;
- u32 reference_clock = rdev->clock.spll.reference_freq;
- u32 reference_divider;
- u32 fbdiv;
- int ret;
ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
engine_clock, false, ÷rs);
- if (ret)
return ret;
- reference_divider = 1 + dividers.ref_div;
- tmp = (u64) engine_clock * reference_divider * dividers.post_div;
- fbdiv = (u32) ((16384 * tmp) / reference_clock);
spll_func_cntl &= ~(SPLL_PDIV_A_MASK | SPLL_REF_DIV_MASK);
spll_func_cntl |= SPLL_REF_DIV(dividers.ref_div);
spll_func_cntl |= SPLL_PDIV_A(dividers.post_div);
spll_func_cntl_2 &= ~SCLK_MUX_SEL_MASK;
spll_func_cntl_2 |= SCLK_MUX_SEL(2);
^ Broken indentation.
- spll_func_cntl_3 &= ~SPLL_FB_DIV_MASK;
spll_func_cntl_3 |= SPLL_FB_DIV(fbdiv);
spll_func_cntl_3 |= SPLL_DITHEN;
- if (pi->sclk_ss) {
struct radeon_atom_ss ss;
u32 vco_freq = engine_clock * dividers.post_div;
if (radeon_atombios_get_asic_ss_info(rdev, &ss,
ASIC_INTERNAL_ENGINE_SS, vco_freq)) {
u32 clk_s = reference_clock * 5 / (reference_divider * ss.rate);
u32 clk_v = 4 * ss.percentage * fbdiv / (clk_s * 10000);
cg_spll_spread_spectrum &= ~CLK_S_MASK;
cg_spll_spread_spectrum |= CLK_S(clk_s);
cg_spll_spread_spectrum |= SSEN;
cg_spll_spread_spectrum_2 &= ~CLK_V_MASK;
cg_spll_spread_spectrum_2 |= CLK_V(clk_v);
^ Broken indentation.
}
- }
- sclk->sclk_value = engine_clock;
- sclk->vCG_SPLL_FUNC_CNTL = spll_func_cntl;
- sclk->vCG_SPLL_FUNC_CNTL_2 = spll_func_cntl_2;
- sclk->vCG_SPLL_FUNC_CNTL_3 = spll_func_cntl_3;
- sclk->vCG_SPLL_FUNC_CNTL_4 = spll_func_cntl_4;
- sclk->vCG_SPLL_SPREAD_SPECTRUM = cg_spll_spread_spectrum;
- sclk->vCG_SPLL_SPREAD_SPECTRUM_2 = cg_spll_spread_spectrum_2;
- return 0;
+}
+static int ni_populate_sclk_value(struct radeon_device *rdev,
u32 engine_clock,
NISLANDS_SMC_SCLK_VALUE *sclk)
+{
- NISLANDS_SMC_SCLK_VALUE sclk_tmp;
- int ret;
- ret = ni_calculate_sclk_params(rdev, engine_clock, &sclk_tmp);
- if (!ret) {
sclk->sclk_value = cpu_to_be32(sclk_tmp.sclk_value);
sclk->vCG_SPLL_FUNC_CNTL = cpu_to_be32(sclk_tmp.vCG_SPLL_FUNC_CNTL);
sclk->vCG_SPLL_FUNC_CNTL_2 = cpu_to_be32(sclk_tmp.vCG_SPLL_FUNC_CNTL_2);
sclk->vCG_SPLL_FUNC_CNTL_3 = cpu_to_be32(sclk_tmp.vCG_SPLL_FUNC_CNTL_3);
sclk->vCG_SPLL_FUNC_CNTL_4 = cpu_to_be32(sclk_tmp.vCG_SPLL_FUNC_CNTL_4);
sclk->vCG_SPLL_SPREAD_SPECTRUM = cpu_to_be32(sclk_tmp.vCG_SPLL_SPREAD_SPECTRUM);
sclk->vCG_SPLL_SPREAD_SPECTRUM_2 = cpu_to_be32(sclk_tmp.vCG_SPLL_SPREAD_SPECTRUM_2);
- }
- return ret;
+}
+static int ni_init_smc_spll_table(struct radeon_device *rdev) +{
struct rv7xx_power_info *pi = rv770_get_pi(rdev);
- struct ni_power_info *ni_pi = ni_get_pi(rdev);
- SMC_NISLANDS_SPLL_DIV_TABLE *spll_table;
- NISLANDS_SMC_SCLK_VALUE sclk_params;
- u32 fb_div;
- u32 p_div;
- u32 clk_s;
- u32 clk_v;
- u32 sclk = 0;
- int i, ret;
- u32 tmp;
- if (ni_pi->spll_table_start == 0)
return -EINVAL;
- spll_table = kzalloc(sizeof(SMC_NISLANDS_SPLL_DIV_TABLE), GFP_KERNEL);
- if (spll_table == NULL)
return -ENOMEM;
- for (i = 0; i < 256; i++) {
ret = ni_calculate_sclk_params(rdev, sclk, &sclk_params);
if (ret)
break;
p_div = (sclk_params.vCG_SPLL_FUNC_CNTL & SPLL_PDIV_A_MASK) >> SPLL_PDIV_A_SHIFT;
fb_div = (sclk_params.vCG_SPLL_FUNC_CNTL_3 & SPLL_FB_DIV_MASK) >> SPLL_FB_DIV_SHIFT;
clk_s = (sclk_params.vCG_SPLL_SPREAD_SPECTRUM & CLK_S_MASK) >> CLK_S_SHIFT;
clk_v = (sclk_params.vCG_SPLL_SPREAD_SPECTRUM_2 & CLK_V_MASK) >> CLK_V_SHIFT;
fb_div &= ~0x00001FFF;
fb_div >>= 1;
clk_v >>= 6;
if (p_div & ~(SMC_NISLANDS_SPLL_DIV_TABLE_PDIV_MASK >> SMC_NISLANDS_SPLL_DIV_TABLE_PDIV_SHIFT))
ret = -EINVAL;
if (clk_s & ~(SMC_NISLANDS_SPLL_DIV_TABLE_CLKS_MASK >> SMC_NISLANDS_SPLL_DIV_TABLE_CLKS_SHIFT))
ret = -EINVAL;
if (clk_s & ~(SMC_NISLANDS_SPLL_DIV_TABLE_CLKS_MASK >> SMC_NISLANDS_SPLL_DIV_TABLE_CLKS_SHIFT))
ret = -EINVAL;
if (clk_v & ~(SMC_NISLANDS_SPLL_DIV_TABLE_CLKV_MASK >> SMC_NISLANDS_SPLL_DIV_TABLE_CLKV_SHIFT))
ret = -EINVAL;
if (ret)
break;
tmp = ((fb_div << SMC_NISLANDS_SPLL_DIV_TABLE_FBDIV_SHIFT) & SMC_NISLANDS_SPLL_DIV_TABLE_FBDIV_MASK) |
((p_div << SMC_NISLANDS_SPLL_DIV_TABLE_PDIV_SHIFT) & SMC_NISLANDS_SPLL_DIV_TABLE_PDIV_MASK);
spll_table->freq[i] = cpu_to_be32(tmp);
tmp = ((clk_v << SMC_NISLANDS_SPLL_DIV_TABLE_CLKV_SHIFT) & SMC_NISLANDS_SPLL_DIV_TABLE_CLKV_MASK) |
((clk_s << SMC_NISLANDS_SPLL_DIV_TABLE_CLKS_SHIFT) & SMC_NISLANDS_SPLL_DIV_TABLE_CLKS_MASK);
spll_table->ss[i] = cpu_to_be32(tmp);
sclk += 512;
- }
- if (!ret)
ret = rv770_copy_bytes_to_smc(rdev, ni_pi->spll_table_start, (u8 *)spll_table,
sizeof(SMC_NISLANDS_SPLL_DIV_TABLE), pi->sram_end);
- kfree(spll_table);
- return ret;
+}
+static int ni_populate_mclk_value(struct radeon_device *rdev,
u32 engine_clock,
u32 memory_clock,
NISLANDS_SMC_MCLK_VALUE *mclk,
bool strobe_mode,
bool dll_state_on)
+{
- struct rv7xx_power_info *pi = rv770_get_pi(rdev);
- struct ni_power_info *ni_pi = ni_get_pi(rdev);
- u32 mpll_ad_func_cntl = ni_pi->clock_registers.mpll_ad_func_cntl;
- u32 mpll_ad_func_cntl_2 = ni_pi->clock_registers.mpll_ad_func_cntl_2;
- u32 mpll_dq_func_cntl = ni_pi->clock_registers.mpll_dq_func_cntl;
- u32 mpll_dq_func_cntl_2 = ni_pi->clock_registers.mpll_dq_func_cntl_2;
- u32 mclk_pwrmgt_cntl = ni_pi->clock_registers.mclk_pwrmgt_cntl;
- u32 dll_cntl = ni_pi->clock_registers.dll_cntl;
- u32 mpll_ss1 = ni_pi->clock_registers.mpll_ss1;
- u32 mpll_ss2 = ni_pi->clock_registers.mpll_ss2;
- struct atom_clock_dividers dividers;
- u32 ibias;
- u32 dll_speed;
- int ret;
- u32 mc_seq_misc7;
ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_MEMORY_PLL_PARAM,
memory_clock, strobe_mode, ÷rs);
^ Broken indentation.
- if (ret)
return ret;
- if (!strobe_mode) {
mc_seq_misc7 = RREG32(MC_SEQ_MISC7);
if (mc_seq_misc7 & 0x8000000)
dividers.post_div = 1;
- }
- ibias = cypress_map_clkf_to_ibias(rdev, dividers.whole_fb_div);
mpll_ad_func_cntl &= ~(CLKR_MASK |
YCLK_POST_DIV_MASK |
CLKF_MASK |
CLKFRAC_MASK |
IBIAS_MASK);
mpll_ad_func_cntl |= CLKR(dividers.ref_div);
mpll_ad_func_cntl |= YCLK_POST_DIV(dividers.post_div);
mpll_ad_func_cntl |= CLKF(dividers.whole_fb_div);
mpll_ad_func_cntl |= CLKFRAC(dividers.frac_fb_div);
^ Broken indentation.
- mpll_ad_func_cntl |= IBIAS(ibias);
if (dividers.vco_mode)
^ Broken indentation.
mpll_ad_func_cntl_2 |= VCO_MODE;
- else
mpll_ad_func_cntl_2 &= ~VCO_MODE;
- if (pi->mem_gddr5) {
mpll_dq_func_cntl &= ~(CLKR_MASK |
YCLK_POST_DIV_MASK |
CLKF_MASK |
CLKFRAC_MASK |
IBIAS_MASK);
mpll_dq_func_cntl |= CLKR(dividers.ref_div);
mpll_dq_func_cntl |= YCLK_POST_DIV(dividers.post_div);
mpll_dq_func_cntl |= CLKF(dividers.whole_fb_div);
mpll_dq_func_cntl |= CLKFRAC(dividers.frac_fb_div);
mpll_dq_func_cntl |= IBIAS(ibias);
if (strobe_mode)
mpll_dq_func_cntl &= ~PDNB;
else
mpll_dq_func_cntl |= PDNB;
if (dividers.vco_mode)
mpll_dq_func_cntl_2 |= VCO_MODE;
else
mpll_dq_func_cntl_2 &= ~VCO_MODE;
- }
- if (pi->mclk_ss) {
struct radeon_atom_ss ss;
u32 vco_freq = memory_clock * dividers.post_div;
if (radeon_atombios_get_asic_ss_info(rdev, &ss,
ASIC_INTERNAL_MEMORY_SS, vco_freq)) {
u32 reference_clock = rdev->clock.mpll.reference_freq;
u32 decoded_ref = rv740_get_decoded_reference_divider(dividers.ref_div);
u32 clk_s = reference_clock * 5 / (decoded_ref * ss.rate);
u32 clk_v = ss.percentage *
(0x4000 * dividers.whole_fb_div + 0x800 * dividers.frac_fb_div) / (clk_s * 625);
mpll_ss1 &= ~CLKV_MASK;
mpll_ss1 |= CLKV(clk_v);
mpll_ss2 &= ~CLKS_MASK;
mpll_ss2 |= CLKS(clk_s);
}
- }
dll_speed = rv740_get_dll_speed(pi->mem_gddr5,
memory_clock);
mclk_pwrmgt_cntl &= ~DLL_SPEED_MASK;
mclk_pwrmgt_cntl |= DLL_SPEED(dll_speed);
if (dll_state_on)
mclk_pwrmgt_cntl |= (MRDCKA0_PDNB |
MRDCKA1_PDNB |
MRDCKB0_PDNB |
MRDCKB1_PDNB |
MRDCKC0_PDNB |
MRDCKC1_PDNB |
MRDCKD0_PDNB |
MRDCKD1_PDNB);
else
mclk_pwrmgt_cntl &= ~(MRDCKA0_PDNB |
MRDCKA1_PDNB |
MRDCKB0_PDNB |
MRDCKB1_PDNB |
MRDCKC0_PDNB |
MRDCKC1_PDNB |
MRDCKD0_PDNB |
MRDCKD1_PDNB);
- mclk->mclk_value = cpu_to_be32(memory_clock);
- mclk->vMPLL_AD_FUNC_CNTL = cpu_to_be32(mpll_ad_func_cntl);
- mclk->vMPLL_AD_FUNC_CNTL_2 = cpu_to_be32(mpll_ad_func_cntl_2);
- mclk->vMPLL_DQ_FUNC_CNTL = cpu_to_be32(mpll_dq_func_cntl);
- mclk->vMPLL_DQ_FUNC_CNTL_2 = cpu_to_be32(mpll_dq_func_cntl_2);
- mclk->vMCLK_PWRMGT_CNTL = cpu_to_be32(mclk_pwrmgt_cntl);
- mclk->vDLL_CNTL = cpu_to_be32(dll_cntl);
- mclk->vMPLL_SS = cpu_to_be32(mpll_ss1);
- mclk->vMPLL_SS2 = cpu_to_be32(mpll_ss2);
- return 0;
+}
+static void ni_populate_smc_sp(struct radeon_device *rdev,
struct radeon_ps *radeon_state,
NISLANDS_SMC_SWSTATE *smc_state)
+{
- struct ni_ps *ps = ni_get_ps(radeon_state);
- struct rv7xx_power_info *pi = rv770_get_pi(rdev);
- int i;
- for (i = 0; i < ps->performance_level_count - 1; i++)
smc_state->levels[i].bSP = cpu_to_be32(pi->dsp);
- smc_state->levels[ps->performance_level_count - 1].bSP =
cpu_to_be32(pi->psp);
+}
+static int ni_convert_power_level_to_smc(struct radeon_device *rdev,
struct rv7xx_pl *pl,
NISLANDS_SMC_HW_PERFORMANCE_LEVEL *level)
+{
- struct rv7xx_power_info *pi = rv770_get_pi(rdev);
struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
struct ni_power_info *ni_pi = ni_get_pi(rdev);
- int ret;
- bool dll_state_on;
- u16 std_vddc;
- u32 tmp = RREG32(DC_STUTTER_CNTL);
- level->gen2PCIE = pi->pcie_gen2 ?
((pl->flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) ? 1 : 0) : 0;
- ret = ni_populate_sclk_value(rdev, pl->sclk, &level->sclk);
- if (ret)
return ret;
- level->mcFlags = 0;
- if (pi->mclk_stutter_mode_threshold &&
(pl->mclk <= pi->mclk_stutter_mode_threshold) &&
!eg_pi->uvd_enabled &&
(tmp & DC_STUTTER_ENABLE_A) &&
(tmp & DC_STUTTER_ENABLE_B))
level->mcFlags |= NISLANDS_SMC_MC_STUTTER_EN;
- if (pi->mem_gddr5) {
if (pl->mclk > pi->mclk_edc_enable_threshold)
level->mcFlags |= NISLANDS_SMC_MC_EDC_RD_FLAG;
if (pl->mclk > eg_pi->mclk_edc_wr_enable_threshold)
level->mcFlags |= NISLANDS_SMC_MC_EDC_WR_FLAG;
level->strobeMode = cypress_get_strobe_mode_settings(rdev, pl->mclk);
if (level->strobeMode & NISLANDS_SMC_STROBE_ENABLE) {
if (cypress_get_mclk_frequency_ratio(rdev, pl->mclk, true) >=
((RREG32(MC_SEQ_MISC7) >> 16) & 0xf))
dll_state_on = ((RREG32(MC_SEQ_MISC5) >> 1) & 0x1) ? true : false;
else
dll_state_on = ((RREG32(MC_SEQ_MISC6) >> 1) & 0x1) ? true : false;
} else {
dll_state_on = false;
if (pl->mclk > ni_pi->mclk_rtt_mode_threshold)
level->mcFlags |= NISLANDS_SMC_MC_RTT_ENABLE;
}
ret = ni_populate_mclk_value(rdev, pl->sclk, pl->mclk,
&level->mclk,
(level->strobeMode & NISLANDS_SMC_STROBE_ENABLE) != 0,
dll_state_on);
- } else
ret = ni_populate_mclk_value(rdev, pl->sclk, pl->mclk, &level->mclk, 1, 1);
- if (ret)
return ret;
- ret = ni_populate_voltage_value(rdev, &eg_pi->vddc_voltage_table,
pl->vddc, &level->vddc);
- if (ret)
return ret;
- ret = ni_get_std_voltage_value(rdev, &level->vddc, &std_vddc);
- if (ret)
return ret;
- ni_populate_std_voltage_value(rdev, std_vddc,
level->vddc.index, &level->std_vddc);
- if (eg_pi->vddci_control) {
ret = ni_populate_voltage_value(rdev, &eg_pi->vddci_voltage_table,
pl->vddci, &level->vddci);
if (ret)
return ret;
- }
- ni_populate_mvdd_value(rdev, pl->mclk, &level->mvdd);
- return ret;
+}
+static int ni_populate_smc_t(struct radeon_device *rdev,
struct radeon_ps *radeon_state,
NISLANDS_SMC_SWSTATE *smc_state)
+{
struct rv7xx_power_info *pi = rv770_get_pi(rdev);
struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
- struct ni_ps *state = ni_get_ps(radeon_state);
- u32 a_t;
- u32 t_l, t_h;
- u32 high_bsp;
- int i, ret;
- if (state->performance_level_count >= 9)
return -EINVAL;
- if (state->performance_level_count < 2) {
a_t = CG_R(0xffff) | CG_L(0);
smc_state->levels[0].aT = cpu_to_be32(a_t);
return 0;
- }
- smc_state->levels[0].aT = cpu_to_be32(0);
- for (i = 0; i <= state->performance_level_count - 2; i++) {
if (eg_pi->uvd_enabled)
ret = r600_calculate_at(
1000 * (i * (eg_pi->smu_uvd_hs ? 2 : 8) + 2),
100 * R600_AH_DFLT,
state->performance_levels[i + 1].sclk,
state->performance_levels[i].sclk,
&t_l,
&t_h);
else
ret = r600_calculate_at(
1000 * (i + 1),
100 * R600_AH_DFLT,
state->performance_levels[i + 1].sclk,
state->performance_levels[i].sclk,
&t_l,
&t_h);
if (ret) {
t_h = (i + 1) * 1000 - 50 * R600_AH_DFLT;
t_l = (i + 1) * 1000 + 50 * R600_AH_DFLT;
}
a_t = be32_to_cpu(smc_state->levels[i].aT) & ~CG_R_MASK;
a_t |= CG_R(t_l * pi->bsp / 20000);
smc_state->levels[i].aT = cpu_to_be32(a_t);
high_bsp = (i == state->performance_level_count - 2) ?
pi->pbsp : pi->bsp;
a_t = CG_R(0xffff) | CG_L(t_h * high_bsp / 20000);
smc_state->levels[i + 1].aT = cpu_to_be32(a_t);
- }
- return 0;
+}
+static int ni_populate_power_containment_values(struct radeon_device *rdev,
struct radeon_ps *radeon_state,
NISLANDS_SMC_SWSTATE *smc_state)
+{
struct rv7xx_power_info *pi = rv770_get_pi(rdev);
struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
- struct ni_power_info *ni_pi = ni_get_pi(rdev);
- struct ni_ps *state = ni_get_ps(radeon_state);
- u32 prev_sclk;
- u32 max_sclk;
- u32 min_sclk;
- int i, ret;
- u32 tdp_limit;
- u32 near_tdp_limit;
- u32 power_boost_limit;
- u8 max_ps_percent;
- if (ni_pi->enable_power_containment == false)
return 0;
- if (state->performance_level_count == 0)
return -EINVAL;
- if (smc_state->levelCount != state->performance_level_count)
return -EINVAL;
- ret = ni_calculate_adjusted_tdp_limits(rdev,
false, /* ??? */
rdev->pm.dpm.tdp_adjustment,
&tdp_limit,
&near_tdp_limit);
- if (ret)
return ret;
- power_boost_limit = ni_calculate_power_boost_limit(rdev, radeon_state, near_tdp_limit);
- ret = rv770_write_smc_sram_dword(rdev,
pi->state_table_start +
offsetof(NISLANDS_SMC_STATETABLE, dpm2Params) +
offsetof(PP_NIslands_DPM2Parameters, PowerBoostLimit),
ni_scale_power_for_smc(power_boost_limit, ni_get_smc_power_scaling_factor(rdev)),
pi->sram_end);
- if (ret)
power_boost_limit = 0;
- smc_state->levels[0].dpm2.MaxPS = 0;
- smc_state->levels[0].dpm2.NearTDPDec = 0;
- smc_state->levels[0].dpm2.AboveSafeInc = 0;
- smc_state->levels[0].dpm2.BelowSafeInc = 0;
- smc_state->levels[0].stateFlags |= power_boost_limit ? PPSMC_STATEFLAG_POWERBOOST : 0;
- for (i = 1; i < state->performance_level_count; i++) {
prev_sclk = state->performance_levels[i-1].sclk;
max_sclk = state->performance_levels[i].sclk;
max_ps_percent = (i != (state->performance_level_count - 1)) ?
NISLANDS_DPM2_MAXPS_PERCENT_M : NISLANDS_DPM2_MAXPS_PERCENT_H;
if (max_sclk < prev_sclk)
return -EINVAL;
if ((max_ps_percent == 0) || (prev_sclk == max_sclk) || eg_pi->uvd_enabled)
min_sclk = max_sclk;
else if (1 == i)
min_sclk = prev_sclk;
else
min_sclk = (prev_sclk * (u32)max_ps_percent) / 100;
if (min_sclk < state->performance_levels[0].sclk)
min_sclk = state->performance_levels[0].sclk;
if (min_sclk == 0)
return -EINVAL;
smc_state->levels[i].dpm2.MaxPS =
(u8)((NISLANDS_DPM2_MAX_PULSE_SKIP * (max_sclk - min_sclk)) / max_sclk);
smc_state->levels[i].dpm2.NearTDPDec = NISLANDS_DPM2_NEAR_TDP_DEC;
smc_state->levels[i].dpm2.AboveSafeInc = NISLANDS_DPM2_ABOVE_SAFE_INC;
smc_state->levels[i].dpm2.BelowSafeInc = NISLANDS_DPM2_BELOW_SAFE_INC;
smc_state->levels[i].stateFlags |=
((i != (state->performance_level_count - 1)) && power_boost_limit) ?
PPSMC_STATEFLAG_POWERBOOST : 0;
- }
- return 0;
+}
+static int ni_populate_sq_ramping_values(struct radeon_device *rdev,
struct radeon_ps *radeon_state,
NISLANDS_SMC_SWSTATE *smc_state)
+{
- struct ni_power_info *ni_pi = ni_get_pi(rdev);
- struct ni_ps *state = ni_get_ps(radeon_state);
- u32 sq_power_throttle;
- u32 sq_power_throttle2;
- bool enable_sq_ramping = ni_pi->enable_sq_ramping;
- int i;
- if (state->performance_level_count == 0)
return -EINVAL;
- if (smc_state->levelCount != state->performance_level_count)
return -EINVAL;
- if (rdev->pm.dpm.sq_ramping_threshold == 0)
return -EINVAL;
- if (NISLANDS_DPM2_SQ_RAMP_MAX_POWER > (MAX_POWER_MASK >> MAX_POWER_SHIFT))
enable_sq_ramping = false;
- if (NISLANDS_DPM2_SQ_RAMP_MIN_POWER > (MIN_POWER_MASK >> MIN_POWER_SHIFT))
enable_sq_ramping = false;
- if (NISLANDS_DPM2_SQ_RAMP_MAX_POWER_DELTA > (MAX_POWER_DELTA_MASK >> MAX_POWER_DELTA_SHIFT))
enable_sq_ramping = false;
- if (NISLANDS_DPM2_SQ_RAMP_STI_SIZE > (STI_SIZE_MASK >> STI_SIZE_SHIFT))
enable_sq_ramping = false;
- if (NISLANDS_DPM2_SQ_RAMP_LTI_RATIO <= (LTI_RATIO_MASK >> LTI_RATIO_SHIFT))
enable_sq_ramping = false;
- for (i = 0; i < state->performance_level_count; i++) {
sq_power_throttle = 0;
sq_power_throttle2 = 0;
if ((state->performance_levels[i].sclk >= rdev->pm.dpm.sq_ramping_threshold) &&
enable_sq_ramping) {
sq_power_throttle |= MAX_POWER(NISLANDS_DPM2_SQ_RAMP_MAX_POWER);
sq_power_throttle |= MIN_POWER(NISLANDS_DPM2_SQ_RAMP_MIN_POWER);
sq_power_throttle2 |= MAX_POWER_DELTA(NISLANDS_DPM2_SQ_RAMP_MAX_POWER_DELTA);
sq_power_throttle2 |= STI_SIZE(NISLANDS_DPM2_SQ_RAMP_STI_SIZE);
sq_power_throttle2 |= LTI_RATIO(NISLANDS_DPM2_SQ_RAMP_LTI_RATIO);
} else {
sq_power_throttle |= MAX_POWER_MASK | MIN_POWER_MASK;
sq_power_throttle2 |= MAX_POWER_DELTA_MASK | STI_SIZE_MASK | LTI_RATIO_MASK;
}
smc_state->levels[i].SQPowerThrottle = cpu_to_be32(sq_power_throttle);
smc_state->levels[i].SQPowerThrottle_2 = cpu_to_be32(sq_power_throttle2);
- }
- return 0;
+}
+static int ni_enable_power_containment(struct radeon_device *rdev, bool enable) +{
struct ni_power_info *ni_pi = ni_get_pi(rdev);
- PPSMC_Result smc_result;
- int ret = 0;
- if (ni_pi->enable_power_containment) {
if (enable) {
struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps;
if (!r600_is_uvd_state(radeon_new_state->class, radeon_new_state->class2)) {
smc_result = rv770_send_msg_to_smc(rdev, PPSMC_TDPClampingActive);
if (smc_result != PPSMC_Result_OK) {
ret = -EINVAL;
ni_pi->pc_enabled = false;
} else {
ni_pi->pc_enabled = true;
}
}
} else {
smc_result = rv770_send_msg_to_smc(rdev, PPSMC_TDPClampingInactive);
if (smc_result != PPSMC_Result_OK)
ret = -EINVAL;
ni_pi->pc_enabled = false;
}
- }
- return ret;
+}
+static int ni_convert_power_state_to_smc(struct radeon_device *rdev,
struct radeon_ps *radeon_state,
NISLANDS_SMC_SWSTATE *smc_state)
+{
struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
- struct ni_power_info *ni_pi = ni_get_pi(rdev);
- struct ni_ps *state = ni_get_ps(radeon_state);
- int i, ret;
- u32 threshold = state->performance_levels[state->performance_level_count - 1].sclk * 100 / 100;
- if (!(radeon_state->caps & ATOM_PPLIB_DISALLOW_ON_DC))
smc_state->flags |= PPSMC_SWSTATE_FLAG_DC;
- smc_state->levelCount = 0;
- if (state->performance_level_count > NISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE)
return -EINVAL;
- for (i = 0; i < state->performance_level_count; i++) {
ret = ni_convert_power_level_to_smc(rdev, &state->performance_levels[i],
&smc_state->levels[i]);
smc_state->levels[i].arbRefreshState =
(u8)(NISLANDS_DRIVER_STATE_ARB_INDEX + i);
if (ret)
return ret;
if (ni_pi->enable_power_containment)
smc_state->levels[i].displayWatermark =
(state->performance_levels[i].sclk < threshold) ?
PPSMC_DISPLAY_WATERMARK_LOW : PPSMC_DISPLAY_WATERMARK_HIGH;
else
smc_state->levels[i].displayWatermark = (i < 2) ?
PPSMC_DISPLAY_WATERMARK_LOW : PPSMC_DISPLAY_WATERMARK_HIGH;
if (eg_pi->dynamic_ac_timing)
smc_state->levels[i].ACIndex = NISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT + i;
else
smc_state->levels[i].ACIndex = 0;
smc_state->levelCount++;
- }
- rv770_write_smc_soft_register(rdev, NI_SMC_SOFT_REGISTER_watermark_threshold,
cpu_to_be32(threshold / 512));
- ni_populate_smc_sp(rdev, radeon_state, smc_state);
- ret = ni_populate_power_containment_values(rdev, radeon_state, smc_state);
- if (ret)
ni_pi->enable_power_containment = false;
- ret = ni_populate_sq_ramping_values(rdev, radeon_state, smc_state);
- if (ret)
ni_pi->enable_sq_ramping = false;
- return ni_populate_smc_t(rdev, radeon_state, smc_state);
+}
+static int ni_upload_sw_state(struct radeon_device *rdev) +{
- struct rv7xx_power_info *pi = rv770_get_pi(rdev);
- struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps;
- u16 address = pi->state_table_start +
offsetof(NISLANDS_SMC_STATETABLE, driverState);
- u16 state_size = sizeof(NISLANDS_SMC_SWSTATE) +
((NISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE - 1) * sizeof(NISLANDS_SMC_HW_PERFORMANCE_LEVEL));
- int ret;
- NISLANDS_SMC_SWSTATE *smc_state = kzalloc(state_size, GFP_KERNEL);
- if (smc_state == NULL)
return -ENOMEM;
- ret = ni_convert_power_state_to_smc(rdev, radeon_new_state, smc_state);
- if (ret)
goto done;
- ret = rv770_copy_bytes_to_smc(rdev, address, (u8 *)smc_state, state_size, pi->sram_end);
+done:
- kfree(smc_state);
- return ret;
+}
+static int ni_set_mc_special_registers(struct radeon_device *rdev,
struct ni_mc_reg_table *table)
+{
- struct rv7xx_power_info *pi = rv770_get_pi(rdev);
- u8 i, j, k;
- u32 temp_reg;
- for (i = 0, j = table->last; i < table->last; i++) {
switch (table->mc_reg_address[i].s1) {
case MC_SEQ_MISC1 >> 2:
if (j >= SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE)
return -EINVAL;
temp_reg = RREG32(MC_PMG_CMD_EMRS);
table->mc_reg_address[j].s1 = MC_PMG_CMD_EMRS >> 2;
table->mc_reg_address[j].s0 = MC_SEQ_PMG_CMD_EMRS_LP >> 2;
for (k = 0; k < table->num_entries; k++)
table->mc_reg_table_entry[k].mc_data[j] =
((temp_reg & 0xffff0000)) |
((table->mc_reg_table_entry[k].mc_data[i] & 0xffff0000) >> 16);
j++;
if (j >= SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE)
return -EINVAL;
temp_reg = RREG32(MC_PMG_CMD_MRS);
table->mc_reg_address[j].s1 = MC_PMG_CMD_MRS >> 2;
table->mc_reg_address[j].s0 = MC_SEQ_PMG_CMD_MRS_LP >> 2;
for(k = 0; k < table->num_entries; k++) {
table->mc_reg_table_entry[k].mc_data[j] =
(temp_reg & 0xffff0000) |
(table->mc_reg_table_entry[k].mc_data[i] & 0x0000ffff);
if (!pi->mem_gddr5)
table->mc_reg_table_entry[k].mc_data[j] |= 0x100;
}
j++;
if (j > SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE)
return -EINVAL;
break;
case MC_SEQ_RESERVE_M >> 2:
temp_reg = RREG32(MC_PMG_CMD_MRS1);
table->mc_reg_address[j].s1 = MC_PMG_CMD_MRS1 >> 2;
table->mc_reg_address[j].s0 = MC_SEQ_PMG_CMD_MRS1_LP >> 2;
for (k = 0; k < table->num_entries; k++)
table->mc_reg_table_entry[k].mc_data[j] =
(temp_reg & 0xffff0000) |
(table->mc_reg_table_entry[k].mc_data[i] & 0x0000ffff);
j++;
if (j > SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE)
return -EINVAL;
break;
default:
break;
}
- }
- table->last = j;
- return 0;
+}
+static bool ni_check_s0_mc_reg_index(u16 in_reg, u16 *out_reg) +{
- bool result = true;
- switch (in_reg) {
case MC_SEQ_RAS_TIMING >> 2:
*out_reg = MC_SEQ_RAS_TIMING_LP >> 2;
break;
case MC_SEQ_CAS_TIMING >> 2:
*out_reg = MC_SEQ_CAS_TIMING_LP >> 2;
break;
case MC_SEQ_MISC_TIMING >> 2:
*out_reg = MC_SEQ_MISC_TIMING_LP >> 2;
break;
case MC_SEQ_MISC_TIMING2 >> 2:
*out_reg = MC_SEQ_MISC_TIMING2_LP >> 2;
break;
case MC_SEQ_RD_CTL_D0 >> 2:
*out_reg = MC_SEQ_RD_CTL_D0_LP >> 2;
break;
case MC_SEQ_RD_CTL_D1 >> 2:
*out_reg = MC_SEQ_RD_CTL_D1_LP >> 2;
break;
case MC_SEQ_WR_CTL_D0 >> 2:
*out_reg = MC_SEQ_WR_CTL_D0_LP >> 2;
break;
case MC_SEQ_WR_CTL_D1 >> 2:
*out_reg = MC_SEQ_WR_CTL_D1_LP >> 2;
break;
case MC_PMG_CMD_EMRS >> 2:
*out_reg = MC_SEQ_PMG_CMD_EMRS_LP >> 2;
break;
case MC_PMG_CMD_MRS >> 2:
*out_reg = MC_SEQ_PMG_CMD_MRS_LP >> 2;
break;
case MC_PMG_CMD_MRS1 >> 2:
*out_reg = MC_SEQ_PMG_CMD_MRS1_LP >> 2;
break;
case MC_SEQ_PMG_TIMING >> 2:
*out_reg = MC_SEQ_PMG_TIMING_LP >> 2;
break;
case MC_PMG_CMD_MRS2 >> 2:
*out_reg = MC_SEQ_PMG_CMD_MRS2_LP >> 2;
break;
default:
result = false;
break;
- }
- return result;
+}
+static void ni_set_valid_flag(struct ni_mc_reg_table *table) +{
- u8 i, j;
- for (i = 0; i < table->last; i++) {
for (j = 1; j < table->num_entries; j++) {
if (table->mc_reg_table_entry[j-1].mc_data[i] != table->mc_reg_table_entry[j].mc_data[i]) {
table->valid_flag |= 1 << i;
break;
}
}
- }
+}
+static void ni_set_s0_mc_reg_index(struct ni_mc_reg_table *table) +{
- u32 i;
- u16 address;
- for (i = 0; i < table->last; i++)
table->mc_reg_address[i].s0 =
ni_check_s0_mc_reg_index(table->mc_reg_address[i].s1, &address) ?
address : table->mc_reg_address[i].s1;
+}
+static int ni_copy_vbios_mc_reg_table(struct atom_mc_reg_table *table,
struct ni_mc_reg_table *ni_table)
+{
- u8 i, j;
- if (table->last > SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE)
return -EINVAL;
- if (table->num_entries > MAX_AC_TIMING_ENTRIES)
return -EINVAL;
- for (i = 0; i < table->last; i++)
ni_table->mc_reg_address[i].s1 = table->mc_reg_address[i].s1;
- ni_table->last = table->last;
- for (i = 0; i < table->num_entries; i++) {
ni_table->mc_reg_table_entry[i].mclk_max =
table->mc_reg_table_entry[i].mclk_max;
for (j = 0; j < table->last; j++)
ni_table->mc_reg_table_entry[i].mc_data[j] =
table->mc_reg_table_entry[i].mc_data[j];
- }
- ni_table->num_entries = table->num_entries;
- return 0;
+}
+static int ni_initialize_mc_reg_table(struct radeon_device *rdev) +{
- struct ni_power_info *ni_pi = ni_get_pi(rdev);
- int ret;
- struct atom_mc_reg_table *table;
- struct ni_mc_reg_table *ni_table = &ni_pi->mc_reg_table;
- u8 module_index = rv770_get_memory_module_index(rdev);
table = kzalloc(sizeof(struct atom_mc_reg_table), GFP_KERNEL);
if (!table)
return -ENOMEM;
- WREG32(MC_SEQ_RAS_TIMING_LP, RREG32(MC_SEQ_RAS_TIMING));
- WREG32(MC_SEQ_CAS_TIMING_LP, RREG32(MC_SEQ_CAS_TIMING));
- WREG32(MC_SEQ_MISC_TIMING_LP, RREG32(MC_SEQ_MISC_TIMING));
- WREG32(MC_SEQ_MISC_TIMING2_LP, RREG32(MC_SEQ_MISC_TIMING2));
- WREG32(MC_SEQ_PMG_CMD_EMRS_LP, RREG32(MC_PMG_CMD_EMRS));
- WREG32(MC_SEQ_PMG_CMD_MRS_LP, RREG32(MC_PMG_CMD_MRS));
- WREG32(MC_SEQ_PMG_CMD_MRS1_LP, RREG32(MC_PMG_CMD_MRS1));
- WREG32(MC_SEQ_WR_CTL_D0_LP, RREG32(MC_SEQ_WR_CTL_D0));
- WREG32(MC_SEQ_WR_CTL_D1_LP, RREG32(MC_SEQ_WR_CTL_D1));
- WREG32(MC_SEQ_RD_CTL_D0_LP, RREG32(MC_SEQ_RD_CTL_D0));
- WREG32(MC_SEQ_RD_CTL_D1_LP, RREG32(MC_SEQ_RD_CTL_D1));
- WREG32(MC_SEQ_PMG_TIMING_LP, RREG32(MC_SEQ_PMG_TIMING));
- WREG32(MC_SEQ_PMG_CMD_MRS2_LP, RREG32(MC_PMG_CMD_MRS2));
- ret = radeon_atom_init_mc_reg_table(rdev, module_index, table);
if (ret)
goto init_mc_done;
- ret = ni_copy_vbios_mc_reg_table(table, ni_table);
if (ret)
goto init_mc_done;
- ni_set_s0_mc_reg_index(ni_table);
- ret = ni_set_mc_special_registers(rdev, ni_table);
if (ret)
goto init_mc_done;
- ni_set_valid_flag(ni_table);
+init_mc_done:
kfree(table);
- return ret;
+}
+static void ni_populate_mc_reg_addresses(struct radeon_device *rdev,
SMC_NIslands_MCRegisters *mc_reg_table)
+{
- struct ni_power_info *ni_pi = ni_get_pi(rdev);
- u32 i, j;
- for (i = 0, j = 0; j < ni_pi->mc_reg_table.last; j++) {
if (ni_pi->mc_reg_table.valid_flag & (1 << j)) {
if (i >= SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE)
break;
mc_reg_table->address[i].s0 =
cpu_to_be16(ni_pi->mc_reg_table.mc_reg_address[j].s0);
mc_reg_table->address[i].s1 =
cpu_to_be16(ni_pi->mc_reg_table.mc_reg_address[j].s1);
i++;
}
- }
- mc_reg_table->last = (u8)i;
+}
+static void ni_convert_mc_registers(struct ni_mc_reg_entry *entry,
SMC_NIslands_MCRegisterSet *data,
u32 num_entries, u32 valid_flag)
+{
- u32 i, j;
- for (i = 0, j = 0; j < num_entries; j++) {
if (valid_flag & (1 << j)) {
data->value[i] = cpu_to_be32(entry->mc_data[j]);
i++;
}
- }
+}
+static void ni_convert_mc_reg_table_entry_to_smc(struct radeon_device *rdev,
struct rv7xx_pl *pl,
SMC_NIslands_MCRegisterSet *mc_reg_table_data)
+{
- struct ni_power_info *ni_pi = ni_get_pi(rdev);
- u32 i = 0;
- for (i = 0; i < ni_pi->mc_reg_table.num_entries; i++) {
if (pl->mclk <= ni_pi->mc_reg_table.mc_reg_table_entry[i].mclk_max)
break;
- }
- if ((i == ni_pi->mc_reg_table.num_entries) && (i > 0))
--i;
- ni_convert_mc_registers(&ni_pi->mc_reg_table.mc_reg_table_entry[i],
mc_reg_table_data,
ni_pi->mc_reg_table.last,
ni_pi->mc_reg_table.valid_flag);
+}
+static void ni_convert_mc_reg_table_to_smc(struct radeon_device *rdev,
struct radeon_ps *radeon_state,
SMC_NIslands_MCRegisters *mc_reg_table)
+{
- struct ni_ps *state = ni_get_ps(radeon_state);
- int i;
- for (i = 0; i < state->performance_level_count; i++) {
ni_convert_mc_reg_table_entry_to_smc(rdev,
&state->performance_levels[i],
&mc_reg_table->data[NISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT + i]);
- }
+}
+static int ni_populate_mc_reg_table(struct radeon_device *rdev) +{
- struct rv7xx_power_info *pi = rv770_get_pi(rdev);
- struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
struct ni_power_info *ni_pi = ni_get_pi(rdev);
struct radeon_ps *radeon_boot_state = rdev->pm.dpm.boot_ps;
- struct ni_ps *boot_state = ni_get_ps(radeon_boot_state);
- SMC_NIslands_MCRegisters *mc_reg_table = &ni_pi->smc_mc_reg_table;
- memset(mc_reg_table, 0, sizeof(SMC_NIslands_MCRegisters));
- rv770_write_smc_soft_register(rdev, NI_SMC_SOFT_REGISTER_seq_index, 1);
- ni_populate_mc_reg_addresses(rdev, mc_reg_table);
- ni_convert_mc_reg_table_entry_to_smc(rdev, &boot_state->performance_levels[0],
&mc_reg_table->data[0]);
- ni_convert_mc_registers(&ni_pi->mc_reg_table.mc_reg_table_entry[0],
&mc_reg_table->data[1],
ni_pi->mc_reg_table.last,
ni_pi->mc_reg_table.valid_flag);
- ni_convert_mc_reg_table_to_smc(rdev, radeon_boot_state, mc_reg_table);
- return rv770_copy_bytes_to_smc(rdev, eg_pi->mc_reg_table_start,
(u8 *)mc_reg_table,
sizeof(SMC_NIslands_MCRegisters),
pi->sram_end);
+}
+static int ni_upload_mc_reg_table(struct radeon_device *rdev) +{
- struct rv7xx_power_info *pi = rv770_get_pi(rdev);
- struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
struct ni_power_info *ni_pi = ni_get_pi(rdev);
- struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps;
- struct ni_ps *ni_new_state = ni_get_ps(radeon_new_state);
- SMC_NIslands_MCRegisters *mc_reg_table = &ni_pi->smc_mc_reg_table;
- u16 address;
- memset(mc_reg_table, 0, sizeof(SMC_NIslands_MCRegisters));
- ni_convert_mc_reg_table_to_smc(rdev, radeon_new_state, mc_reg_table);
- address = eg_pi->mc_reg_table_start +
(u16)offsetof(SMC_NIslands_MCRegisters, data[NISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT]);
- return rv770_copy_bytes_to_smc(rdev, address,
(u8 *)&mc_reg_table->data[NISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT],
sizeof(SMC_NIslands_MCRegisterSet) * ni_new_state->performance_level_count,
pi->sram_end);
+}
+static int ni_init_driver_calculated_leakage_table(struct radeon_device *rdev,
PP_NIslands_CACTABLES *cac_tables)
+{
- struct ni_power_info *ni_pi = ni_get_pi(rdev);
- struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
- u32 leakage = 0;
- unsigned int i, j, table_size;
- s32 t;
- u32 smc_leakage, max_leakage = 0;
- u32 scaling_factor;
- table_size = eg_pi->vddc_voltage_table.count;
- if (SMC_NISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES < table_size)
table_size = SMC_NISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES;
- scaling_factor = ni_get_smc_power_scaling_factor(rdev);
- for (i = 0; i < SMC_NISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES; i++) {
for (j = 0; j < table_size; j++) {
t = (1000 * ((i + 1) * 8));
if (t < ni_pi->cac_data.leakage_minimum_temperature)
t = ni_pi->cac_data.leakage_minimum_temperature;
ni_calculate_leakage_for_v_and_t(rdev,
&ni_pi->cac_data.leakage_coefficients,
eg_pi->vddc_voltage_table.entries[j].value,
t,
ni_pi->cac_data.i_leakage,
&leakage);
smc_leakage = ni_scale_power_for_smc(leakage, scaling_factor) / 1000;
if (smc_leakage > max_leakage)
max_leakage = smc_leakage;
cac_tables->cac_lkge_lut[i][j] = cpu_to_be32(smc_leakage);
}
- }
- for (j = table_size; j < SMC_NISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES; j++) {
for (i = 0; i < SMC_NISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES; i++)
cac_tables->cac_lkge_lut[i][j] = cpu_to_be32(max_leakage);
- }
- return 0;
+}
+static int ni_init_simplified_leakage_table(struct radeon_device *rdev,
PP_NIslands_CACTABLES *cac_tables)
+{
- struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
- struct radeon_cac_leakage_table *leakage_table =
&rdev->pm.dpm.dyn_state.cac_leakage_table;
- u32 i, j, table_size;
- u32 smc_leakage, max_leakage = 0;
- u32 scaling_factor;
- if (!leakage_table)
return -EINVAL;
- table_size = leakage_table->count;
- if (eg_pi->vddc_voltage_table.count != table_size)
table_size = (eg_pi->vddc_voltage_table.count < leakage_table->count) ?
eg_pi->vddc_voltage_table.count : leakage_table->count;
- if (SMC_NISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES < table_size)
table_size = SMC_NISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES;
- if (table_size == 0)
return -EINVAL;
- scaling_factor = ni_get_smc_power_scaling_factor(rdev);
- for (j = 0; j < table_size; j++) {
smc_leakage = leakage_table->entries[j].leakage;
if (smc_leakage > max_leakage)
max_leakage = smc_leakage;
for (i = 0; i < SMC_NISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES; i++)
cac_tables->cac_lkge_lut[i][j] =
cpu_to_be32(ni_scale_power_for_smc(smc_leakage, scaling_factor));
- }
- for (j = table_size; j < SMC_NISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES; j++) {
for (i = 0; i < SMC_NISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES; i++)
cac_tables->cac_lkge_lut[i][j] =
cpu_to_be32(ni_scale_power_for_smc(max_leakage, scaling_factor));
- }
- return 0;
+}
+static int ni_initialize_smc_cac_tables(struct radeon_device *rdev) +{
- struct rv7xx_power_info *pi = rv770_get_pi(rdev);
- struct ni_power_info *ni_pi = ni_get_pi(rdev);
- PP_NIslands_CACTABLES *cac_tables = NULL;
- int i, ret;
u32 reg;
- if (ni_pi->enable_cac == false)
return 0;
- cac_tables = kzalloc(sizeof(PP_NIslands_CACTABLES), GFP_KERNEL);
- if (!cac_tables)
return -ENOMEM;
- reg = RREG32(CG_CAC_CTRL) & ~(TID_CNT_MASK | TID_UNIT_MASK);
- reg |= (TID_CNT(ni_pi->cac_weights->tid_cnt) |
TID_UNIT(ni_pi->cac_weights->tid_unit));
- WREG32(CG_CAC_CTRL, reg);
- for (i = 0; i < NISLANDS_DCCAC_MAX_LEVELS; i++)
ni_pi->dc_cac_table[i] = ni_pi->cac_weights->dc_cac[i];
- for (i = 0; i < SMC_NISLANDS_BIF_LUT_NUM_OF_ENTRIES; i++)
cac_tables->cac_bif_lut[i] = ni_pi->cac_weights->pcie_cac[i];
- ni_pi->cac_data.i_leakage = rdev->pm.dpm.cac_leakage;
- ni_pi->cac_data.pwr_const = 0;
- ni_pi->cac_data.dc_cac_value = ni_pi->dc_cac_table[NISLANDS_DCCAC_LEVEL_0];
- ni_pi->cac_data.bif_cac_value = 0;
- ni_pi->cac_data.mc_wr_weight = ni_pi->cac_weights->mc_write_weight;
- ni_pi->cac_data.mc_rd_weight = ni_pi->cac_weights->mc_read_weight;
- ni_pi->cac_data.allow_ovrflw = 0;
- ni_pi->cac_data.l2num_win_tdp = ni_pi->lta_window_size;
- ni_pi->cac_data.num_win_tdp = 0;
- ni_pi->cac_data.lts_truncate_n = ni_pi->lts_truncate;
- if (ni_pi->driver_calculate_cac_leakage)
ret = ni_init_driver_calculated_leakage_table(rdev, cac_tables);
- else
ret = ni_init_simplified_leakage_table(rdev, cac_tables);
- if (ret)
goto done_free;
- cac_tables->pwr_const = cpu_to_be32(ni_pi->cac_data.pwr_const);
- cac_tables->dc_cacValue = cpu_to_be32(ni_pi->cac_data.dc_cac_value);
- cac_tables->bif_cacValue = cpu_to_be32(ni_pi->cac_data.bif_cac_value);
- cac_tables->AllowOvrflw = ni_pi->cac_data.allow_ovrflw;
- cac_tables->MCWrWeight = ni_pi->cac_data.mc_wr_weight;
- cac_tables->MCRdWeight = ni_pi->cac_data.mc_rd_weight;
- cac_tables->numWin_TDP = ni_pi->cac_data.num_win_tdp;
- cac_tables->l2numWin_TDP = ni_pi->cac_data.l2num_win_tdp;
- cac_tables->lts_truncate_n = ni_pi->cac_data.lts_truncate_n;
- ret = rv770_copy_bytes_to_smc(rdev, ni_pi->cac_table_start, (u8 *)cac_tables,
sizeof(PP_NIslands_CACTABLES), pi->sram_end);
+done_free:
- if (ret) {
ni_pi->enable_cac = false;
ni_pi->enable_power_containment = false;
- }
- kfree(cac_tables);
- return 0;
+}
+static int ni_initialize_hardware_cac_manager(struct radeon_device *rdev) +{
- struct ni_power_info *ni_pi = ni_get_pi(rdev);
- u32 reg;
- if (!ni_pi->enable_cac ||
!ni_pi->cac_configuration_required)
return 0;
- if (ni_pi->cac_weights == NULL)
return -EINVAL;
- reg = RREG32_CG(CG_CAC_REGION_1_WEIGHT_0) & ~(WEIGHT_TCP_SIG0_MASK |
WEIGHT_TCP_SIG1_MASK |
WEIGHT_TA_SIG_MASK);
- reg |= (WEIGHT_TCP_SIG0(ni_pi->cac_weights->weight_tcp_sig0) |
WEIGHT_TCP_SIG1(ni_pi->cac_weights->weight_tcp_sig1) |
WEIGHT_TA_SIG(ni_pi->cac_weights->weight_ta_sig));
- WREG32_CG(CG_CAC_REGION_1_WEIGHT_0, reg);
- reg = RREG32_CG(CG_CAC_REGION_1_WEIGHT_1) & ~(WEIGHT_TCC_EN0_MASK |
WEIGHT_TCC_EN1_MASK |
WEIGHT_TCC_EN2_MASK);
- reg |= (WEIGHT_TCC_EN0(ni_pi->cac_weights->weight_tcc_en0) |
WEIGHT_TCC_EN1(ni_pi->cac_weights->weight_tcc_en1) |
WEIGHT_TCC_EN2(ni_pi->cac_weights->weight_tcc_en2));
- WREG32_CG(CG_CAC_REGION_1_WEIGHT_1, reg);
- reg = RREG32_CG(CG_CAC_REGION_2_WEIGHT_0) & ~(WEIGHT_CB_EN0_MASK |
WEIGHT_CB_EN1_MASK |
WEIGHT_CB_EN2_MASK |
WEIGHT_CB_EN3_MASK);
- reg |= (WEIGHT_CB_EN0(ni_pi->cac_weights->weight_cb_en0) |
WEIGHT_CB_EN1(ni_pi->cac_weights->weight_cb_en1) |
WEIGHT_CB_EN2(ni_pi->cac_weights->weight_cb_en2) |
WEIGHT_CB_EN3(ni_pi->cac_weights->weight_cb_en3));
- WREG32_CG(CG_CAC_REGION_2_WEIGHT_0, reg);
- reg = RREG32_CG(CG_CAC_REGION_2_WEIGHT_1) & ~(WEIGHT_DB_SIG0_MASK |
WEIGHT_DB_SIG1_MASK |
WEIGHT_DB_SIG2_MASK |
WEIGHT_DB_SIG3_MASK);
- reg |= (WEIGHT_DB_SIG0(ni_pi->cac_weights->weight_db_sig0) |
WEIGHT_DB_SIG1(ni_pi->cac_weights->weight_db_sig1) |
WEIGHT_DB_SIG2(ni_pi->cac_weights->weight_db_sig2) |
WEIGHT_DB_SIG3(ni_pi->cac_weights->weight_db_sig3));
- WREG32_CG(CG_CAC_REGION_2_WEIGHT_1, reg);
- reg = RREG32_CG(CG_CAC_REGION_2_WEIGHT_2) & ~(WEIGHT_SXM_SIG0_MASK |
WEIGHT_SXM_SIG1_MASK |
WEIGHT_SXM_SIG2_MASK |
WEIGHT_SXS_SIG0_MASK |
WEIGHT_SXS_SIG1_MASK);
- reg |= (WEIGHT_SXM_SIG0(ni_pi->cac_weights->weight_sxm_sig0) |
WEIGHT_SXM_SIG1(ni_pi->cac_weights->weight_sxm_sig1) |
WEIGHT_SXM_SIG2(ni_pi->cac_weights->weight_sxm_sig2) |
WEIGHT_SXS_SIG0(ni_pi->cac_weights->weight_sxs_sig0) |
WEIGHT_SXS_SIG1(ni_pi->cac_weights->weight_sxs_sig1));
- WREG32_CG(CG_CAC_REGION_2_WEIGHT_2, reg);
- reg = RREG32_CG(CG_CAC_REGION_3_WEIGHT_0) & ~(WEIGHT_XBR_0_MASK |
WEIGHT_XBR_1_MASK |
WEIGHT_XBR_2_MASK |
WEIGHT_SPI_SIG0_MASK);
- reg |= (WEIGHT_XBR_0(ni_pi->cac_weights->weight_xbr_0) |
WEIGHT_XBR_1(ni_pi->cac_weights->weight_xbr_1) |
WEIGHT_XBR_2(ni_pi->cac_weights->weight_xbr_2) |
WEIGHT_SPI_SIG0(ni_pi->cac_weights->weight_spi_sig0));
- WREG32_CG(CG_CAC_REGION_3_WEIGHT_0, reg);
- reg = RREG32_CG(CG_CAC_REGION_3_WEIGHT_1) & ~(WEIGHT_SPI_SIG1_MASK |
WEIGHT_SPI_SIG2_MASK |
WEIGHT_SPI_SIG3_MASK |
WEIGHT_SPI_SIG4_MASK |
WEIGHT_SPI_SIG5_MASK);
- reg |= (WEIGHT_SPI_SIG1(ni_pi->cac_weights->weight_spi_sig1) |
WEIGHT_SPI_SIG2(ni_pi->cac_weights->weight_spi_sig2) |
WEIGHT_SPI_SIG3(ni_pi->cac_weights->weight_spi_sig3) |
WEIGHT_SPI_SIG4(ni_pi->cac_weights->weight_spi_sig4) |
WEIGHT_SPI_SIG5(ni_pi->cac_weights->weight_spi_sig5));
- WREG32_CG(CG_CAC_REGION_3_WEIGHT_1, reg);
- reg = RREG32_CG(CG_CAC_REGION_4_WEIGHT_0) & ~(WEIGHT_LDS_SIG0_MASK |
WEIGHT_LDS_SIG1_MASK |
WEIGHT_SC_MASK);
- reg |= (WEIGHT_LDS_SIG0(ni_pi->cac_weights->weight_lds_sig0) |
WEIGHT_LDS_SIG1(ni_pi->cac_weights->weight_lds_sig1) |
WEIGHT_SC(ni_pi->cac_weights->weight_sc));
- WREG32_CG(CG_CAC_REGION_4_WEIGHT_0, reg);
- reg = RREG32_CG(CG_CAC_REGION_4_WEIGHT_1) & ~(WEIGHT_BIF_MASK |
WEIGHT_CP_MASK |
WEIGHT_PA_SIG0_MASK |
WEIGHT_PA_SIG1_MASK |
WEIGHT_VGT_SIG0_MASK);
- reg |= (WEIGHT_BIF(ni_pi->cac_weights->weight_bif) |
WEIGHT_CP(ni_pi->cac_weights->weight_cp) |
WEIGHT_PA_SIG0(ni_pi->cac_weights->weight_pa_sig0) |
WEIGHT_PA_SIG1(ni_pi->cac_weights->weight_pa_sig1) |
WEIGHT_VGT_SIG0(ni_pi->cac_weights->weight_vgt_sig0));
- WREG32_CG(CG_CAC_REGION_4_WEIGHT_1, reg);
- reg = RREG32_CG(CG_CAC_REGION_4_WEIGHT_2) & ~(WEIGHT_VGT_SIG1_MASK |
WEIGHT_VGT_SIG2_MASK |
WEIGHT_DC_SIG0_MASK |
WEIGHT_DC_SIG1_MASK |
WEIGHT_DC_SIG2_MASK);
- reg |= (WEIGHT_VGT_SIG1(ni_pi->cac_weights->weight_vgt_sig1) |
WEIGHT_VGT_SIG2(ni_pi->cac_weights->weight_vgt_sig2) |
WEIGHT_DC_SIG0(ni_pi->cac_weights->weight_dc_sig0) |
WEIGHT_DC_SIG1(ni_pi->cac_weights->weight_dc_sig1) |
WEIGHT_DC_SIG2(ni_pi->cac_weights->weight_dc_sig2));
- WREG32_CG(CG_CAC_REGION_4_WEIGHT_2, reg);
- reg = RREG32_CG(CG_CAC_REGION_4_WEIGHT_3) & ~(WEIGHT_DC_SIG3_MASK |
WEIGHT_UVD_SIG0_MASK |
WEIGHT_UVD_SIG1_MASK |
WEIGHT_SPARE0_MASK |
WEIGHT_SPARE1_MASK);
- reg |= (WEIGHT_DC_SIG3(ni_pi->cac_weights->weight_dc_sig3) |
WEIGHT_UVD_SIG0(ni_pi->cac_weights->weight_uvd_sig0) |
WEIGHT_UVD_SIG1(ni_pi->cac_weights->weight_uvd_sig1) |
WEIGHT_SPARE0(ni_pi->cac_weights->weight_spare0) |
WEIGHT_SPARE1(ni_pi->cac_weights->weight_spare1));
- WREG32_CG(CG_CAC_REGION_4_WEIGHT_3, reg);
- reg = RREG32_CG(CG_CAC_REGION_5_WEIGHT_0) & ~(WEIGHT_SQ_VSP_MASK |
WEIGHT_SQ_VSP0_MASK);
- reg |= (WEIGHT_SQ_VSP(ni_pi->cac_weights->weight_sq_vsp) |
WEIGHT_SQ_VSP0(ni_pi->cac_weights->weight_sq_vsp0));
- WREG32_CG(CG_CAC_REGION_5_WEIGHT_0, reg);
- reg = RREG32_CG(CG_CAC_REGION_5_WEIGHT_1) & ~(WEIGHT_SQ_GPR_MASK);
- reg |= WEIGHT_SQ_GPR(ni_pi->cac_weights->weight_sq_gpr);
- WREG32_CG(CG_CAC_REGION_5_WEIGHT_1, reg);
- reg = RREG32_CG(CG_CAC_REGION_4_OVERRIDE_4) & ~(OVR_MODE_SPARE_0_MASK |
OVR_VAL_SPARE_0_MASK |
OVR_MODE_SPARE_1_MASK |
OVR_VAL_SPARE_1_MASK);
- reg |= (OVR_MODE_SPARE_0(ni_pi->cac_weights->ovr_mode_spare_0) |
OVR_VAL_SPARE_0(ni_pi->cac_weights->ovr_val_spare_0) |
OVR_MODE_SPARE_1(ni_pi->cac_weights->ovr_mode_spare_1) |
OVR_VAL_SPARE_1(ni_pi->cac_weights->ovr_val_spare_1));
- WREG32_CG(CG_CAC_REGION_4_OVERRIDE_4, reg);
- reg = RREG32(SQ_CAC_THRESHOLD) & ~(VSP_MASK |
VSP0_MASK |
GPR_MASK);
- reg |= (VSP(ni_pi->cac_weights->vsp) |
VSP0(ni_pi->cac_weights->vsp0) |
GPR(ni_pi->cac_weights->gpr));
- WREG32(SQ_CAC_THRESHOLD, reg);
- reg = (MCDW_WR_ENABLE |
MCDX_WR_ENABLE |
MCDY_WR_ENABLE |
MCDZ_WR_ENABLE |
INDEX(0x09D4));
- WREG32(MC_CG_CONFIG, reg);
- reg = (READ_WEIGHT(ni_pi->cac_weights->mc_read_weight) |
WRITE_WEIGHT(ni_pi->cac_weights->mc_write_weight) |
ALLOW_OVERFLOW);
- WREG32(MC_CG_DATAPORT, reg);
- return 0;
+}
+static int ni_enable_smc_cac(struct radeon_device *rdev, bool enable) +{
- struct ni_power_info *ni_pi = ni_get_pi(rdev);
- int ret = 0;
- PPSMC_Result smc_result;
- if (ni_pi->enable_cac) {
if (enable) {
struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps;
if (!r600_is_uvd_state(radeon_new_state->class, radeon_new_state->class2)) {
smc_result = rv770_send_msg_to_smc(rdev, PPSMC_MSG_CollectCAC_PowerCorreln);
if (ni_pi->support_cac_long_term_average) {
smc_result = rv770_send_msg_to_smc(rdev, PPSMC_CACLongTermAvgEnable);
if (PPSMC_Result_OK != smc_result)
ni_pi->support_cac_long_term_average = false;
}
smc_result = rv770_send_msg_to_smc(rdev, PPSMC_MSG_EnableCac);
if (PPSMC_Result_OK != smc_result)
ret = -EINVAL;
ni_pi->cac_enabled = (PPSMC_Result_OK == smc_result) ? true : false;
}
} else if (ni_pi->cac_enabled) {
smc_result = rv770_send_msg_to_smc(rdev, PPSMC_MSG_DisableCac);
ni_pi->cac_enabled = false;
if (ni_pi->support_cac_long_term_average) {
smc_result = rv770_send_msg_to_smc(rdev, PPSMC_CACLongTermAvgDisable);
if (PPSMC_Result_OK != smc_result)
ni_pi->support_cac_long_term_average = false;
}
}
- }
- return ret;
+}
+static int ni_pcie_performance_request(struct radeon_device *rdev,
u8 perf_req, bool advertise)
+{
- struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
+#if defined(CONFIG_ACPI)
- if ((perf_req == PCIE_PERF_REQ_PECI_GEN1) ||
(perf_req == PCIE_PERF_REQ_PECI_GEN2)) {
if (eg_pi->pcie_performance_request_registered == false)
radeon_acpi_pcie_notify_device_ready(rdev);
eg_pi->pcie_performance_request_registered = true;
return radeon_acpi_pcie_performance_request(rdev, perf_req, advertise);
- } else if ((perf_req == PCIE_PERF_REQ_REMOVE_REGISTRY) &&
eg_pi->pcie_performance_request_registered) {
eg_pi->pcie_performance_request_registered = false;
return radeon_acpi_pcie_performance_request(rdev, perf_req, advertise);
- }
+#endif
- return 0;
+}
+static int ni_advertise_gen2_capability(struct radeon_device *rdev) +{
- struct rv7xx_power_info *pi = rv770_get_pi(rdev);
- u32 tmp;
tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL);
if ((tmp & LC_OTHER_SIDE_EVER_SENT_GEN2) &&
(tmp & LC_OTHER_SIDE_SUPPORTS_GEN2))
pi->pcie_gen2 = true;
else
pi->pcie_gen2 = false;
- if (!pi->pcie_gen2)
ni_pcie_performance_request(rdev, PCIE_PERF_REQ_PECI_GEN2, true);
- return 0;
+}
+static void ni_enable_bif_dynamic_pcie_gen2(struct radeon_device *rdev,
bool enable)
+{
struct rv7xx_power_info *pi = rv770_get_pi(rdev);
u32 tmp, bif;
- tmp = RREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL);
- if ((tmp & LC_OTHER_SIDE_EVER_SENT_GEN2) &&
(tmp & LC_OTHER_SIDE_SUPPORTS_GEN2)) {
if (enable) {
if (!pi->boot_in_gen2) {
bif = RREG32(CG_BIF_REQ_AND_RSP) & ~CG_CLIENT_REQ_MASK;
bif |= CG_CLIENT_REQ(0xd);
WREG32(CG_BIF_REQ_AND_RSP, bif);
}
tmp &= ~LC_HW_VOLTAGE_IF_CONTROL_MASK;
tmp |= LC_HW_VOLTAGE_IF_CONTROL(1);
tmp |= LC_GEN2_EN_STRAP;
tmp |= LC_CLR_FAILED_SPD_CHANGE_CNT;
WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp);
udelay(10);
tmp &= ~LC_CLR_FAILED_SPD_CHANGE_CNT;
WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp);
} else {
if (!pi->boot_in_gen2) {
bif = RREG32(CG_BIF_REQ_AND_RSP) & ~CG_CLIENT_REQ_MASK;
bif |= CG_CLIENT_REQ(0xd);
WREG32(CG_BIF_REQ_AND_RSP, bif);
tmp &= ~LC_HW_VOLTAGE_IF_CONTROL_MASK;
tmp &= ~LC_GEN2_EN_STRAP;
}
WREG32_PCIE_PORT(PCIE_LC_SPEED_CNTL, tmp);
}
- }
+}
+static void ni_enable_dynamic_pcie_gen2(struct radeon_device *rdev,
bool enable)
+{
- ni_enable_bif_dynamic_pcie_gen2(rdev, enable);
- if (enable)
WREG32_P(GENERAL_PWRMGT, ENABLE_GEN2PCIE, ~ENABLE_GEN2PCIE);
- else
WREG32_P(GENERAL_PWRMGT, 0, ~ENABLE_GEN2PCIE);
+}
+void ni_dpm_setup_asic(struct radeon_device *rdev) +{
- struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
- ni_read_clock_registers(rdev);
- btc_read_arb_registers(rdev);
- rv770_get_memory_type(rdev);
- if (eg_pi->pcie_performance_request)
ni_advertise_gen2_capability(rdev);
- rv770_get_pcie_gen2_status(rdev);
- rv770_enable_acpi_pm(rdev);
+}
+int ni_dpm_enable(struct radeon_device *rdev) +{
- struct rv7xx_power_info *pi = rv770_get_pi(rdev);
- struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
- if (pi->gfx_clock_gating)
ni_cg_clockgating_default(rdev);
if (btc_dpm_enabled(rdev))
return -EINVAL;
- if (pi->mg_clock_gating)
ni_mg_clockgating_default(rdev);
- if (eg_pi->ls_clock_gating)
ni_ls_clockgating_default(rdev);
- if (pi->voltage_control) {
rv770_enable_voltage_control(rdev, true);
cypress_construct_voltage_tables(rdev);
- }
- if (eg_pi->dynamic_ac_timing)
ni_initialize_mc_reg_table(rdev);
- if (pi->dynamic_ss)
cypress_enable_spread_spectrum(rdev, true);
- if (pi->thermal_protection)
rv770_enable_thermal_protection(rdev, true);
- rv770_setup_bsp(rdev);
- rv770_program_git(rdev);
- rv770_program_tp(rdev);
- rv770_program_tpp(rdev);
- rv770_program_sstp(rdev);
- cypress_enable_display_gap(rdev);
- rv770_program_vc(rdev);
- if (pi->dynamic_pcie_gen2)
ni_enable_dynamic_pcie_gen2(rdev, true);
- if (rv770_upload_firmware(rdev))
return -EINVAL;
- ni_process_firmware_header(rdev);
- ni_initial_switch_from_arb_f0_to_f1(rdev);
- ni_init_smc_table(rdev);
- ni_init_smc_spll_table(rdev);
- ni_init_arb_table_index(rdev);
- if (eg_pi->dynamic_ac_timing)
ni_populate_mc_reg_table(rdev);
- ni_initialize_smc_cac_tables(rdev);
- ni_initialize_hardware_cac_manager(rdev);
- ni_populate_smc_tdp_limits(rdev);
- ni_program_response_times(rdev);
- r7xx_start_smc(rdev);
- cypress_notify_smc_display_change(rdev, false);
- cypress_enable_sclk_control(rdev, true);
- if (eg_pi->memory_transition)
cypress_enable_mclk_control(rdev, true);
- cypress_start_dpm(rdev);
- if (pi->gfx_clock_gating)
ni_gfx_clockgating_enable(rdev, true);
- if (pi->mg_clock_gating)
ni_mg_clockgating_enable(rdev, true);
- if (eg_pi->ls_clock_gating)
ni_ls_clockgating_enable(rdev, true);
- if (rdev->irq.installed &&
r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) {
PPSMC_Result result;
rv770_set_thermal_temperature_range(rdev, R600_TEMP_RANGE_MIN, 0xff * 1000);
rdev->irq.dpm_thermal = true;
radeon_irq_set(rdev);
result = rv770_send_msg_to_smc(rdev, PPSMC_MSG_EnableThermalInterrupt);
if (result != PPSMC_Result_OK)
DRM_DEBUG_KMS("Could not enable thermal interrupts.\n");
- }
- rv770_enable_auto_throttle_source(rdev, RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL, true);
- return 0;
+}
+void ni_dpm_disable(struct radeon_device *rdev) +{
- struct rv7xx_power_info *pi = rv770_get_pi(rdev);
- struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
- if (!btc_dpm_enabled(rdev))
return;
- rv770_clear_vc(rdev);
- if (pi->thermal_protection)
rv770_enable_thermal_protection(rdev, false);
- ni_enable_power_containment(rdev, false);
- ni_enable_smc_cac(rdev, false);
- cypress_enable_spread_spectrum(rdev, false);
- rv770_enable_auto_throttle_source(rdev, RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL, false);
- if (pi->dynamic_pcie_gen2)
ni_enable_dynamic_pcie_gen2(rdev, false);
- if (rdev->irq.installed &&
r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) {
rdev->irq.dpm_thermal = false;
radeon_irq_set(rdev);
- }
- if (pi->gfx_clock_gating)
ni_gfx_clockgating_enable(rdev, false);
- if (pi->mg_clock_gating)
ni_mg_clockgating_enable(rdev, false);
- if (eg_pi->ls_clock_gating)
ni_ls_clockgating_enable(rdev, false);
- ni_stop_dpm(rdev);
- btc_reset_to_default(rdev);
- ni_stop_smc(rdev);
- ni_force_switch_to_arb_f0(rdev);
+}
+int ni_power_control_set_level(struct radeon_device *rdev) +{
- ni_restrict_performance_levels_before_switch(rdev);
- rv770_halt_smc(rdev);
- ni_populate_smc_tdp_limits(rdev);
- rv770_resume_smc(rdev);
- rv770_set_sw_state(rdev);
- return 0;
+}
+int ni_dpm_set_power_state(struct radeon_device *rdev) +{
- struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
- int ret;
- ni_apply_state_adjust_rules(rdev);
- ni_restrict_performance_levels_before_switch(rdev);
- ni_enable_power_containment(rdev, false);
- ni_enable_smc_cac(rdev, false);
- rv770_halt_smc(rdev);
- if (eg_pi->smu_uvd_hs)
btc_notify_uvd_to_smc(rdev);
- ni_upload_sw_state(rdev);
- if (eg_pi->dynamic_ac_timing)
ni_upload_mc_reg_table(rdev);
- ret = ni_program_memory_timing_parameters(rdev);
- if (ret)
return ret;
- rv770_resume_smc(rdev);
- rv770_set_sw_state(rdev);
- ni_enable_smc_cac(rdev, true);
- ni_enable_power_containment(rdev, true);
- return 0;
+}
+void ni_dpm_reset_asic(struct radeon_device *rdev) +{
- ni_restrict_performance_levels_before_switch(rdev);
- rv770_set_boot_state(rdev);
+}
+union power_info {
- struct _ATOM_POWERPLAY_INFO info;
- struct _ATOM_POWERPLAY_INFO_V2 info_2;
- struct _ATOM_POWERPLAY_INFO_V3 info_3;
- struct _ATOM_PPLIB_POWERPLAYTABLE pplib;
- struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2;
- struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3;
+};
+union pplib_clock_info {
- struct _ATOM_PPLIB_R600_CLOCK_INFO r600;
- struct _ATOM_PPLIB_RS780_CLOCK_INFO rs780;
- struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO evergreen;
- struct _ATOM_PPLIB_SUMO_CLOCK_INFO sumo;
+};
+union pplib_power_state {
- struct _ATOM_PPLIB_STATE v1;
- struct _ATOM_PPLIB_STATE_V2 v2;
+};
+static void ni_parse_pplib_non_clock_info(struct radeon_device *rdev,
struct radeon_ps *rps,
struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info,
u8 table_rev)
+{
- rps->caps = le32_to_cpu(non_clock_info->ulCapsAndSettings);
- rps->class = le16_to_cpu(non_clock_info->usClassification);
- rps->class2 = le16_to_cpu(non_clock_info->usClassification2);
- if (ATOM_PPLIB_NONCLOCKINFO_VER1 < table_rev) {
rps->vclk = le32_to_cpu(non_clock_info->ulVCLK);
rps->dclk = le32_to_cpu(non_clock_info->ulDCLK);
- } else if (r600_is_uvd_state(rps->class, rps->class2)) {
rps->vclk = RV770_DEFAULT_VCLK_FREQ;
rps->dclk = RV770_DEFAULT_DCLK_FREQ;
- } else {
rps->vclk = 0;
rps->dclk = 0;
- }
- if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT)
rdev->pm.dpm.boot_ps = rps;
- if (rps->class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE)
rdev->pm.dpm.uvd_ps = rps;
+}
+static void ni_parse_pplib_clock_info(struct radeon_device *rdev,
struct radeon_ps *rps, int index,
union pplib_clock_info *clock_info)
+{
- struct rv7xx_power_info *pi = rv770_get_pi(rdev);
- struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
- struct ni_ps *ps = ni_get_ps(rps);
- u16 vddc;
- struct rv7xx_pl *pl = &ps->performance_levels[index];
- ps->performance_level_count = index + 1;
- pl->sclk = le16_to_cpu(clock_info->evergreen.usEngineClockLow);
- pl->sclk |= clock_info->evergreen.ucEngineClockHigh << 16;
- pl->mclk = le16_to_cpu(clock_info->evergreen.usMemoryClockLow);
- pl->mclk |= clock_info->evergreen.ucMemoryClockHigh << 16;
- pl->vddc = le16_to_cpu(clock_info->evergreen.usVDDC);
- pl->vddci = le16_to_cpu(clock_info->evergreen.usVDDCI);
- pl->flags = le32_to_cpu(clock_info->evergreen.ulFlags);
- /* patch up vddc if necessary */
- if (pl->vddc == 0xff01) {
if (radeon_atom_get_max_vddc(rdev, 0, 0, &vddc) == 0)
pl->vddc = vddc;
- }
- if (rps->class & ATOM_PPLIB_CLASSIFICATION_ACPI) {
pi->acpi_vddc = pl->vddc;
eg_pi->acpi_vddci = pl->vddci;
if (ps->performance_levels[0].flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2)
pi->acpi_pcie_gen2 = true;
else
pi->acpi_pcie_gen2 = false;
- }
- if (rps->class2 & ATOM_PPLIB_CLASSIFICATION2_ULV) {
eg_pi->ulv.supported = true;
eg_pi->ulv.pl = pl;
- }
- if (pi->min_vddc_in_table > pl->vddc)
pi->min_vddc_in_table = pl->vddc;
- if (pi->max_vddc_in_table < pl->vddc)
pi->max_vddc_in_table = pl->vddc;
- /* patch up boot state */
- if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT) {
u16 vddc, vddci;
radeon_atombios_get_default_voltages(rdev, &vddc, &vddci);
pl->mclk = rdev->clock.default_mclk;
pl->sclk = rdev->clock.default_sclk;
pl->vddc = vddc;
pl->vddci = vddci;
- }
- if ((rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) ==
ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE) {
rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac.sclk = pl->sclk;
rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac.mclk = pl->mclk;
rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac.vddc = pl->vddc;
rdev->pm.dpm.dyn_state.max_clock_voltage_on_ac.vddci = pl->vddci;
- }
+}
+static int ni_parse_power_table(struct radeon_device *rdev) +{
- struct radeon_mode_info *mode_info = &rdev->mode_info;
- struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info;
- union pplib_power_state *power_state;
- int i, j;
- union pplib_clock_info *clock_info;
- union power_info *power_info;
- int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo);
u16 data_offset;
- u8 frev, crev;
- struct ni_ps *ps;
- if (!atom_parse_data_header(mode_info->atom_context, index, NULL,
&frev, &crev, &data_offset))
return -EINVAL;
- power_info = (union power_info *)(mode_info->atom_context->bios + data_offset);
- rdev->pm.dpm.ps = kzalloc(sizeof(struct radeon_ps) *
power_info->pplib.ucNumStates, GFP_KERNEL);
- if (!rdev->pm.dpm.ps)
return -ENOMEM;
- rdev->pm.dpm.platform_caps = le32_to_cpu(power_info->pplib.ulPlatformCaps);
- rdev->pm.dpm.backbias_response_time = le16_to_cpu(power_info->pplib.usBackbiasTime);
- rdev->pm.dpm.voltage_response_time = le16_to_cpu(power_info->pplib.usVoltageTime);
- for (i = 0; i < power_info->pplib.ucNumStates; i++) {
power_state = (union pplib_power_state *)
(mode_info->atom_context->bios + data_offset +
le16_to_cpu(power_info->pplib.usStateArrayOffset) +
i * power_info->pplib.ucStateEntrySize);
non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *)
(mode_info->atom_context->bios + data_offset +
le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset) +
(power_state->v1.ucNonClockStateIndex *
power_info->pplib.ucNonClockSize));
if (power_info->pplib.ucStateEntrySize - 1) {
ps = kzalloc(sizeof(struct ni_ps), GFP_KERNEL);
if (ps == NULL) {
kfree(rdev->pm.dpm.ps);
return -ENOMEM;
}
rdev->pm.dpm.ps[i].ps_priv = ps;
ni_parse_pplib_non_clock_info(rdev, &rdev->pm.dpm.ps[i],
non_clock_info,
power_info->pplib.ucNonClockSize);
for (j = 0; j < (power_info->pplib.ucStateEntrySize - 1); j++) {
clock_info = (union pplib_clock_info *)
(mode_info->atom_context->bios + data_offset +
le16_to_cpu(power_info->pplib.usClockInfoArrayOffset) +
(power_state->v1.ucClockStateIndices[j] *
power_info->pplib.ucClockInfoSize));
ni_parse_pplib_clock_info(rdev,
&rdev->pm.dpm.ps[i], j,
clock_info);
}
}
- }
- rdev->pm.dpm.num_ps = power_info->pplib.ucNumStates;
- return 0;
+}
+int ni_dpm_init(struct radeon_device *rdev) +{
- struct rv7xx_power_info *pi;
- struct evergreen_power_info *eg_pi;
- struct ni_power_info *ni_pi;
- int index = GetIndexIntoMasterTable(DATA, ASIC_InternalSS_Info);
- u16 data_offset, size;
- u8 frev, crev;
- struct atom_clock_dividers dividers;
- int ret;
- ni_pi = kzalloc(sizeof(struct ni_power_info), GFP_KERNEL);
- if (ni_pi == NULL)
return -ENOMEM;
- rdev->pm.dpm.priv = ni_pi;
- eg_pi = &ni_pi->eg;
- pi = &eg_pi->rv7xx;
- rv770_get_max_vddc(rdev);
- eg_pi->ulv.supported = false;
- pi->acpi_vddc = 0;
- eg_pi->acpi_vddci = 0;
- pi->min_vddc_in_table = 0;
- pi->max_vddc_in_table = 0;
- ret = ni_parse_power_table(rdev);
- if (ret)
return ret;
- ret = r600_parse_extended_power_table(rdev);
- if (ret)
return ret;
- ni_patch_dependency_tables_based_on_leakage(rdev);
- if (rdev->pm.dpm.voltage_response_time == 0)
rdev->pm.dpm.voltage_response_time = R600_VOLTAGERESPONSETIME_DFLT;
- if (rdev->pm.dpm.backbias_response_time == 0)
rdev->pm.dpm.backbias_response_time = R600_BACKBIASRESPONSETIME_DFLT;
- ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM,
0, false, ÷rs);
- if (ret)
pi->ref_div = dividers.ref_div + 1;
- else
pi->ref_div = R600_REFERENCEDIVIDER_DFLT;
- pi->rlp = RV770_RLP_DFLT;
- pi->rmp = RV770_RMP_DFLT;
- pi->lhp = RV770_LHP_DFLT;
- pi->lmp = RV770_LMP_DFLT;
- eg_pi->ats[0].rlp = RV770_RLP_DFLT;
- eg_pi->ats[0].rmp = RV770_RMP_DFLT;
- eg_pi->ats[0].lhp = RV770_LHP_DFLT;
- eg_pi->ats[0].lmp = RV770_LMP_DFLT;
- eg_pi->ats[1].rlp = BTC_RLP_UVD_DFLT;
- eg_pi->ats[1].rmp = BTC_RMP_UVD_DFLT;
- eg_pi->ats[1].lhp = BTC_LHP_UVD_DFLT;
- eg_pi->ats[1].lmp = BTC_LMP_UVD_DFLT;
- eg_pi->smu_uvd_hs = true;
- if (rdev->pdev->device == 0x6707) {
pi->mclk_strobe_mode_threshold = 55000;
pi->mclk_edc_enable_threshold = 55000;
eg_pi->mclk_edc_wr_enable_threshold = 55000;
- } else {
pi->mclk_strobe_mode_threshold = 40000;
pi->mclk_edc_enable_threshold = 40000;
eg_pi->mclk_edc_wr_enable_threshold = 40000;
- }
- ni_pi->mclk_rtt_mode_threshold = eg_pi->mclk_edc_wr_enable_threshold;
- pi->voltage_control =
radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDC);
- pi->mvdd_control =
radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_MVDDC);
- eg_pi->vddci_control =
radeon_atom_is_voltage_gpio(rdev, SET_VOLTAGE_TYPE_ASIC_VDDCI);
- if (atom_parse_data_header(rdev->mode_info.atom_context, index, &size,
&frev, &crev, &data_offset)) {
pi->sclk_ss = true;
pi->mclk_ss = true;
pi->dynamic_ss = true;
- } else {
pi->sclk_ss = false;
pi->mclk_ss = false;
pi->dynamic_ss = true;
- }
- pi->asi = RV770_ASI_DFLT;
- pi->pasi = CYPRESS_HASI_DFLT;
- pi->vrc = CYPRESS_VRC_DFLT;
- pi->power_gating = false;
- pi->gfx_clock_gating = true;
- pi->mg_clock_gating = true;
- pi->mgcgtssm = true;
- eg_pi->ls_clock_gating = false;
- eg_pi->sclk_deep_sleep = false;
- pi->dynamic_pcie_gen2 = true;
- if (pi->gfx_clock_gating &&
(rdev->pm.int_thermal_type != THERMAL_TYPE_NONE))
pi->thermal_protection = true;
- else
pi->thermal_protection = false;
- pi->display_gap = true;
- pi->dcodt = true;
- pi->ulps = true;
- eg_pi->dynamic_ac_timing = true;
- eg_pi->abm = true;
- eg_pi->mcls = true;
- eg_pi->light_sleep = true;
- eg_pi->memory_transition = true;
+#if defined(CONFIG_ACPI)
- eg_pi->pcie_performance_request =
radeon_acpi_is_pcie_performance_request_supported(rdev);
+#else
- eg_pi->pcie_performance_request = false;
+#endif
- eg_pi->dll_default_on = false;
- eg_pi->sclk_deep_sleep = false;
- pi->mclk_stutter_mode_threshold = 0;
- pi->sram_end = SMC_RAM_END;
- rdev->pm.dpm.dyn_state.mclk_sclk_ratio = 3;
- rdev->pm.dpm.dyn_state.vddc_vddci_delta = 200;
- rdev->pm.dpm.dyn_state.min_vddc_for_pcie_gen2 = 900;
- rdev->pm.dpm.dyn_state.valid_sclk_values.count = ARRAY_SIZE(btc_valid_sclk);
- rdev->pm.dpm.dyn_state.valid_sclk_values.values = btc_valid_sclk;
- rdev->pm.dpm.dyn_state.valid_mclk_values.count = 0;
- rdev->pm.dpm.dyn_state.valid_mclk_values.values = NULL;
- rdev->pm.dpm.dyn_state.sclk_mclk_delta = 12500;
- ni_pi->cac_data.leakage_coefficients.at = 516;
- ni_pi->cac_data.leakage_coefficients.bt = 18;
- ni_pi->cac_data.leakage_coefficients.av = 51;
- ni_pi->cac_data.leakage_coefficients.bv = 2957;
- switch (rdev->pdev->device) {
- case 0x6700:
- case 0x6701:
- case 0x6702:
- case 0x6703:
- case 0x6718:
ni_pi->cac_weights = &cac_weights_cayman_xt;
break;
- case 0x6705:
- case 0x6719:
- case 0x671D:
- case 0x671C:
- default:
ni_pi->cac_weights = &cac_weights_cayman_pro;
break;
- case 0x6704:
- case 0x6706:
- case 0x6707:
- case 0x6708:
- case 0x6709:
ni_pi->cac_weights = &cac_weights_cayman_le;
break;
- }
- if (ni_pi->cac_weights->enable_power_containment_by_default) {
ni_pi->enable_power_containment = true;
ni_pi->enable_cac = true;
ni_pi->enable_sq_ramping = true;
- } else {
ni_pi->enable_power_containment = false;
ni_pi->enable_cac = false;
ni_pi->enable_sq_ramping = false;
- }
- ni_pi->driver_calculate_cac_leakage = false;
- ni_pi->cac_configuration_required = true;
- if (ni_pi->cac_configuration_required) {
ni_pi->support_cac_long_term_average = true;
ni_pi->lta_window_size = ni_pi->cac_weights->l2_lta_window_size;
ni_pi->lts_truncate = ni_pi->cac_weights->lts_truncate;
- } else {
ni_pi->support_cac_long_term_average = false;
ni_pi->lta_window_size = 0;
ni_pi->lts_truncate = 0;
- }
- ni_pi->use_power_boost_limit = true;
- return 0;
+}
+void ni_dpm_fini(struct radeon_device *rdev) +{
- int i;
- for (i = 0; i < rdev->pm.dpm.num_ps; i++) {
kfree(rdev->pm.dpm.ps[i].ps_priv);
- }
- kfree(rdev->pm.dpm.ps);
- kfree(rdev->pm.dpm.priv);
- r600_free_extended_power_table(rdev);
+}
+void ni_dpm_print_power_state(struct radeon_device *rdev,
struct radeon_ps *rps)
+{
- struct ni_ps *ps = ni_get_ps(rps);
- struct rv7xx_pl *pl;
- int i;
- r600_dpm_print_class_info(rps->class, rps->class2);
- r600_dpm_print_cap_info(rps->caps);
- printk("\tuvd vclk: %d dclk: %d\n", rps->vclk, rps->dclk);
- for (i = 0; i < ps->performance_level_count; i++) {
pl = &ps->performance_levels[i];
printk("\t\tpower level 0 sclk: %u mclk: %u vddc: %u vddci: %u\n",
pl->sclk, pl->mclk, pl->vddc, pl->vddci);
- }
- r600_dpm_print_ps_status(rdev, rps);
+}
+u32 ni_dpm_get_sclk(struct radeon_device *rdev, bool low) +{
- struct ni_ps *requested_state = ni_get_ps(rdev->pm.dpm.requested_ps);
- if (low)
return requested_state->performance_levels[0].sclk;
- else
return requested_state->performance_levels[requested_state->performance_level_count - 1].sclk;
+}
+u32 ni_dpm_get_mclk(struct radeon_device *rdev, bool low) +{
- struct ni_ps *requested_state = ni_get_ps(rdev->pm.dpm.requested_ps);
- if (low)
return requested_state->performance_levels[0].mclk;
- else
return requested_state->performance_levels[requested_state->performance_level_count - 1].mclk;
+}
diff --git a/drivers/gpu/drm/radeon/ni_dpm.h b/drivers/gpu/drm/radeon/ni_dpm.h new file mode 100644 index 0000000..e10f747 --- /dev/null +++ b/drivers/gpu/drm/radeon/ni_dpm.h @@ -0,0 +1,233 @@ +/*
- Copyright 2012 Advanced Micro Devices, Inc.
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- OTHER DEALINGS IN THE SOFTWARE.
- */
+#ifndef __NI_DPM_H__ +#define __NI_DPM_H__
+#include "cypress_dpm.h" +#include "btc_dpm.h" +#include "nislands_smc.h"
+struct ni_clock_registers {
- u32 cg_spll_func_cntl;
- u32 cg_spll_func_cntl_2;
- u32 cg_spll_func_cntl_3;
- u32 cg_spll_func_cntl_4;
- u32 cg_spll_spread_spectrum;
- u32 cg_spll_spread_spectrum_2;
- u32 mclk_pwrmgt_cntl;
- u32 dll_cntl;
- u32 mpll_ad_func_cntl;
- u32 mpll_ad_func_cntl_2;
- u32 mpll_dq_func_cntl;
- u32 mpll_dq_func_cntl_2;
- u32 mpll_ss1;
- u32 mpll_ss2;
+};
+struct ni_mc_reg_entry {
- u32 mclk_max;
- u32 mc_data[SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE];
+};
+struct ni_mc_reg_table {
- u8 last;
- u8 num_entries;
- u16 valid_flag;
- struct ni_mc_reg_entry mc_reg_table_entry[MAX_AC_TIMING_ENTRIES];
- SMC_NIslands_MCRegisterAddress mc_reg_address[SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE];
+};
+#define NISLANDS_MCREGISTERTABLE_FIRST_DRIVERSTATE_SLOT 2
+enum ni_dc_cac_level +{
- NISLANDS_DCCAC_LEVEL_0 = 0,
- NISLANDS_DCCAC_LEVEL_1,
- NISLANDS_DCCAC_LEVEL_2,
- NISLANDS_DCCAC_LEVEL_3,
- NISLANDS_DCCAC_LEVEL_4,
- NISLANDS_DCCAC_LEVEL_5,
- NISLANDS_DCCAC_LEVEL_6,
- NISLANDS_DCCAC_LEVEL_7,
- NISLANDS_DCCAC_MAX_LEVELS
+};
+struct ni_leakage_coeffients +{
- u32 at;
- u32 bt;
- u32 av;
- u32 bv;
- s32 t_slope;
- s32 t_intercept;
- u32 t_ref;
+};
+struct ni_cac_data +{
- struct ni_leakage_coeffients leakage_coefficients;
- u32 i_leakage;
- s32 leakage_minimum_temperature;
- u32 pwr_const;
- u32 dc_cac_value;
- u32 bif_cac_value;
- u32 lkge_pwr;
- u8 mc_wr_weight;
- u8 mc_rd_weight;
- u8 allow_ovrflw;
- u8 num_win_tdp;
- u8 l2num_win_tdp;
- u8 lts_truncate_n;
+};
+struct ni_cac_weights +{
- u32 weight_tcp_sig0;
- u32 weight_tcp_sig1;
- u32 weight_ta_sig;
- u32 weight_tcc_en0;
- u32 weight_tcc_en1;
- u32 weight_tcc_en2;
- u32 weight_cb_en0;
- u32 weight_cb_en1;
- u32 weight_cb_en2;
- u32 weight_cb_en3;
- u32 weight_db_sig0;
- u32 weight_db_sig1;
- u32 weight_db_sig2;
- u32 weight_db_sig3;
- u32 weight_sxm_sig0;
- u32 weight_sxm_sig1;
- u32 weight_sxm_sig2;
- u32 weight_sxs_sig0;
- u32 weight_sxs_sig1;
- u32 weight_xbr_0;
- u32 weight_xbr_1;
- u32 weight_xbr_2;
- u32 weight_spi_sig0;
- u32 weight_spi_sig1;
- u32 weight_spi_sig2;
- u32 weight_spi_sig3;
- u32 weight_spi_sig4;
- u32 weight_spi_sig5;
- u32 weight_lds_sig0;
- u32 weight_lds_sig1;
- u32 weight_sc;
- u32 weight_bif;
- u32 weight_cp;
- u32 weight_pa_sig0;
- u32 weight_pa_sig1;
- u32 weight_vgt_sig0;
- u32 weight_vgt_sig1;
- u32 weight_vgt_sig2;
- u32 weight_dc_sig0;
- u32 weight_dc_sig1;
- u32 weight_dc_sig2;
- u32 weight_dc_sig3;
- u32 weight_uvd_sig0;
- u32 weight_uvd_sig1;
- u32 weight_spare0;
- u32 weight_spare1;
- u32 weight_sq_vsp;
- u32 weight_sq_vsp0;
- u32 weight_sq_gpr;
- u32 ovr_mode_spare_0;
- u32 ovr_val_spare_0;
- u32 ovr_mode_spare_1;
- u32 ovr_val_spare_1;
- u32 vsp;
- u32 vsp0;
- u32 gpr;
- u8 mc_read_weight;
- u8 mc_write_weight;
- u32 tid_cnt;
- u32 tid_unit;
- u32 l2_lta_window_size;
- u32 lts_truncate;
- u32 dc_cac[NISLANDS_DCCAC_MAX_LEVELS];
- u32 pcie_cac[SMC_NISLANDS_BIF_LUT_NUM_OF_ENTRIES];
- bool enable_power_containment_by_default;
+};
+struct ni_ps {
- u16 performance_level_count;
- bool dc_compatible;
- struct rv7xx_pl performance_levels[NISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE];
+};
+struct ni_power_info {
- /* must be first! */
- struct evergreen_power_info eg;
- struct ni_clock_registers clock_registers;
- struct ni_mc_reg_table mc_reg_table;
- u32 mclk_rtt_mode_threshold;
- /* flags */
- bool use_power_boost_limit;
- bool support_cac_long_term_average;
- bool cac_enabled;
- bool cac_configuration_required;
- bool driver_calculate_cac_leakage;
- bool pc_enabled;
- bool enable_power_containment;
- bool enable_cac;
- bool enable_sq_ramping;
- /* smc offsets */
- u16 arb_table_start;
- u16 fan_table_start;
- u16 cac_table_start;
- u16 spll_table_start;
- /* CAC stuff */
- struct ni_cac_data cac_data;
- u32 dc_cac_table[NISLANDS_DCCAC_MAX_LEVELS];
- const struct ni_cac_weights *cac_weights;
- u8 lta_window_size;
- u8 lts_truncate;
- struct ni_ps hw_ps;
- /* scratch structs */
- SMC_NIslands_MCRegisters smc_mc_reg_table;
- NISLANDS_SMC_STATETABLE smc_statetable;
+};
+#define NISLANDS_INITIAL_STATE_ARB_INDEX 0 +#define NISLANDS_ACPI_STATE_ARB_INDEX 1 +#define NISLANDS_ULV_STATE_ARB_INDEX 2 +#define NISLANDS_DRIVER_STATE_ARB_INDEX 3
+#define NISLANDS_DPM2_MAX_PULSE_SKIP 256
+#define NISLANDS_DPM2_NEAR_TDP_DEC 10 +#define NISLANDS_DPM2_ABOVE_SAFE_INC 5 +#define NISLANDS_DPM2_BELOW_SAFE_INC 20
+#define NISLANDS_DPM2_TDP_SAFE_LIMIT_PERCENT 80
+#define NISLANDS_DPM2_MAXPS_PERCENT_H 90 +#define NISLANDS_DPM2_MAXPS_PERCENT_M 0
+#define NISLANDS_DPM2_SQ_RAMP_MAX_POWER 0x3FFF +#define NISLANDS_DPM2_SQ_RAMP_MIN_POWER 0x12 +#define NISLANDS_DPM2_SQ_RAMP_MAX_POWER_DELTA 0x15 +#define NISLANDS_DPM2_SQ_RAMP_STI_SIZE 0x1E +#define NISLANDS_DPM2_SQ_RAMP_LTI_RATIO 0xF
+#endif diff --git a/drivers/gpu/drm/radeon/nid.h b/drivers/gpu/drm/radeon/nid.h index 7b8da52..1775043 100644 --- a/drivers/gpu/drm/radeon/nid.h +++ b/drivers/gpu/drm/radeon/nid.h @@ -492,6 +492,558 @@ /* TN SMU registers */ #define TN_CURRENT_GNB_TEMP 0x1F390
+/* pm registers */ +#define SMC_MSG 0x20c +#define HOST_SMC_MSG(x) ((x) << 0) +#define HOST_SMC_MSG_MASK (0xff << 0) +#define HOST_SMC_MSG_SHIFT 0 +#define HOST_SMC_RESP(x) ((x) << 8) +#define HOST_SMC_RESP_MASK (0xff << 8) +#define HOST_SMC_RESP_SHIFT 8 +#define SMC_HOST_MSG(x) ((x) << 16) +#define SMC_HOST_MSG_MASK (0xff << 16) +#define SMC_HOST_MSG_SHIFT 16 +#define SMC_HOST_RESP(x) ((x) << 24) +#define SMC_HOST_RESP_MASK (0xff << 24) +#define SMC_HOST_RESP_SHIFT 24
+#define CG_SPLL_FUNC_CNTL 0x600 +#define SPLL_RESET (1 << 0) +#define SPLL_SLEEP (1 << 1) +#define SPLL_BYPASS_EN (1 << 3) +#define SPLL_REF_DIV(x) ((x) << 4) +#define SPLL_REF_DIV_MASK (0x3f << 4) +#define SPLL_PDIV_A(x) ((x) << 20) +#define SPLL_PDIV_A_MASK (0x7f << 20) +#define SPLL_PDIV_A_SHIFT 20 +#define CG_SPLL_FUNC_CNTL_2 0x604 +#define SCLK_MUX_SEL(x) ((x) << 0) +#define SCLK_MUX_SEL_MASK (0x1ff << 0) +#define CG_SPLL_FUNC_CNTL_3 0x608 +#define SPLL_FB_DIV(x) ((x) << 0) +#define SPLL_FB_DIV_MASK (0x3ffffff << 0) +#define SPLL_FB_DIV_SHIFT 0 +#define SPLL_DITHEN (1 << 28)
+#define MPLL_CNTL_MODE 0x61c +# define SS_SSEN (1 << 24) +# define SS_DSMODE_EN (1 << 25)
+#define MPLL_AD_FUNC_CNTL 0x624 +#define CLKF(x) ((x) << 0) +#define CLKF_MASK (0x7f << 0) +#define CLKR(x) ((x) << 7) +#define CLKR_MASK (0x1f << 7) +#define CLKFRAC(x) ((x) << 12) +#define CLKFRAC_MASK (0x1f << 12) +#define YCLK_POST_DIV(x) ((x) << 17) +#define YCLK_POST_DIV_MASK (3 << 17) +#define IBIAS(x) ((x) << 20) +#define IBIAS_MASK (0x3ff << 20) +#define RESET (1 << 30) +#define PDNB (1 << 31) +#define MPLL_AD_FUNC_CNTL_2 0x628 +#define BYPASS (1 << 19) +#define BIAS_GEN_PDNB (1 << 24) +#define RESET_EN (1 << 25) +#define VCO_MODE (1 << 29) +#define MPLL_DQ_FUNC_CNTL 0x62c +#define MPLL_DQ_FUNC_CNTL_2 0x630
+#define GENERAL_PWRMGT 0x63c +# define GLOBAL_PWRMGT_EN (1 << 0) +# define STATIC_PM_EN (1 << 1) +# define THERMAL_PROTECTION_DIS (1 << 2) +# define THERMAL_PROTECTION_TYPE (1 << 3) +# define ENABLE_GEN2PCIE (1 << 4) +# define ENABLE_GEN2XSP (1 << 5) +# define SW_SMIO_INDEX(x) ((x) << 6) +# define SW_SMIO_INDEX_MASK (3 << 6) +# define SW_SMIO_INDEX_SHIFT 6 +# define LOW_VOLT_D2_ACPI (1 << 8) +# define LOW_VOLT_D3_ACPI (1 << 9) +# define VOLT_PWRMGT_EN (1 << 10) +# define BACKBIAS_PAD_EN (1 << 18) +# define BACKBIAS_VALUE (1 << 19) +# define DYN_SPREAD_SPECTRUM_EN (1 << 23) +# define AC_DC_SW (1 << 24)
+#define SCLK_PWRMGT_CNTL 0x644 +# define SCLK_PWRMGT_OFF (1 << 0) +# define SCLK_LOW_D1 (1 << 1) +# define FIR_RESET (1 << 4) +# define FIR_FORCE_TREND_SEL (1 << 5) +# define FIR_TREND_MODE (1 << 6) +# define DYN_GFX_CLK_OFF_EN (1 << 7) +# define GFX_CLK_FORCE_ON (1 << 8) +# define GFX_CLK_REQUEST_OFF (1 << 9) +# define GFX_CLK_FORCE_OFF (1 << 10) +# define GFX_CLK_OFF_ACPI_D1 (1 << 11) +# define GFX_CLK_OFF_ACPI_D2 (1 << 12) +# define GFX_CLK_OFF_ACPI_D3 (1 << 13) +# define DYN_LIGHT_SLEEP_EN (1 << 14) +#define MCLK_PWRMGT_CNTL 0x648 +# define DLL_SPEED(x) ((x) << 0) +# define DLL_SPEED_MASK (0x1f << 0) +# define MPLL_PWRMGT_OFF (1 << 5) +# define DLL_READY (1 << 6) +# define MC_INT_CNTL (1 << 7) +# define MRDCKA0_PDNB (1 << 8) +# define MRDCKA1_PDNB (1 << 9) +# define MRDCKB0_PDNB (1 << 10) +# define MRDCKB1_PDNB (1 << 11) +# define MRDCKC0_PDNB (1 << 12) +# define MRDCKC1_PDNB (1 << 13) +# define MRDCKD0_PDNB (1 << 14) +# define MRDCKD1_PDNB (1 << 15) +# define MRDCKA0_RESET (1 << 16) +# define MRDCKA1_RESET (1 << 17) +# define MRDCKB0_RESET (1 << 18) +# define MRDCKB1_RESET (1 << 19) +# define MRDCKC0_RESET (1 << 20) +# define MRDCKC1_RESET (1 << 21) +# define MRDCKD0_RESET (1 << 22) +# define MRDCKD1_RESET (1 << 23) +# define DLL_READY_READ (1 << 24) +# define USE_DISPLAY_GAP (1 << 25) +# define USE_DISPLAY_URGENT_NORMAL (1 << 26) +# define MPLL_TURNOFF_D2 (1 << 28) +#define DLL_CNTL 0x64c +# define MRDCKA0_BYPASS (1 << 24) +# define MRDCKA1_BYPASS (1 << 25) +# define MRDCKB0_BYPASS (1 << 26) +# define MRDCKB1_BYPASS (1 << 27) +# define MRDCKC0_BYPASS (1 << 28) +# define MRDCKC1_BYPASS (1 << 29) +# define MRDCKD0_BYPASS (1 << 30) +# define MRDCKD1_BYPASS (1 << 31)
+#define CG_AT 0x6d4 +# define CG_R(x) ((x) << 0) +# define CG_R_MASK (0xffff << 0) +# define CG_L(x) ((x) << 16) +# define CG_L_MASK (0xffff << 16)
+#define CG_BIF_REQ_AND_RSP 0x7f4 +#define CG_CLIENT_REQ(x) ((x) << 0) +#define CG_CLIENT_REQ_MASK (0xff << 0) +#define CG_CLIENT_REQ_SHIFT 0 +#define CG_CLIENT_RESP(x) ((x) << 8) +#define CG_CLIENT_RESP_MASK (0xff << 8) +#define CG_CLIENT_RESP_SHIFT 8 +#define CLIENT_CG_REQ(x) ((x) << 16) +#define CLIENT_CG_REQ_MASK (0xff << 16) +#define CLIENT_CG_REQ_SHIFT 16 +#define CLIENT_CG_RESP(x) ((x) << 24) +#define CLIENT_CG_RESP_MASK (0xff << 24) +#define CLIENT_CG_RESP_SHIFT 24
+#define CG_SPLL_SPREAD_SPECTRUM 0x790 +#define SSEN (1 << 0) +#define CLK_S(x) ((x) << 4) +#define CLK_S_MASK (0xfff << 4) +#define CLK_S_SHIFT 4 +#define CG_SPLL_SPREAD_SPECTRUM_2 0x794 +#define CLK_V(x) ((x) << 0) +#define CLK_V_MASK (0x3ffffff << 0) +#define CLK_V_SHIFT 0
+#define SMC_SCRATCH0 0x81c
+#define CG_SPLL_FUNC_CNTL_4 0x850
+#define MPLL_SS1 0x85c +#define CLKV(x) ((x) << 0) +#define CLKV_MASK (0x3ffffff << 0) +#define MPLL_SS2 0x860 +#define CLKS(x) ((x) << 0) +#define CLKS_MASK (0xfff << 0)
+#define CG_CAC_CTRL 0x88c +#define TID_CNT(x) ((x) << 0) +#define TID_CNT_MASK (0x3fff << 0) +#define TID_UNIT(x) ((x) << 14) +#define TID_UNIT_MASK (0xf << 14)
+#define MC_CG_CONFIG 0x25bc +#define MCDW_WR_ENABLE (1 << 0) +#define MCDX_WR_ENABLE (1 << 1) +#define MCDY_WR_ENABLE (1 << 2) +#define MCDZ_WR_ENABLE (1 << 3) +#define MC_RD_ENABLE(x) ((x) << 4) +#define MC_RD_ENABLE_MASK (3 << 4) +#define INDEX(x) ((x) << 6) +#define INDEX_MASK (0xfff << 6) +#define INDEX_SHIFT 6
+#define MC_ARB_CAC_CNTL 0x2750 +#define ENABLE (1 << 0) +#define READ_WEIGHT(x) ((x) << 1) +#define READ_WEIGHT_MASK (0x3f << 1) +#define READ_WEIGHT_SHIFT 1 +#define WRITE_WEIGHT(x) ((x) << 7) +#define WRITE_WEIGHT_MASK (0x3f << 7) +#define WRITE_WEIGHT_SHIFT 7 +#define ALLOW_OVERFLOW (1 << 13)
+#define MC_ARB_DRAM_TIMING 0x2774 +#define MC_ARB_DRAM_TIMING2 0x2778
+#define MC_ARB_RFSH_RATE 0x27b0 +#define POWERMODE0(x) ((x) << 0) +#define POWERMODE0_MASK (0xff << 0) +#define POWERMODE0_SHIFT 0 +#define POWERMODE1(x) ((x) << 8) +#define POWERMODE1_MASK (0xff << 8) +#define POWERMODE1_SHIFT 8 +#define POWERMODE2(x) ((x) << 16) +#define POWERMODE2_MASK (0xff << 16) +#define POWERMODE2_SHIFT 16 +#define POWERMODE3(x) ((x) << 24) +#define POWERMODE3_MASK (0xff << 24) +#define POWERMODE3_SHIFT 24
+#define MC_ARB_CG 0x27e8 +#define CG_ARB_REQ(x) ((x) << 0) +#define CG_ARB_REQ_MASK (0xff << 0) +#define CG_ARB_REQ_SHIFT 0 +#define CG_ARB_RESP(x) ((x) << 8) +#define CG_ARB_RESP_MASK (0xff << 8) +#define CG_ARB_RESP_SHIFT 8 +#define ARB_CG_REQ(x) ((x) << 16) +#define ARB_CG_REQ_MASK (0xff << 16) +#define ARB_CG_REQ_SHIFT 16 +#define ARB_CG_RESP(x) ((x) << 24) +#define ARB_CG_RESP_MASK (0xff << 24) +#define ARB_CG_RESP_SHIFT 24
+#define MC_ARB_DRAM_TIMING_1 0x27f0 +#define MC_ARB_DRAM_TIMING_2 0x27f4 +#define MC_ARB_DRAM_TIMING_3 0x27f8 +#define MC_ARB_DRAM_TIMING2_1 0x27fc +#define MC_ARB_DRAM_TIMING2_2 0x2800 +#define MC_ARB_DRAM_TIMING2_3 0x2804 +#define MC_ARB_BURST_TIME 0x2808 +#define STATE0(x) ((x) << 0) +#define STATE0_MASK (0x1f << 0) +#define STATE0_SHIFT 0 +#define STATE1(x) ((x) << 5) +#define STATE1_MASK (0x1f << 5) +#define STATE1_SHIFT 5 +#define STATE2(x) ((x) << 10) +#define STATE2_MASK (0x1f << 10) +#define STATE2_SHIFT 10 +#define STATE3(x) ((x) << 15) +#define STATE3_MASK (0x1f << 15) +#define STATE3_SHIFT 15
+#define MC_CG_DATAPORT 0x2884
+#define MC_SEQ_RAS_TIMING 0x28a0 +#define MC_SEQ_CAS_TIMING 0x28a4 +#define MC_SEQ_MISC_TIMING 0x28a8 +#define MC_SEQ_MISC_TIMING2 0x28ac +#define MC_SEQ_PMG_TIMING 0x28b0 +#define MC_SEQ_RD_CTL_D0 0x28b4 +#define MC_SEQ_RD_CTL_D1 0x28b8 +#define MC_SEQ_WR_CTL_D0 0x28bc +#define MC_SEQ_WR_CTL_D1 0x28c0
+#define MC_SEQ_MISC0 0x2a00 +#define MC_SEQ_MISC0_GDDR5_SHIFT 28 +#define MC_SEQ_MISC0_GDDR5_MASK 0xf0000000 +#define MC_SEQ_MISC0_GDDR5_VALUE 5 +#define MC_SEQ_MISC1 0x2a04 +#define MC_SEQ_RESERVE_M 0x2a08 +#define MC_PMG_CMD_EMRS 0x2a0c
+#define MC_SEQ_MISC3 0x2a2c
+#define MC_SEQ_MISC5 0x2a54 +#define MC_SEQ_MISC6 0x2a58
+#define MC_SEQ_MISC7 0x2a64
+#define MC_SEQ_RAS_TIMING_LP 0x2a6c +#define MC_SEQ_CAS_TIMING_LP 0x2a70 +#define MC_SEQ_MISC_TIMING_LP 0x2a74 +#define MC_SEQ_MISC_TIMING2_LP 0x2a78 +#define MC_SEQ_WR_CTL_D0_LP 0x2a7c +#define MC_SEQ_WR_CTL_D1_LP 0x2a80 +#define MC_SEQ_PMG_CMD_EMRS_LP 0x2a84 +#define MC_SEQ_PMG_CMD_MRS_LP 0x2a88
+#define MC_PMG_CMD_MRS 0x2aac
+#define MC_SEQ_RD_CTL_D0_LP 0x2b1c +#define MC_SEQ_RD_CTL_D1_LP 0x2b20
+#define MC_PMG_CMD_MRS1 0x2b44 +#define MC_SEQ_PMG_CMD_MRS1_LP 0x2b48 +#define MC_SEQ_PMG_TIMING_LP 0x2b4c
+#define MC_PMG_CMD_MRS2 0x2b5c +#define MC_SEQ_PMG_CMD_MRS2_LP 0x2b60
+#define LB_SYNC_RESET_SEL 0x6b28 +#define LB_SYNC_RESET_SEL_MASK (3 << 0) +#define LB_SYNC_RESET_SEL_SHIFT 0
+#define DC_STUTTER_CNTL 0x6b30 +#define DC_STUTTER_ENABLE_A (1 << 0) +#define DC_STUTTER_ENABLE_B (1 << 1)
+#define SQ_CAC_THRESHOLD 0x8e4c +#define VSP(x) ((x) << 0) +#define VSP_MASK (0xff << 0) +#define VSP_SHIFT 0 +#define VSP0(x) ((x) << 8) +#define VSP0_MASK (0xff << 8) +#define VSP0_SHIFT 8 +#define GPR(x) ((x) << 16) +#define GPR_MASK (0xff << 16) +#define GPR_SHIFT 16
+#define SQ_POWER_THROTTLE 0x8e58 +#define MIN_POWER(x) ((x) << 0) +#define MIN_POWER_MASK (0x3fff << 0) +#define MIN_POWER_SHIFT 0 +#define MAX_POWER(x) ((x) << 16) +#define MAX_POWER_MASK (0x3fff << 16) +#define MAX_POWER_SHIFT 0 +#define SQ_POWER_THROTTLE2 0x8e5c +#define MAX_POWER_DELTA(x) ((x) << 0) +#define MAX_POWER_DELTA_MASK (0x3fff << 0) +#define MAX_POWER_DELTA_SHIFT 0 +#define STI_SIZE(x) ((x) << 16) +#define STI_SIZE_MASK (0x3ff << 16) +#define STI_SIZE_SHIFT 16 +#define LTI_RATIO(x) ((x) << 27) +#define LTI_RATIO_MASK (0xf << 27) +#define LTI_RATIO_SHIFT 27
+/* CG indirect registers */ +#define CG_CAC_REGION_1_WEIGHT_0 0x83 +#define WEIGHT_TCP_SIG0(x) ((x) << 0) +#define WEIGHT_TCP_SIG0_MASK (0x3f << 0) +#define WEIGHT_TCP_SIG0_SHIFT 0 +#define WEIGHT_TCP_SIG1(x) ((x) << 6) +#define WEIGHT_TCP_SIG1_MASK (0x3f << 6) +#define WEIGHT_TCP_SIG1_SHIFT 6 +#define WEIGHT_TA_SIG(x) ((x) << 12) +#define WEIGHT_TA_SIG_MASK (0x3f << 12) +#define WEIGHT_TA_SIG_SHIFT 12 +#define CG_CAC_REGION_1_WEIGHT_1 0x84 +#define WEIGHT_TCC_EN0(x) ((x) << 0) +#define WEIGHT_TCC_EN0_MASK (0x3f << 0) +#define WEIGHT_TCC_EN0_SHIFT 0 +#define WEIGHT_TCC_EN1(x) ((x) << 6) +#define WEIGHT_TCC_EN1_MASK (0x3f << 6) +#define WEIGHT_TCC_EN1_SHIFT 6 +#define WEIGHT_TCC_EN2(x) ((x) << 12) +#define WEIGHT_TCC_EN2_MASK (0x3f << 12) +#define WEIGHT_TCC_EN2_SHIFT 12 +#define WEIGHT_TCC_EN3(x) ((x) << 18) +#define WEIGHT_TCC_EN3_MASK (0x3f << 18) +#define WEIGHT_TCC_EN3_SHIFT 18 +#define CG_CAC_REGION_2_WEIGHT_0 0x85 +#define WEIGHT_CB_EN0(x) ((x) << 0) +#define WEIGHT_CB_EN0_MASK (0x3f << 0) +#define WEIGHT_CB_EN0_SHIFT 0 +#define WEIGHT_CB_EN1(x) ((x) << 6) +#define WEIGHT_CB_EN1_MASK (0x3f << 6) +#define WEIGHT_CB_EN1_SHIFT 6 +#define WEIGHT_CB_EN2(x) ((x) << 12) +#define WEIGHT_CB_EN2_MASK (0x3f << 12) +#define WEIGHT_CB_EN2_SHIFT 12 +#define WEIGHT_CB_EN3(x) ((x) << 18) +#define WEIGHT_CB_EN3_MASK (0x3f << 18) +#define WEIGHT_CB_EN3_SHIFT 18 +#define CG_CAC_REGION_2_WEIGHT_1 0x86 +#define WEIGHT_DB_SIG0(x) ((x) << 0) +#define WEIGHT_DB_SIG0_MASK (0x3f << 0) +#define WEIGHT_DB_SIG0_SHIFT 0 +#define WEIGHT_DB_SIG1(x) ((x) << 6) +#define WEIGHT_DB_SIG1_MASK (0x3f << 6) +#define WEIGHT_DB_SIG1_SHIFT 6 +#define WEIGHT_DB_SIG2(x) ((x) << 12) +#define WEIGHT_DB_SIG2_MASK (0x3f << 12) +#define WEIGHT_DB_SIG2_SHIFT 12 +#define WEIGHT_DB_SIG3(x) ((x) << 18) +#define WEIGHT_DB_SIG3_MASK (0x3f << 18) +#define WEIGHT_DB_SIG3_SHIFT 18 +#define CG_CAC_REGION_2_WEIGHT_2 0x87 +#define WEIGHT_SXM_SIG0(x) ((x) << 0) +#define WEIGHT_SXM_SIG0_MASK (0x3f << 0) +#define WEIGHT_SXM_SIG0_SHIFT 0 +#define WEIGHT_SXM_SIG1(x) ((x) << 6) +#define WEIGHT_SXM_SIG1_MASK (0x3f << 6) +#define WEIGHT_SXM_SIG1_SHIFT 6 +#define WEIGHT_SXM_SIG2(x) ((x) << 12) +#define WEIGHT_SXM_SIG2_MASK (0x3f << 12) +#define WEIGHT_SXM_SIG2_SHIFT 12 +#define WEIGHT_SXS_SIG0(x) ((x) << 18) +#define WEIGHT_SXS_SIG0_MASK (0x3f << 18) +#define WEIGHT_SXS_SIG0_SHIFT 18 +#define WEIGHT_SXS_SIG1(x) ((x) << 24) +#define WEIGHT_SXS_SIG1_MASK (0x3f << 24) +#define WEIGHT_SXS_SIG1_SHIFT 24 +#define CG_CAC_REGION_3_WEIGHT_0 0x88 +#define WEIGHT_XBR_0(x) ((x) << 0) +#define WEIGHT_XBR_0_MASK (0x3f << 0) +#define WEIGHT_XBR_0_SHIFT 0 +#define WEIGHT_XBR_1(x) ((x) << 6) +#define WEIGHT_XBR_1_MASK (0x3f << 6) +#define WEIGHT_XBR_1_SHIFT 6 +#define WEIGHT_XBR_2(x) ((x) << 12) +#define WEIGHT_XBR_2_MASK (0x3f << 12) +#define WEIGHT_XBR_2_SHIFT 12 +#define WEIGHT_SPI_SIG0(x) ((x) << 18) +#define WEIGHT_SPI_SIG0_MASK (0x3f << 18) +#define WEIGHT_SPI_SIG0_SHIFT 18 +#define CG_CAC_REGION_3_WEIGHT_1 0x89 +#define WEIGHT_SPI_SIG1(x) ((x) << 0) +#define WEIGHT_SPI_SIG1_MASK (0x3f << 0) +#define WEIGHT_SPI_SIG1_SHIFT 0 +#define WEIGHT_SPI_SIG2(x) ((x) << 6) +#define WEIGHT_SPI_SIG2_MASK (0x3f << 6) +#define WEIGHT_SPI_SIG2_SHIFT 6 +#define WEIGHT_SPI_SIG3(x) ((x) << 12) +#define WEIGHT_SPI_SIG3_MASK (0x3f << 12) +#define WEIGHT_SPI_SIG3_SHIFT 12 +#define WEIGHT_SPI_SIG4(x) ((x) << 18) +#define WEIGHT_SPI_SIG4_MASK (0x3f << 18) +#define WEIGHT_SPI_SIG4_SHIFT 18 +#define WEIGHT_SPI_SIG5(x) ((x) << 24) +#define WEIGHT_SPI_SIG5_MASK (0x3f << 24) +#define WEIGHT_SPI_SIG5_SHIFT 24 +#define CG_CAC_REGION_4_WEIGHT_0 0x8a +#define WEIGHT_LDS_SIG0(x) ((x) << 0) +#define WEIGHT_LDS_SIG0_MASK (0x3f << 0) +#define WEIGHT_LDS_SIG0_SHIFT 0 +#define WEIGHT_LDS_SIG1(x) ((x) << 6) +#define WEIGHT_LDS_SIG1_MASK (0x3f << 6) +#define WEIGHT_LDS_SIG1_SHIFT 6 +#define WEIGHT_SC(x) ((x) << 24) +#define WEIGHT_SC_MASK (0x3f << 24) +#define WEIGHT_SC_SHIFT 24 +#define CG_CAC_REGION_4_WEIGHT_1 0x8b +#define WEIGHT_BIF(x) ((x) << 0) +#define WEIGHT_BIF_MASK (0x3f << 0) +#define WEIGHT_BIF_SHIFT 0 +#define WEIGHT_CP(x) ((x) << 6) +#define WEIGHT_CP_MASK (0x3f << 6) +#define WEIGHT_CP_SHIFT 6 +#define WEIGHT_PA_SIG0(x) ((x) << 12) +#define WEIGHT_PA_SIG0_MASK (0x3f << 12) +#define WEIGHT_PA_SIG0_SHIFT 12 +#define WEIGHT_PA_SIG1(x) ((x) << 18) +#define WEIGHT_PA_SIG1_MASK (0x3f << 18) +#define WEIGHT_PA_SIG1_SHIFT 18 +#define WEIGHT_VGT_SIG0(x) ((x) << 24) +#define WEIGHT_VGT_SIG0_MASK (0x3f << 24) +#define WEIGHT_VGT_SIG0_SHIFT 24 +#define CG_CAC_REGION_4_WEIGHT_2 0x8c +#define WEIGHT_VGT_SIG1(x) ((x) << 0) +#define WEIGHT_VGT_SIG1_MASK (0x3f << 0) +#define WEIGHT_VGT_SIG1_SHIFT 0 +#define WEIGHT_VGT_SIG2(x) ((x) << 6) +#define WEIGHT_VGT_SIG2_MASK (0x3f << 6) +#define WEIGHT_VGT_SIG2_SHIFT 6 +#define WEIGHT_DC_SIG0(x) ((x) << 12) +#define WEIGHT_DC_SIG0_MASK (0x3f << 12) +#define WEIGHT_DC_SIG0_SHIFT 12 +#define WEIGHT_DC_SIG1(x) ((x) << 18) +#define WEIGHT_DC_SIG1_MASK (0x3f << 18) +#define WEIGHT_DC_SIG1_SHIFT 18 +#define WEIGHT_DC_SIG2(x) ((x) << 24) +#define WEIGHT_DC_SIG2_MASK (0x3f << 24) +#define WEIGHT_DC_SIG2_SHIFT 24 +#define CG_CAC_REGION_4_WEIGHT_3 0x8d +#define WEIGHT_DC_SIG3(x) ((x) << 0) +#define WEIGHT_DC_SIG3_MASK (0x3f << 0) +#define WEIGHT_DC_SIG3_SHIFT 0 +#define WEIGHT_UVD_SIG0(x) ((x) << 6) +#define WEIGHT_UVD_SIG0_MASK (0x3f << 6) +#define WEIGHT_UVD_SIG0_SHIFT 6 +#define WEIGHT_UVD_SIG1(x) ((x) << 12) +#define WEIGHT_UVD_SIG1_MASK (0x3f << 12) +#define WEIGHT_UVD_SIG1_SHIFT 12 +#define WEIGHT_SPARE0(x) ((x) << 18) +#define WEIGHT_SPARE0_MASK (0x3f << 18) +#define WEIGHT_SPARE0_SHIFT 18 +#define WEIGHT_SPARE1(x) ((x) << 24) +#define WEIGHT_SPARE1_MASK (0x3f << 24) +#define WEIGHT_SPARE1_SHIFT 24 +#define CG_CAC_REGION_5_WEIGHT_0 0x8e +#define WEIGHT_SQ_VSP(x) ((x) << 0) +#define WEIGHT_SQ_VSP_MASK (0x3fff << 0) +#define WEIGHT_SQ_VSP_SHIFT 0 +#define WEIGHT_SQ_VSP0(x) ((x) << 14) +#define WEIGHT_SQ_VSP0_MASK (0x3fff << 14) +#define WEIGHT_SQ_VSP0_SHIFT 14 +#define CG_CAC_REGION_4_OVERRIDE_4 0xab +#define OVR_MODE_SPARE_0(x) ((x) << 16) +#define OVR_MODE_SPARE_0_MASK (0x1 << 16) +#define OVR_MODE_SPARE_0_SHIFT 16 +#define OVR_VAL_SPARE_0(x) ((x) << 17) +#define OVR_VAL_SPARE_0_MASK (0x1 << 17) +#define OVR_VAL_SPARE_0_SHIFT 17 +#define OVR_MODE_SPARE_1(x) ((x) << 18) +#define OVR_MODE_SPARE_1_MASK (0x3f << 18) +#define OVR_MODE_SPARE_1_SHIFT 18 +#define OVR_VAL_SPARE_1(x) ((x) << 19) +#define OVR_VAL_SPARE_1_MASK (0x3f << 19) +#define OVR_VAL_SPARE_1_SHIFT 19 +#define CG_CAC_REGION_5_WEIGHT_1 0xb7 +#define WEIGHT_SQ_GPR(x) ((x) << 0) +#define WEIGHT_SQ_GPR_MASK (0x3fff << 0) +#define WEIGHT_SQ_GPR_SHIFT 0 +#define WEIGHT_SQ_LDS(x) ((x) << 14) +#define WEIGHT_SQ_LDS_MASK (0x3fff << 14) +#define WEIGHT_SQ_LDS_SHIFT 14
+/* PCIE link stuff */ +#define PCIE_LC_TRAINING_CNTL 0xa1 /* PCIE_P */ +#define PCIE_LC_LINK_WIDTH_CNTL 0xa2 /* PCIE_P */ +# define LC_LINK_WIDTH_SHIFT 0 +# define LC_LINK_WIDTH_MASK 0x7 +# define LC_LINK_WIDTH_X0 0 +# define LC_LINK_WIDTH_X1 1 +# define LC_LINK_WIDTH_X2 2 +# define LC_LINK_WIDTH_X4 3 +# define LC_LINK_WIDTH_X8 4 +# define LC_LINK_WIDTH_X16 6 +# define LC_LINK_WIDTH_RD_SHIFT 4 +# define LC_LINK_WIDTH_RD_MASK 0x70 +# define LC_RECONFIG_ARC_MISSING_ESCAPE (1 << 7) +# define LC_RECONFIG_NOW (1 << 8) +# define LC_RENEGOTIATION_SUPPORT (1 << 9) +# define LC_RENEGOTIATE_EN (1 << 10) +# define LC_SHORT_RECONFIG_EN (1 << 11) +# define LC_UPCONFIGURE_SUPPORT (1 << 12) +# define LC_UPCONFIGURE_DIS (1 << 13) +#define PCIE_LC_SPEED_CNTL 0xa4 /* PCIE_P */ +# define LC_GEN2_EN_STRAP (1 << 0) +# define LC_TARGET_LINK_SPEED_OVERRIDE_EN (1 << 1) +# define LC_FORCE_EN_HW_SPEED_CHANGE (1 << 5) +# define LC_FORCE_DIS_HW_SPEED_CHANGE (1 << 6) +# define LC_SPEED_CHANGE_ATTEMPTS_ALLOWED_MASK (0x3 << 8) +# define LC_SPEED_CHANGE_ATTEMPTS_ALLOWED_SHIFT 3 +# define LC_CURRENT_DATA_RATE (1 << 11) +# define LC_HW_VOLTAGE_IF_CONTROL(x) ((x) << 12) +# define LC_HW_VOLTAGE_IF_CONTROL_MASK (3 << 12) +# define LC_HW_VOLTAGE_IF_CONTROL_SHIFT 12 +# define LC_VOLTAGE_TIMER_SEL_MASK (0xf << 14) +# define LC_CLR_FAILED_SPD_CHANGE_CNT (1 << 21) +# define LC_OTHER_SIDE_EVER_SENT_GEN2 (1 << 23) +# define LC_OTHER_SIDE_SUPPORTS_GEN2 (1 << 24) +#define MM_CFGREGS_CNTL 0x544c +# define MM_WR_TO_CFG_EN (1 << 3) +#define LINK_CNTL2 0x88 /* F0 */ +# define TARGET_LINK_SPEED_MASK (0xf << 0) +# define SELECTABLE_DEEMPHASIS (1 << 6)
/*
- UVD
*/ diff --git a/drivers/gpu/drm/radeon/nislands_smc.h b/drivers/gpu/drm/radeon/nislands_smc.h new file mode 100644 index 0000000..3cf8fc0 --- /dev/null +++ b/drivers/gpu/drm/radeon/nislands_smc.h @@ -0,0 +1,329 @@ +/*
- Copyright 2012 Advanced Micro Devices, Inc.
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- OTHER DEALINGS IN THE SOFTWARE.
- */
+#ifndef __NISLANDS_SMC_H__ +#define __NISLANDS_SMC_H__
+#pragma pack(push, 1)
+#define NISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE 16
+struct PP_NIslands_Dpm2PerfLevel +{
- uint8_t MaxPS;
- uint8_t TgtAct;
- uint8_t MaxPS_StepInc;
- uint8_t MaxPS_StepDec;
- uint8_t PSST;
- uint8_t NearTDPDec;
- uint8_t AboveSafeInc;
- uint8_t BelowSafeInc;
- uint8_t PSDeltaLimit;
- uint8_t PSDeltaWin;
- uint8_t Reserved[6];
+};
+typedef struct PP_NIslands_Dpm2PerfLevel PP_NIslands_Dpm2PerfLevel;
+struct PP_NIslands_DPM2Parameters +{
- uint32_t TDPLimit;
- uint32_t NearTDPLimit;
- uint32_t SafePowerLimit;
- uint32_t PowerBoostLimit;
+}; +typedef struct PP_NIslands_DPM2Parameters PP_NIslands_DPM2Parameters;
+struct NISLANDS_SMC_SCLK_VALUE +{
- uint32_t vCG_SPLL_FUNC_CNTL;
- uint32_t vCG_SPLL_FUNC_CNTL_2;
- uint32_t vCG_SPLL_FUNC_CNTL_3;
- uint32_t vCG_SPLL_FUNC_CNTL_4;
- uint32_t vCG_SPLL_SPREAD_SPECTRUM;
- uint32_t vCG_SPLL_SPREAD_SPECTRUM_2;
- uint32_t sclk_value;
+};
+typedef struct NISLANDS_SMC_SCLK_VALUE NISLANDS_SMC_SCLK_VALUE;
+struct NISLANDS_SMC_MCLK_VALUE +{
- uint32_t vMPLL_FUNC_CNTL;
- uint32_t vMPLL_FUNC_CNTL_1;
- uint32_t vMPLL_FUNC_CNTL_2;
- uint32_t vMPLL_AD_FUNC_CNTL;
- uint32_t vMPLL_AD_FUNC_CNTL_2;
- uint32_t vMPLL_DQ_FUNC_CNTL;
- uint32_t vMPLL_DQ_FUNC_CNTL_2;
- uint32_t vMCLK_PWRMGT_CNTL;
- uint32_t vDLL_CNTL;
- uint32_t vMPLL_SS;
- uint32_t vMPLL_SS2;
- uint32_t mclk_value;
+};
+typedef struct NISLANDS_SMC_MCLK_VALUE NISLANDS_SMC_MCLK_VALUE;
+struct NISLANDS_SMC_VOLTAGE_VALUE +{
- uint16_t value;
- uint8_t index;
- uint8_t padding;
+};
+typedef struct NISLANDS_SMC_VOLTAGE_VALUE NISLANDS_SMC_VOLTAGE_VALUE;
+struct NISLANDS_SMC_HW_PERFORMANCE_LEVEL +{
- uint8_t arbValue;
- uint8_t ACIndex;
- uint8_t displayWatermark;
- uint8_t gen2PCIE;
- uint8_t reserved1;
- uint8_t reserved2;
- uint8_t strobeMode;
- uint8_t mcFlags;
- uint32_t aT;
- uint32_t bSP;
- NISLANDS_SMC_SCLK_VALUE sclk;
- NISLANDS_SMC_MCLK_VALUE mclk;
- NISLANDS_SMC_VOLTAGE_VALUE vddc;
- NISLANDS_SMC_VOLTAGE_VALUE mvdd;
- NISLANDS_SMC_VOLTAGE_VALUE vddci;
- NISLANDS_SMC_VOLTAGE_VALUE std_vddc;
- uint32_t powergate_en;
- uint8_t hUp;
- uint8_t hDown;
- uint8_t stateFlags;
- uint8_t arbRefreshState;
- uint32_t SQPowerThrottle;
- uint32_t SQPowerThrottle_2;
- uint32_t reserved[2];
- PP_NIslands_Dpm2PerfLevel dpm2;
+};
+#define NISLANDS_SMC_STROBE_RATIO 0x0F +#define NISLANDS_SMC_STROBE_ENABLE 0x10
+#define NISLANDS_SMC_MC_EDC_RD_FLAG 0x01 +#define NISLANDS_SMC_MC_EDC_WR_FLAG 0x02 +#define NISLANDS_SMC_MC_RTT_ENABLE 0x04 +#define NISLANDS_SMC_MC_STUTTER_EN 0x08
+typedef struct NISLANDS_SMC_HW_PERFORMANCE_LEVEL NISLANDS_SMC_HW_PERFORMANCE_LEVEL;
+struct NISLANDS_SMC_SWSTATE +{
- uint8_t flags;
- uint8_t levelCount;
- uint8_t padding2;
- uint8_t padding3;
- NISLANDS_SMC_HW_PERFORMANCE_LEVEL levels[1];
+};
+typedef struct NISLANDS_SMC_SWSTATE NISLANDS_SMC_SWSTATE;
+#define NISLANDS_SMC_VOLTAGEMASK_VDDC 0 +#define NISLANDS_SMC_VOLTAGEMASK_MVDD 1 +#define NISLANDS_SMC_VOLTAGEMASK_VDDCI 2 +#define NISLANDS_SMC_VOLTAGEMASK_MAX 4
+struct NISLANDS_SMC_VOLTAGEMASKTABLE +{
- uint8_t highMask[NISLANDS_SMC_VOLTAGEMASK_MAX];
- uint32_t lowMask[NISLANDS_SMC_VOLTAGEMASK_MAX];
+};
+typedef struct NISLANDS_SMC_VOLTAGEMASKTABLE NISLANDS_SMC_VOLTAGEMASKTABLE;
+#define NISLANDS_MAX_NO_VREG_STEPS 32
+struct NISLANDS_SMC_STATETABLE +{
- uint8_t thermalProtectType;
- uint8_t systemFlags;
- uint8_t maxVDDCIndexInPPTable;
- uint8_t extraFlags;
- uint8_t highSMIO[NISLANDS_MAX_NO_VREG_STEPS];
- uint32_t lowSMIO[NISLANDS_MAX_NO_VREG_STEPS];
- NISLANDS_SMC_VOLTAGEMASKTABLE voltageMaskTable;
- PP_NIslands_DPM2Parameters dpm2Params;
- NISLANDS_SMC_SWSTATE initialState;
- NISLANDS_SMC_SWSTATE ACPIState;
- NISLANDS_SMC_SWSTATE ULVState;
- NISLANDS_SMC_SWSTATE driverState;
- NISLANDS_SMC_HW_PERFORMANCE_LEVEL dpmLevels[NISLANDS_MAX_SMC_PERFORMANCE_LEVELS_PER_SWSTATE - 1];
+};
+typedef struct NISLANDS_SMC_STATETABLE NISLANDS_SMC_STATETABLE;
+#define NI_SMC_SOFT_REGISTERS_START 0x108
+#define NI_SMC_SOFT_REGISTER_mclk_chg_timeout 0x0 +#define NI_SMC_SOFT_REGISTER_delay_bbias 0xC +#define NI_SMC_SOFT_REGISTER_delay_vreg 0x10 +#define NI_SMC_SOFT_REGISTER_delay_acpi 0x2C +#define NI_SMC_SOFT_REGISTER_seq_index 0x64 +#define NI_SMC_SOFT_REGISTER_mvdd_chg_time 0x68 +#define NI_SMC_SOFT_REGISTER_mclk_switch_lim 0x78 +#define NI_SMC_SOFT_REGISTER_watermark_threshold 0x80 +#define NI_SMC_SOFT_REGISTER_mc_block_delay 0x84 +#define NI_SMC_SOFT_REGISTER_uvd_enabled 0x98
+#define SMC_NISLANDS_MC_TPP_CAC_NUM_OF_ENTRIES 16 +#define SMC_NISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES 16 +#define SMC_NISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES 16 +#define SMC_NISLANDS_BIF_LUT_NUM_OF_ENTRIES 4
+struct SMC_NISLANDS_MC_TPP_CAC_TABLE +{
- uint32_t tpp[SMC_NISLANDS_MC_TPP_CAC_NUM_OF_ENTRIES];
- uint32_t cacValue[SMC_NISLANDS_MC_TPP_CAC_NUM_OF_ENTRIES];
+};
+typedef struct SMC_NISLANDS_MC_TPP_CAC_TABLE SMC_NISLANDS_MC_TPP_CAC_TABLE;
+struct PP_NIslands_CACTABLES +{
- uint32_t cac_bif_lut[SMC_NISLANDS_BIF_LUT_NUM_OF_ENTRIES];
- uint32_t cac_lkge_lut[SMC_NISLANDS_LKGE_LUT_NUM_OF_TEMP_ENTRIES][SMC_NISLANDS_LKGE_LUT_NUM_OF_VOLT_ENTRIES];
- uint32_t pwr_const;
- uint32_t dc_cacValue;
- uint32_t bif_cacValue;
- uint32_t lkge_pwr;
- uint8_t cac_width;
- uint8_t window_size_p2;
- uint8_t num_drop_lsb;
- uint8_t padding_0;
- uint32_t last_power;
- uint8_t AllowOvrflw;
- uint8_t MCWrWeight;
- uint8_t MCRdWeight;
- uint8_t padding_1[9];
- uint8_t enableWinAvg;
- uint8_t numWin_TDP;
- uint8_t l2numWin_TDP;
- uint8_t WinIndex;
- uint32_t dynPwr_TDP[4];
- uint32_t lkgePwr_TDP[4];
- uint32_t power_TDP[4];
- uint32_t avg_dynPwr_TDP;
- uint32_t avg_lkgePwr_TDP;
- uint32_t avg_power_TDP;
- uint32_t lts_power_TDP;
- uint8_t lts_truncate_n;
- uint8_t padding_2[7];
+};
+typedef struct PP_NIslands_CACTABLES PP_NIslands_CACTABLES;
+#define SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE 32 +#define SMC_NISLANDS_MC_REGISTER_ARRAY_SET_COUNT 20
+struct SMC_NIslands_MCRegisterAddress +{
- uint16_t s0;
- uint16_t s1;
+};
+typedef struct SMC_NIslands_MCRegisterAddress SMC_NIslands_MCRegisterAddress;
+struct SMC_NIslands_MCRegisterSet +{
- uint32_t value[SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE];
+};
+typedef struct SMC_NIslands_MCRegisterSet SMC_NIslands_MCRegisterSet;
+struct SMC_NIslands_MCRegisters +{
- uint8_t last;
- uint8_t reserved[3];
- SMC_NIslands_MCRegisterAddress address[SMC_NISLANDS_MC_REGISTER_ARRAY_SIZE];
- SMC_NIslands_MCRegisterSet data[SMC_NISLANDS_MC_REGISTER_ARRAY_SET_COUNT];
+};
+typedef struct SMC_NIslands_MCRegisters SMC_NIslands_MCRegisters;
+struct SMC_NIslands_MCArbDramTimingRegisterSet +{
- uint32_t mc_arb_dram_timing;
- uint32_t mc_arb_dram_timing2;
- uint8_t mc_arb_rfsh_rate;
- uint8_t padding[3];
+};
+typedef struct SMC_NIslands_MCArbDramTimingRegisterSet SMC_NIslands_MCArbDramTimingRegisterSet;
+struct SMC_NIslands_MCArbDramTimingRegisters +{
- uint8_t arb_current;
- uint8_t reserved[3];
- SMC_NIslands_MCArbDramTimingRegisterSet data[20];
+};
+typedef struct SMC_NIslands_MCArbDramTimingRegisters SMC_NIslands_MCArbDramTimingRegisters;
+struct SMC_NISLANDS_SPLL_DIV_TABLE +{
- uint32_t freq[256];
- uint32_t ss[256];
+};
+#define SMC_NISLANDS_SPLL_DIV_TABLE_FBDIV_MASK 0x01ffffff +#define SMC_NISLANDS_SPLL_DIV_TABLE_FBDIV_SHIFT 0 +#define SMC_NISLANDS_SPLL_DIV_TABLE_PDIV_MASK 0xfe000000 +#define SMC_NISLANDS_SPLL_DIV_TABLE_PDIV_SHIFT 25 +#define SMC_NISLANDS_SPLL_DIV_TABLE_CLKV_MASK 0x000fffff +#define SMC_NISLANDS_SPLL_DIV_TABLE_CLKV_SHIFT 0 +#define SMC_NISLANDS_SPLL_DIV_TABLE_CLKS_MASK 0xfff00000 +#define SMC_NISLANDS_SPLL_DIV_TABLE_CLKS_SHIFT 20
+typedef struct SMC_NISLANDS_SPLL_DIV_TABLE SMC_NISLANDS_SPLL_DIV_TABLE;
+#define NISLANDS_SMC_FIRMWARE_HEADER_LOCATION 0x100
+#define NISLANDS_SMC_FIRMWARE_HEADER_version 0x0 +#define NISLANDS_SMC_FIRMWARE_HEADER_flags 0x4 +#define NISLANDS_SMC_FIRMWARE_HEADER_softRegisters 0x8 +#define NISLANDS_SMC_FIRMWARE_HEADER_stateTable 0xC +#define NISLANDS_SMC_FIRMWARE_HEADER_fanTable 0x10 +#define NISLANDS_SMC_FIRMWARE_HEADER_cacTable 0x14 +#define NISLANDS_SMC_FIRMWARE_HEADER_mcRegisterTable 0x20 +#define NISLANDS_SMC_FIRMWARE_HEADER_mcArbDramAutoRefreshTable 0x2C +#define NISLANDS_SMC_FIRMWARE_HEADER_spllTable 0x30
+#pragma pack(pop)
+#endif
diff --git a/drivers/gpu/drm/radeon/ppsmc.h b/drivers/gpu/drm/radeon/ppsmc.h index 3d0786f..607982a 100644 --- a/drivers/gpu/drm/radeon/ppsmc.h +++ b/drivers/gpu/drm/radeon/ppsmc.h @@ -46,6 +46,7 @@ #define PPSMC_DISPLAY_WATERMARK_HIGH 1
#define PPSMC_STATEFLAG_AUTO_PULSE_SKIP 0x01 +#define PPSMC_STATEFLAG_POWERBOOST 0x02
#define PPSMC_Result_OK ((uint8_t)0x01) #define PPSMC_Result_Failed ((uint8_t)0xFF) @@ -56,17 +57,28 @@ typedef uint8_t PPSMC_Result; #define PPSMC_MSG_Resume ((uint8_t)0x11) #define PPSMC_MSG_TwoLevelsDisabled ((uint8_t)0x15) #define PPSMC_MSG_EnableThermalInterrupt ((uint8_t)0x16) +#define PPSMC_MSG_RunningOnAC ((uint8_t)0x17) #define PPSMC_MSG_SwitchToSwState ((uint8_t)0x20) #define PPSMC_MSG_SwitchToInitialState ((uint8_t)0x40) #define PPSMC_MSG_NoForcedLevel ((uint8_t)0x41) #define PPSMC_MSG_SwitchToMinimumPower ((uint8_t)0x51) #define PPSMC_MSG_ResumeFromMinimumPower ((uint8_t)0x52) +#define PPSMC_MSG_EnableCac ((uint8_t)0x53) +#define PPSMC_MSG_DisableCac ((uint8_t)0x54) +#define PPSMC_TDPClampingActive ((uint8_t)0x59) +#define PPSMC_TDPClampingInactive ((uint8_t)0x5A) #define PPSMC_MSG_NoDisplay ((uint8_t)0x5D) #define PPSMC_MSG_HasDisplay ((uint8_t)0x5E) +#define PPSMC_MSG_UVDPowerOFF ((uint8_t)0x60) +#define PPSMC_MSG_UVDPowerON ((uint8_t)0x61) #define PPSMC_MSG_EnableULV ((uint8_t)0x62) #define PPSMC_MSG_DisableULV ((uint8_t)0x63) #define PPSMC_MSG_EnterULV ((uint8_t)0x64) #define PPSMC_MSG_ExitULV ((uint8_t)0x65) +#define PPSMC_CACLongTermAvgEnable ((uint8_t)0x6E) +#define PPSMC_CACLongTermAvgDisable ((uint8_t)0x6F) +#define PPSMC_MSG_CollectCAC_PowerCorreln ((uint8_t)0x7A) +#define PPSMC_MSG_SetEnabledLevels ((uint8_t)0x82) #define PPSMC_MSG_ResetToDefaults ((uint8_t)0x84)
/* TN */ diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index 5dcbb28..e6703ef 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1804,6 +1804,18 @@ static struct radeon_asic cayman_asic = { .set_uvd_clocks = &evergreen_set_uvd_clocks, .get_temperature = &evergreen_get_temp, },
- .dpm = {
.init = &ni_dpm_init,
.setup_asic = &ni_dpm_setup_asic,
.enable = &ni_dpm_enable,
.disable = &ni_dpm_disable,
.set_power_state = &ni_dpm_set_power_state,
.display_configuration_changed = &cypress_dpm_display_configuration_changed,
.fini = &ni_dpm_fini,
.get_sclk = &ni_dpm_get_sclk,
.get_mclk = &ni_dpm_get_mclk,
.print_power_state = &ni_dpm_print_power_state,
- }, .pflip = { .pre_page_flip = &evergreen_pre_page_flip, .page_flip = &evergreen_page_flip,
diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 4d3f93d..2fef64d 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -581,6 +581,16 @@ bool cayman_gfx_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring); bool cayman_dma_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring); void cayman_dma_vm_flush(struct radeon_device *rdev, int ridx, struct radeon_vm *vm);
+int ni_dpm_init(struct radeon_device *rdev); +void ni_dpm_setup_asic(struct radeon_device *rdev); +int ni_dpm_enable(struct radeon_device *rdev); +void ni_dpm_disable(struct radeon_device *rdev); +int ni_dpm_set_power_state(struct radeon_device *rdev); +void ni_dpm_fini(struct radeon_device *rdev); +u32 ni_dpm_get_sclk(struct radeon_device *rdev, bool low); +u32 ni_dpm_get_mclk(struct radeon_device *rdev, bool low); +void ni_dpm_print_power_state(struct radeon_device *rdev,
struct radeon_ps *ps);
int trinity_dpm_init(struct radeon_device *rdev); int trinity_dpm_enable(struct radeon_device *rdev); void trinity_dpm_disable(struct radeon_device *rdev); diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 2eb41be..01ff18b 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -1100,6 +1100,7 @@ int radeon_pm_init(struct radeon_device *rdev) case CHIP_BARTS: case CHIP_TURKS: case CHIP_CAICOS:
- case CHIP_CAYMAN: case CHIP_ARUBA: if (radeon_dpm == 1) rdev->pm.pm_method = PM_METHOD_DPM;
diff --git a/drivers/gpu/drm/radeon/radeon_ucode.h b/drivers/gpu/drm/radeon/radeon_ucode.h index e592e27..51beb4c 100644 --- a/drivers/gpu/drm/radeon/radeon_ucode.h +++ b/drivers/gpu/drm/radeon/radeon_ucode.h @@ -100,4 +100,9 @@ #define CAICOS_SMC_INT_VECTOR_START 0xffc0 #define CAICOS_SMC_INT_VECTOR_SIZE 0x0040
+#define CAYMAN_SMC_UCODE_START 0x0100 +#define CAYMAN_SMC_UCODE_SIZE 0x79ec +#define CAYMAN_SMC_INT_VECTOR_START 0xffc0 +#define CAYMAN_SMC_INT_VECTOR_SIZE 0x0040
#endif diff --git a/drivers/gpu/drm/radeon/rv770_smc.c b/drivers/gpu/drm/radeon/rv770_smc.c index 0078c59..ab95da5 100644 --- a/drivers/gpu/drm/radeon/rv770_smc.c +++ b/drivers/gpu/drm/radeon/rv770_smc.c @@ -254,6 +254,26 @@ static const u8 caicos_smc_int_vectors[] = 0x05, 0x0A, 0x05, 0x0A };
+static const u8 cayman_smc_int_vectors[] = +{
- 0x12, 0x05, 0x12, 0x05,
- 0x12, 0x05, 0x12, 0x05,
- 0x12, 0x05, 0x12, 0x05,
- 0x12, 0x05, 0x12, 0x05,
- 0x12, 0x05, 0x12, 0x05,
- 0x12, 0x05, 0x12, 0x05,
- 0x12, 0x05, 0x12, 0x05,
- 0x12, 0x05, 0x12, 0x05,
- 0x12, 0x05, 0x12, 0x05,
- 0x12, 0x05, 0x12, 0x05,
- 0x12, 0x05, 0x12, 0x05,
- 0x12, 0x05, 0x12, 0x05,
- 0x12, 0x05, 0x18, 0xEA,
- 0x12, 0x20, 0x1C, 0x34,
- 0x1C, 0x34, 0x08, 0x72,
- 0x08, 0x72, 0x08, 0x72
+};
int rv770_set_smc_sram_address(struct radeon_device *rdev, u16 smc_address, u16 limit) { @@ -544,6 +564,13 @@ int rv770_load_smc_ucode(struct radeon_device *rdev, int_vect_start_address = CAICOS_SMC_INT_VECTOR_START; int_vect_size = CAICOS_SMC_INT_VECTOR_SIZE; break;
- case CHIP_CAYMAN:
ucode_start_address = CAYMAN_SMC_UCODE_START;
ucode_size = CAYMAN_SMC_UCODE_SIZE;
int_vect = (const u8 *)&cayman_smc_int_vectors;
int_vect_start_address = CAYMAN_SMC_INT_VECTOR_START;
int_vect_size = CAYMAN_SMC_INT_VECTOR_SIZE;
default: DRM_ERROR("unknown asic in smc ucode loader\n"); BUG();break;
-- 1.7.7.5
dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
From: Alex Deucher alexander.deucher@amd.com
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/ni_dpm.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/ni_dpm.c b/drivers/gpu/drm/radeon/ni_dpm.c index 635bf04..44016f2 100644 --- a/drivers/gpu/drm/radeon/ni_dpm.c +++ b/drivers/gpu/drm/radeon/ni_dpm.c @@ -3644,6 +3644,7 @@ int ni_dpm_set_power_state(struct radeon_device *rdev) ret = ni_program_memory_timing_parameters(rdev); if (ret) return ret; + ni_populate_smc_tdp_limits(rdev); rv770_resume_smc(rdev); rv770_set_sw_state(rdev); ni_enable_smc_cac(rdev, true);
From: Alex Deucher alexander.deucher@amd.com
Needed to properly handle dynamic state adjustment.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/rs780_dpm.c | 52 ++++++++++++++++++++++-------------- 1 files changed, 32 insertions(+), 20 deletions(-)
diff --git a/drivers/gpu/drm/radeon/rs780_dpm.c b/drivers/gpu/drm/radeon/rs780_dpm.c index a1497a6..8af1a04 100644 --- a/drivers/gpu/drm/radeon/rs780_dpm.c +++ b/drivers/gpu/drm/radeon/rs780_dpm.c @@ -71,10 +71,11 @@ static void rs780_get_pm_mode_parameters(struct radeon_device *rdev)
static void rs780_voltage_scaling_enable(struct radeon_device *rdev, bool enable);
-static int rs780_initialize_dpm_power_state(struct radeon_device *rdev) +static int rs780_initialize_dpm_power_state(struct radeon_device *rdev, + struct radeon_ps *boot_ps) { struct atom_clock_dividers dividers; - struct igp_ps *default_state = rs780_get_ps(rdev->pm.dpm.boot_ps); + struct igp_ps *default_state = rs780_get_ps(boot_ps); int i, ret;
ret = radeon_atom_get_clock_dividers(rdev, COMPUTE_ENGINE_PLL_PARAM, @@ -104,7 +105,8 @@ static int rs780_initialize_dpm_power_state(struct radeon_device *rdev) return 0; }
-static int rs780_initialize_dpm_parameters(struct radeon_device *rdev) +static int rs780_initialize_dpm_parameters(struct radeon_device *rdev, + struct radeon_ps *boot_ps) { int ret = 0; int i; @@ -140,7 +142,7 @@ static int rs780_initialize_dpm_parameters(struct radeon_device *rdev) r600_vid_rt_set_vrt(rdev, R600_VOLTAGERESPONSETIME_DFLT); r600_vid_rt_set_ssu(rdev, R600_SPLLSTEPUNIT_DFLT);
- ret = rs780_initialize_dpm_power_state(rdev); + ret = rs780_initialize_dpm_power_state(rdev, boot_ps);
r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_LOW, 0); r600_power_level_set_voltage_index(rdev, R600_POWER_LEVEL_MEDIUM, 0); @@ -401,11 +403,13 @@ static void rs780_force_voltage_to_high(struct radeon_device *rdev) WREG32_P(GFX_MACRO_BYPASS_CNTL, 0, ~SPLL_BYPASS_CNTL); }
-static int rs780_set_engine_clock_scaling(struct radeon_device *rdev) +static int rs780_set_engine_clock_scaling(struct radeon_device *rdev, + struct radeon_ps *new_ps, + struct radeon_ps *old_ps) { struct atom_clock_dividers min_dividers, max_dividers, current_max_dividers; - struct igp_ps *new_state = rs780_get_ps(rdev->pm.dpm.requested_ps); - struct igp_ps *old_state = rs780_get_ps(rdev->pm.dpm.current_ps); + struct igp_ps *new_state = rs780_get_ps(new_ps); + struct igp_ps *old_state = rs780_get_ps(old_ps); int ret;
if ((new_state->sclk_high == old_state->sclk_high) && @@ -451,10 +455,12 @@ static int rs780_set_engine_clock_scaling(struct radeon_device *rdev) return 0; }
-static void rs780_set_engine_clock_spc(struct radeon_device *rdev) +static void rs780_set_engine_clock_spc(struct radeon_device *rdev, + struct radeon_ps *new_ps, + struct radeon_ps *old_ps) { - struct igp_ps *new_state = rs780_get_ps(rdev->pm.dpm.requested_ps); - struct igp_ps *old_state = rs780_get_ps(rdev->pm.dpm.current_ps); + struct igp_ps *new_state = rs780_get_ps(new_ps); + struct igp_ps *old_state = rs780_get_ps(old_ps); struct igp_power_info *pi = rs780_get_pi(rdev);
if ((new_state->sclk_high == old_state->sclk_high) && @@ -468,10 +474,12 @@ static void rs780_set_engine_clock_spc(struct radeon_device *rdev)
}
-static void rs780_activate_engine_clk_scaling(struct radeon_device *rdev) +static void rs780_activate_engine_clk_scaling(struct radeon_device *rdev, + struct radeon_ps *new_ps, + struct radeon_ps *old_ps) { - struct igp_ps *new_state = rs780_get_ps(rdev->pm.dpm.requested_ps); - struct igp_ps *old_state = rs780_get_ps(rdev->pm.dpm.current_ps); + struct igp_ps *new_state = rs780_get_ps(new_ps); + struct igp_ps *old_state = rs780_get_ps(old_ps);
if ((new_state->sclk_high == old_state->sclk_high) && (new_state->sclk_low == old_state->sclk_low)) @@ -493,9 +501,10 @@ static u32 rs780_get_voltage_for_vddc_level(struct radeon_device *rdev, return pi->max_voltage; }
-static void rs780_enable_voltage_scaling(struct radeon_device *rdev) +static void rs780_enable_voltage_scaling(struct radeon_device *rdev, + struct radeon_ps *new_ps) { - struct igp_ps *new_state = rs780_get_ps(rdev->pm.dpm.requested_ps); + struct igp_ps *new_state = rs780_get_ps(new_ps); struct igp_power_info *pi = rs780_get_pi(rdev); enum rs780_vddc_level vddc_high, vddc_low;
@@ -536,13 +545,14 @@ static void rs780_enable_voltage_scaling(struct radeon_device *rdev) int rs780_dpm_enable(struct radeon_device *rdev) { struct igp_power_info *pi = rs780_get_pi(rdev); + struct radeon_ps *boot_ps = rdev->pm.dpm.boot_ps;
rs780_get_pm_mode_parameters(rdev); rs780_disable_vbios_powersaving(rdev);
if (r600_dynamicpm_enabled(rdev)) return -EINVAL; - if (rs780_initialize_dpm_parameters(rdev)) + if (rs780_initialize_dpm_parameters(rdev, boot_ps)) return -EINVAL; rs780_start_dpm(rdev);
@@ -591,6 +601,8 @@ void rs780_dpm_disable(struct radeon_device *rdev) int rs780_dpm_set_power_state(struct radeon_device *rdev) { struct igp_power_info *pi = rs780_get_pi(rdev); + struct radeon_ps *new_ps = rdev->pm.dpm.requested_ps; + struct radeon_ps *old_ps = rdev->pm.dpm.current_ps;
rs780_get_pm_mode_parameters(rdev);
@@ -599,13 +611,13 @@ int rs780_dpm_set_power_state(struct radeon_device *rdev) mdelay(5); }
- rs780_set_engine_clock_scaling(rdev); - rs780_set_engine_clock_spc(rdev); + rs780_set_engine_clock_scaling(rdev, new_ps, old_ps); + rs780_set_engine_clock_spc(rdev, new_ps, old_ps);
- rs780_activate_engine_clk_scaling(rdev); + rs780_activate_engine_clk_scaling(rdev, new_ps, old_ps);
if (pi->voltage_control) - rs780_enable_voltage_scaling(rdev); + rs780_enable_voltage_scaling(rdev, new_ps);
return 0; }
From: Alex Deucher alexander.deucher@amd.com
Needed to properly handle dynamic state adjustment.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/rv6xx_dpm.c | 115 +++++++++++++++++++++-------------- 1 files changed, 69 insertions(+), 46 deletions(-)
diff --git a/drivers/gpu/drm/radeon/rv6xx_dpm.c b/drivers/gpu/drm/radeon/rv6xx_dpm.c index fa4beb2..e8f07b1 100644 --- a/drivers/gpu/drm/radeon/rv6xx_dpm.c +++ b/drivers/gpu/drm/radeon/rv6xx_dpm.c @@ -961,9 +961,11 @@ static void rv6xx_program_voltage_gpio_pins(struct radeon_device *rdev) rv6xx_get_master_voltage_mask(rdev)); }
-static void rv6xx_enable_static_voltage_control(struct radeon_device *rdev, bool enable) +static void rv6xx_enable_static_voltage_control(struct radeon_device *rdev, + struct radeon_ps *new_ps, + bool enable) { - struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps); + struct rv6xx_ps *new_state = rv6xx_get_ps(new_ps);
if (enable) radeon_atom_set_voltage(rdev, @@ -1039,9 +1041,10 @@ static void rv6xx_calculate_ap(struct radeon_device *rdev,
}
-static void rv6xx_calculate_stepping_parameters(struct radeon_device *rdev) +static void rv6xx_calculate_stepping_parameters(struct radeon_device *rdev, + struct radeon_ps *new_ps) { - struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps); + struct rv6xx_ps *new_state = rv6xx_get_ps(new_ps);
rv6xx_calculate_engine_speed_stepping_parameters(rdev, new_state); rv6xx_calculate_memory_clock_stepping_parameters(rdev, new_state); @@ -1191,10 +1194,12 @@ static void rv6xx_program_display_gap(struct radeon_device *rdev) WREG32(CG_DISPLAY_GAP_CNTL, tmp); }
-static void rv6xx_set_sw_voltage_to_safe(struct radeon_device *rdev) +static void rv6xx_set_sw_voltage_to_safe(struct radeon_device *rdev, + struct radeon_ps *new_ps, + struct radeon_ps *old_ps) { - struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps); - struct rv6xx_ps *old_state = rv6xx_get_ps(rdev->pm.dpm.current_ps); + struct rv6xx_ps *new_state = rv6xx_get_ps(new_ps); + struct rv6xx_ps *old_state = rv6xx_get_ps(old_ps); u16 safe_voltage;
safe_voltage = (new_state->low.vddc >= old_state->low.vddc) ? @@ -1207,9 +1212,10 @@ static void rv6xx_set_sw_voltage_to_safe(struct radeon_device *rdev) ~SW_GPIO_INDEX_MASK); }
-static void rv6xx_set_sw_voltage_to_low(struct radeon_device *rdev) +static void rv6xx_set_sw_voltage_to_low(struct radeon_device *rdev, + struct radeon_ps *old_ps) { - struct rv6xx_ps *old_state = rv6xx_get_ps(rdev->pm.dpm.current_ps); + struct rv6xx_ps *old_state = rv6xx_get_ps(old_ps);
rv6xx_program_voltage_stepping_entry(rdev, R600_POWER_LEVEL_CTXSW, old_state->low.vddc); @@ -1218,10 +1224,12 @@ static void rv6xx_set_sw_voltage_to_low(struct radeon_device *rdev) ~SW_GPIO_INDEX_MASK); }
-static void rv6xx_set_safe_backbias(struct radeon_device *rdev) +static void rv6xx_set_safe_backbias(struct radeon_device *rdev, + struct radeon_ps *new_ps, + struct radeon_ps *old_ps) { - struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps); - struct rv6xx_ps *old_state = rv6xx_get_ps(rdev->pm.dpm.current_ps); + struct rv6xx_ps *new_state = rv6xx_get_ps(new_ps); + struct rv6xx_ps *old_state = rv6xx_get_ps(old_ps);
if ((new_state->low.flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE) && (old_state->low.flags & ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE)) @@ -1230,10 +1238,12 @@ static void rv6xx_set_safe_backbias(struct radeon_device *rdev) WREG32_P(GENERAL_PWRMGT, 0, ~BACKBIAS_VALUE); }
-static void rv6xx_set_safe_pcie_gen2(struct radeon_device *rdev) +static void rv6xx_set_safe_pcie_gen2(struct radeon_device *rdev, + struct radeon_ps *new_ps, + struct radeon_ps *old_ps) { - struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps); - struct rv6xx_ps *old_state = rv6xx_get_ps(rdev->pm.dpm.current_ps); + struct rv6xx_ps *new_state = rv6xx_get_ps(new_ps); + struct rv6xx_ps *old_state = rv6xx_get_ps(old_ps);
if ((new_state->low.flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2) != (old_state->low.flags & ATOM_PPLIB_R600_FLAGS_PCIEGEN2)) @@ -1290,10 +1300,12 @@ static int rv6xx_step_sw_voltage(struct radeon_device *rdev, return 0; }
-static int rv6xx_step_voltage_if_increasing(struct radeon_device *rdev) +static int rv6xx_step_voltage_if_increasing(struct radeon_device *rdev, + struct radeon_ps *new_ps, + struct radeon_ps *old_ps) { - struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps); - struct rv6xx_ps *old_state = rv6xx_get_ps(rdev->pm.dpm.current_ps); + struct rv6xx_ps *new_state = rv6xx_get_ps(new_ps); + struct rv6xx_ps *old_state = rv6xx_get_ps(old_ps);
if (new_state->low.vddc > old_state->low.vddc) return rv6xx_step_sw_voltage(rdev, @@ -1303,10 +1315,12 @@ static int rv6xx_step_voltage_if_increasing(struct radeon_device *rdev) return 0; }
-static int rv6xx_step_voltage_if_decreasing(struct radeon_device *rdev) +static int rv6xx_step_voltage_if_decreasing(struct radeon_device *rdev, + struct radeon_ps *new_ps, + struct radeon_ps *old_ps) { - struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps); - struct rv6xx_ps *old_state = rv6xx_get_ps(rdev->pm.dpm.current_ps); + struct rv6xx_ps *new_state = rv6xx_get_ps(new_ps); + struct rv6xx_ps *old_state = rv6xx_get_ps(old_ps);
if (new_state->low.vddc < old_state->low.vddc) return rv6xx_step_sw_voltage(rdev, @@ -1399,10 +1413,12 @@ static void rv6xx_enable_thermal_protection(struct radeon_device *rdev, r600_enable_thermal_protection(rdev, enable); }
-static void rv6xx_generate_transition_stepping(struct radeon_device *rdev) +static void rv6xx_generate_transition_stepping(struct radeon_device *rdev, + struct radeon_ps *new_ps, + struct radeon_ps *old_ps) { - struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps); - struct rv6xx_ps *old_state = rv6xx_get_ps(rdev->pm.dpm.current_ps); + struct rv6xx_ps *new_state = rv6xx_get_ps(new_ps); + struct rv6xx_ps *old_state = rv6xx_get_ps(old_ps); struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
rv6xx_generate_steps(rdev, @@ -1411,9 +1427,10 @@ static void rv6xx_generate_transition_stepping(struct radeon_device *rdev) 0, &pi->hw.medium_sclk_index); }
-static void rv6xx_generate_low_step(struct radeon_device *rdev) +static void rv6xx_generate_low_step(struct radeon_device *rdev, + struct radeon_ps *new_ps) { - struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps); + struct rv6xx_ps *new_state = rv6xx_get_ps(new_ps); struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
pi->hw.low_sclk_index = 0; @@ -1430,9 +1447,10 @@ static void rv6xx_invalidate_intermediate_steps(struct radeon_device *rdev) pi->hw.medium_sclk_index); }
-static void rv6xx_generate_stepping_table(struct radeon_device *rdev) +static void rv6xx_generate_stepping_table(struct radeon_device *rdev, + struct radeon_ps *new_ps) { - struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps); + struct rv6xx_ps *new_state = rv6xx_get_ps(new_ps); struct rv6xx_power_info *pi = rv6xx_get_pi(rdev);
pi->hw.low_sclk_index = 0; @@ -1472,9 +1490,10 @@ static void rv6xx_reset_lvtm_data_sync(struct radeon_device *rdev) }
static void rv6xx_enable_dynamic_pcie_gen2(struct radeon_device *rdev, + struct radeon_ps *new_ps, bool enable) { - struct rv6xx_ps *new_state = rv6xx_get_ps(rdev->pm.dpm.requested_ps); + struct rv6xx_ps *new_state = rv6xx_get_ps(new_ps);
if (enable) { rv6xx_enable_bif_dynamic_pcie_gen2(rdev, true); @@ -1491,6 +1510,7 @@ static void rv6xx_enable_dynamic_pcie_gen2(struct radeon_device *rdev, int rv6xx_dpm_enable(struct radeon_device *rdev) { struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + struct radeon_ps *boot_ps = rdev->pm.dpm.boot_ps;
if (r600_dynamicpm_enabled(rdev)) return -EINVAL; @@ -1518,12 +1538,12 @@ int rv6xx_dpm_enable(struct radeon_device *rdev)
rv6xx_program_power_level_enter_state(rdev);
- rv6xx_calculate_stepping_parameters(rdev); + rv6xx_calculate_stepping_parameters(rdev, boot_ps);
if (pi->voltage_control) rv6xx_program_voltage_gpio_pins(rdev);
- rv6xx_generate_stepping_table(rdev); + rv6xx_generate_stepping_table(rdev, boot_ps);
rv6xx_program_stepping_parameters_except_lowest_entry(rdev); rv6xx_program_stepping_parameters_lowest_entry(rdev); @@ -1550,10 +1570,10 @@ int rv6xx_dpm_enable(struct radeon_device *rdev) r600_start_dpm(rdev);
if (pi->voltage_control) - rv6xx_enable_static_voltage_control(rdev, false); + rv6xx_enable_static_voltage_control(rdev, boot_ps, false);
if (pi->dynamic_pcie_gen2) - rv6xx_enable_dynamic_pcie_gen2(rdev, true); + rv6xx_enable_dynamic_pcie_gen2(rdev, boot_ps, true);
if (pi->gfx_clock_gating) r600_gfx_clockgating_enable(rdev, true); @@ -1564,6 +1584,7 @@ int rv6xx_dpm_enable(struct radeon_device *rdev) void rv6xx_dpm_disable(struct radeon_device *rdev) { struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + struct radeon_ps *boot_ps = rdev->pm.dpm.boot_ps;
if (!r600_dynamicpm_enabled(rdev)) return; @@ -1587,10 +1608,10 @@ void rv6xx_dpm_disable(struct radeon_device *rdev) rv6xx_enable_spread_spectrum(rdev, false);
if (pi->voltage_control) - rv6xx_enable_static_voltage_control(rdev, true); + rv6xx_enable_static_voltage_control(rdev, boot_ps, true);
if (pi->dynamic_pcie_gen2) - rv6xx_enable_dynamic_pcie_gen2(rdev, false); + rv6xx_enable_dynamic_pcie_gen2(rdev, boot_ps, false);
if (rdev->irq.installed && r600_is_internal_thermal_sensor(rdev->pm.int_thermal_type)) { @@ -1607,6 +1628,8 @@ void rv6xx_dpm_disable(struct radeon_device *rdev) int rv6xx_dpm_set_power_state(struct radeon_device *rdev) { struct rv6xx_power_info *pi = rv6xx_get_pi(rdev); + struct radeon_ps *new_ps = rdev->pm.dpm.requested_ps; + struct radeon_ps *old_ps = rdev->pm.dpm.current_ps;
rv6xx_clear_vc(rdev); r600_power_level_enable(rdev, R600_POWER_LEVEL_LOW, true); @@ -1619,20 +1642,20 @@ int rv6xx_dpm_set_power_state(struct radeon_device *rdev) r600_power_level_enable(rdev, R600_POWER_LEVEL_HIGH, false); r600_power_level_enable(rdev, R600_POWER_LEVEL_MEDIUM, false);
- rv6xx_generate_transition_stepping(rdev); + rv6xx_generate_transition_stepping(rdev, new_ps, old_ps); rv6xx_program_power_level_medium_for_transition(rdev);
if (pi->voltage_control) { - rv6xx_set_sw_voltage_to_safe(rdev); + rv6xx_set_sw_voltage_to_safe(rdev, new_ps, old_ps); if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_STEPVDDC) - rv6xx_set_sw_voltage_to_low(rdev); + rv6xx_set_sw_voltage_to_low(rdev, old_ps); }
if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_BACKBIAS) - rv6xx_set_safe_backbias(rdev); + rv6xx_set_safe_backbias(rdev, new_ps, old_ps);
if (pi->dynamic_pcie_gen2) - rv6xx_set_safe_pcie_gen2(rdev); + rv6xx_set_safe_pcie_gen2(rdev, new_ps, old_ps);
if (pi->voltage_control) rv6xx_enable_dynamic_voltage_control(rdev, false); @@ -1642,7 +1665,7 @@ int rv6xx_dpm_set_power_state(struct radeon_device *rdev)
if (pi->voltage_control) { if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_STEPVDDC) - rv6xx_step_voltage_if_increasing(rdev); + rv6xx_step_voltage_if_increasing(rdev, new_ps, old_ps); msleep((rdev->pm.dpm.voltage_response_time + 999) / 1000); }
@@ -1650,9 +1673,9 @@ int rv6xx_dpm_set_power_state(struct radeon_device *rdev) r600_power_level_enable(rdev, R600_POWER_LEVEL_LOW, false); r600_wait_for_power_level_unequal(rdev, R600_POWER_LEVEL_LOW);
- rv6xx_generate_low_step(rdev); + rv6xx_generate_low_step(rdev, new_ps); rv6xx_invalidate_intermediate_steps(rdev); - rv6xx_calculate_stepping_parameters(rdev); + rv6xx_calculate_stepping_parameters(rdev, new_ps); rv6xx_program_stepping_parameters_lowest_entry(rdev); rv6xx_program_power_level_low_to_lowest_state(rdev);
@@ -1662,7 +1685,7 @@ int rv6xx_dpm_set_power_state(struct radeon_device *rdev)
if (pi->voltage_control) { if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_STEPVDDC) - rv6xx_step_voltage_if_decreasing(rdev); + rv6xx_step_voltage_if_decreasing(rdev, new_ps, old_ps); rv6xx_enable_dynamic_voltage_control(rdev, true); }
@@ -1670,11 +1693,11 @@ int rv6xx_dpm_set_power_state(struct radeon_device *rdev) rv6xx_enable_dynamic_backbias_control(rdev, true);
if (pi->dynamic_pcie_gen2) - rv6xx_enable_dynamic_pcie_gen2(rdev, true); + rv6xx_enable_dynamic_pcie_gen2(rdev, new_ps, true);
rv6xx_reset_lvtm_data_sync(rdev);
- rv6xx_generate_stepping_table(rdev); + rv6xx_generate_stepping_table(rdev, new_ps); rv6xx_program_stepping_parameters_except_lowest_entry(rdev); rv6xx_program_power_level_low(rdev); rv6xx_program_power_level_medium(rdev);
From: Alex Deucher alexander.deucher@amd.com
Needed to properly handle dynamic state adjustment.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/btc_dpm.c | 6 ++- drivers/gpu/drm/radeon/cypress_dpm.c | 6 ++- drivers/gpu/drm/radeon/rv740_dpm.c | 1 - drivers/gpu/drm/radeon/rv770_dpm.c | 77 ++++++++++++++++++---------------- drivers/gpu/drm/radeon/rv770_dpm.h | 8 +++- 5 files changed, 55 insertions(+), 43 deletions(-)
diff --git a/drivers/gpu/drm/radeon/btc_dpm.c b/drivers/gpu/drm/radeon/btc_dpm.c index 79f5ed4..3ae4de6 100644 --- a/drivers/gpu/drm/radeon/btc_dpm.c +++ b/drivers/gpu/drm/radeon/btc_dpm.c @@ -2233,6 +2233,8 @@ void btc_dpm_reset_asic(struct radeon_device *rdev) int btc_dpm_set_power_state(struct radeon_device *rdev) { struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct radeon_ps *new_ps = rdev->pm.dpm.requested_ps; + struct radeon_ps *old_ps = rdev->pm.dpm.current_ps;
btc_apply_state_adjust_rules(rdev);
@@ -2243,7 +2245,7 @@ int btc_dpm_set_power_state(struct radeon_device *rdev) if (eg_pi->pcie_performance_request) cypress_notify_link_speed_change_before_state_change(rdev);
- rv770_set_uvd_clock_before_set_eng_clock(rdev); + rv770_set_uvd_clock_before_set_eng_clock(rdev, new_ps, old_ps); rv770_halt_smc(rdev); btc_set_at_for_uvd(rdev); if (eg_pi->smu_uvd_hs) @@ -2257,7 +2259,7 @@ int btc_dpm_set_power_state(struct radeon_device *rdev)
rv770_resume_smc(rdev); rv770_set_sw_state(rdev); - rv770_set_uvd_clock_after_set_eng_clock(rdev); + rv770_set_uvd_clock_after_set_eng_clock(rdev, new_ps, old_ps);
if (eg_pi->pcie_performance_request) cypress_notify_link_speed_change_after_state_change(rdev); diff --git a/drivers/gpu/drm/radeon/cypress_dpm.c b/drivers/gpu/drm/radeon/cypress_dpm.c index 22297b1..c9026ed 100644 --- a/drivers/gpu/drm/radeon/cypress_dpm.c +++ b/drivers/gpu/drm/radeon/cypress_dpm.c @@ -1930,13 +1930,15 @@ void cypress_dpm_disable(struct radeon_device *rdev) int cypress_dpm_set_power_state(struct radeon_device *rdev) { struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct radeon_ps *new_ps = rdev->pm.dpm.requested_ps; + struct radeon_ps *old_ps = rdev->pm.dpm.current_ps;
rv770_restrict_performance_levels_before_switch(rdev);
if (eg_pi->pcie_performance_request) cypress_notify_link_speed_change_before_state_change(rdev);
- rv770_set_uvd_clock_before_set_eng_clock(rdev); + rv770_set_uvd_clock_before_set_eng_clock(rdev, new_ps, old_ps); rv770_halt_smc(rdev); cypress_upload_sw_state(rdev);
@@ -1947,7 +1949,7 @@ int cypress_dpm_set_power_state(struct radeon_device *rdev)
rv770_resume_smc(rdev); rv770_set_sw_state(rdev); - rv770_set_uvd_clock_after_set_eng_clock(rdev); + rv770_set_uvd_clock_after_set_eng_clock(rdev, new_ps, old_ps);
if (eg_pi->pcie_performance_request) cypress_notify_link_speed_change_after_state_change(rdev); diff --git a/drivers/gpu/drm/radeon/rv740_dpm.c b/drivers/gpu/drm/radeon/rv740_dpm.c index d0d750e..7aa1608 100644 --- a/drivers/gpu/drm/radeon/rv740_dpm.c +++ b/drivers/gpu/drm/radeon/rv740_dpm.c @@ -29,7 +29,6 @@ #include "rv770_dpm.h" #include "atom.h"
-struct rv7xx_ps *rv770_get_ps(struct radeon_ps *rps); struct rv7xx_power_info *rv770_get_pi(struct radeon_device *rdev);
u32 rv740_get_decoded_reference_divider(u32 encoded_ref) diff --git a/drivers/gpu/drm/radeon/rv770_dpm.c b/drivers/gpu/drm/radeon/rv770_dpm.c index f5efa4c..db1dfbc 100644 --- a/drivers/gpu/drm/radeon/rv770_dpm.c +++ b/drivers/gpu/drm/radeon/rv770_dpm.c @@ -1155,10 +1155,10 @@ static int rv770_populate_smc_mvdd_table(struct radeon_device *rdev, return 0; }
-static int rv770_init_smc_table(struct radeon_device *rdev) +static int rv770_init_smc_table(struct radeon_device *rdev, + struct radeon_ps *radeon_boot_state) { struct rv7xx_power_info *pi = rv770_get_pi(rdev); - struct radeon_ps *radeon_boot_state = rdev->pm.dpm.boot_ps; struct rv7xx_ps *boot_state = rv770_get_ps(radeon_boot_state); RV770_SMC_STATETABLE *table = &pi->smc_statetable; int ret; @@ -1364,10 +1364,9 @@ static void rv770_enable_dynamic_pcie_gen2(struct radeon_device *rdev, WREG32_P(GENERAL_PWRMGT, 0, ~ENABLE_GEN2PCIE); }
-static void r7xx_program_memory_timing_parameters(struct radeon_device *rdev) +static void r7xx_program_memory_timing_parameters(struct radeon_device *rdev, + struct radeon_ps *radeon_new_state) { - struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; - if ((rdev->family == CHIP_RV730) || (rdev->family == CHIP_RV710) || (rdev->family == CHIP_RV740)) @@ -1376,10 +1375,10 @@ static void r7xx_program_memory_timing_parameters(struct radeon_device *rdev) rv770_program_memory_timing_parameters(rdev, radeon_new_state); }
-static int rv770_upload_sw_state(struct radeon_device *rdev) +static int rv770_upload_sw_state(struct radeon_device *rdev, + struct radeon_ps *radeon_new_state) { struct rv7xx_power_info *pi = rv770_get_pi(rdev); - struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; u16 address = pi->state_table_start + offsetof(RV770_SMC_STATETABLE, driverState); RV770_SMC_SWSTATE state = { 0 }; @@ -1426,36 +1425,38 @@ int rv770_set_boot_state(struct radeon_device *rdev) return 0; }
-void rv770_set_uvd_clock_before_set_eng_clock(struct radeon_device *rdev) +void rv770_set_uvd_clock_before_set_eng_clock(struct radeon_device *rdev, + struct radeon_ps *new_ps, + struct radeon_ps *old_ps) { - struct rv7xx_ps *new_state = rv770_get_ps(rdev->pm.dpm.requested_ps); - struct rv7xx_ps *current_state = rv770_get_ps(rdev->pm.dpm.current_ps); + struct rv7xx_ps *new_state = rv770_get_ps(new_ps); + struct rv7xx_ps *current_state = rv770_get_ps(old_ps);
- if ((rdev->pm.dpm.requested_ps->vclk == rdev->pm.dpm.current_ps->vclk) && - (rdev->pm.dpm.requested_ps->dclk == rdev->pm.dpm.current_ps->dclk)) + if ((new_ps->vclk == old_ps->vclk) && + (new_ps->dclk == old_ps->dclk)) return;
if (new_state->high.sclk >= current_state->high.sclk) return;
- radeon_set_uvd_clocks(rdev, rdev->pm.dpm.requested_ps->vclk, - rdev->pm.dpm.requested_ps->dclk); + radeon_set_uvd_clocks(rdev, new_ps->vclk, old_ps->dclk); }
-void rv770_set_uvd_clock_after_set_eng_clock(struct radeon_device *rdev) +void rv770_set_uvd_clock_after_set_eng_clock(struct radeon_device *rdev, + struct radeon_ps *new_ps, + struct radeon_ps *old_ps) { - struct rv7xx_ps *new_state = rv770_get_ps(rdev->pm.dpm.requested_ps); - struct rv7xx_ps *current_state = rv770_get_ps(rdev->pm.dpm.current_ps); + struct rv7xx_ps *new_state = rv770_get_ps(new_ps); + struct rv7xx_ps *current_state = rv770_get_ps(old_ps);
- if ((rdev->pm.dpm.requested_ps->vclk == rdev->pm.dpm.current_ps->vclk) && - (rdev->pm.dpm.requested_ps->dclk == rdev->pm.dpm.current_ps->dclk)) + if ((new_ps->vclk == old_ps->vclk) && + (new_ps->dclk == old_ps->dclk)) return;
if (new_state->high.sclk < current_state->high.sclk) return;
- radeon_set_uvd_clocks(rdev, rdev->pm.dpm.requested_ps->vclk, - rdev->pm.dpm.requested_ps->dclk); + radeon_set_uvd_clocks(rdev, new_ps->vclk, new_ps->dclk); }
int rv770_restrict_performance_levels_before_switch(struct radeon_device *rdev) @@ -1709,11 +1710,11 @@ void rv770_program_response_times(struct radeon_device *rdev) #endif }
-static void rv770_program_dcodt_before_state_switch(struct radeon_device *rdev) +static void rv770_program_dcodt_before_state_switch(struct radeon_device *rdev, + struct radeon_ps *radeon_new_state, + struct radeon_ps *radeon_current_state) { struct rv7xx_power_info *pi = rv770_get_pi(rdev); - struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; - struct radeon_ps *radeon_current_state = rdev->pm.dpm.current_ps; struct rv7xx_ps *new_state = rv770_get_ps(radeon_new_state); struct rv7xx_ps *current_state = rv770_get_ps(radeon_current_state); bool current_use_dc = false; @@ -1738,11 +1739,11 @@ static void rv770_program_dcodt_before_state_switch(struct radeon_device *rdev) rv730_program_dcodt(rdev, new_use_dc); }
-static void rv770_program_dcodt_after_state_switch(struct radeon_device *rdev) +static void rv770_program_dcodt_after_state_switch(struct radeon_device *rdev, + struct radeon_ps *radeon_new_state, + struct radeon_ps *radeon_current_state) { struct rv7xx_power_info *pi = rv770_get_pi(rdev); - struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; - struct radeon_ps *radeon_current_state = rdev->pm.dpm.current_ps; struct rv7xx_ps *new_state = rv770_get_ps(radeon_new_state); struct rv7xx_ps *current_state = rv770_get_ps(radeon_current_state); bool current_use_dc = false; @@ -1862,6 +1863,7 @@ int rv770_set_thermal_temperature_range(struct radeon_device *rdev, int rv770_dpm_enable(struct radeon_device *rdev) { struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct radeon_ps *boot_ps = rdev->pm.dpm.boot_ps;
if (pi->gfx_clock_gating) rv770_restore_cgcg(rdev); @@ -1904,7 +1906,7 @@ int rv770_dpm_enable(struct radeon_device *rdev) if (rv770_upload_firmware(rdev)) return -EINVAL; /* get ucode version ? */ - if (rv770_init_smc_table(rdev)) + if (rv770_init_smc_table(rdev, boot_ps)) return -EINVAL; rv770_program_response_times(rdev); r7xx_start_smc(rdev); @@ -1979,19 +1981,21 @@ void rv770_dpm_disable(struct radeon_device *rdev) int rv770_dpm_set_power_state(struct radeon_device *rdev) { struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct radeon_ps *new_ps = rdev->pm.dpm.requested_ps; + struct radeon_ps *old_ps = rdev->pm.dpm.current_ps;
rv770_restrict_performance_levels_before_switch(rdev); - rv770_set_uvd_clock_before_set_eng_clock(rdev); + rv770_set_uvd_clock_before_set_eng_clock(rdev, new_ps, old_ps); rv770_halt_smc(rdev); - rv770_upload_sw_state(rdev); - r7xx_program_memory_timing_parameters(rdev); + rv770_upload_sw_state(rdev, new_ps); + r7xx_program_memory_timing_parameters(rdev, new_ps); if (pi->dcodt) - rv770_program_dcodt_before_state_switch(rdev); + rv770_program_dcodt_before_state_switch(rdev, new_ps, old_ps); rv770_resume_smc(rdev); rv770_set_sw_state(rdev); if (pi->dcodt) - rv770_program_dcodt_after_state_switch(rdev); - rv770_set_uvd_clock_after_set_eng_clock(rdev); + rv770_program_dcodt_after_state_switch(rdev, new_ps, old_ps); + rv770_set_uvd_clock_after_set_eng_clock(rdev, new_ps, old_ps);
return 0; } @@ -1999,13 +2003,14 @@ int rv770_dpm_set_power_state(struct radeon_device *rdev) void rv770_dpm_reset_asic(struct radeon_device *rdev) { struct rv7xx_power_info *pi = rv770_get_pi(rdev); + struct radeon_ps *boot_ps = rdev->pm.dpm.boot_ps;
rv770_restrict_performance_levels_before_switch(rdev); if (pi->dcodt) - rv770_program_dcodt_before_state_switch(rdev); + rv770_program_dcodt_before_state_switch(rdev, boot_ps, boot_ps); rv770_set_boot_state(rdev); if (pi->dcodt) - rv770_program_dcodt_after_state_switch(rdev); + rv770_program_dcodt_after_state_switch(rdev, boot_ps, boot_ps); }
void rv770_dpm_setup_asic(struct radeon_device *rdev) diff --git a/drivers/gpu/drm/radeon/rv770_dpm.h b/drivers/gpu/drm/radeon/rv770_dpm.h index dfb133f..22586df 100644 --- a/drivers/gpu/drm/radeon/rv770_dpm.h +++ b/drivers/gpu/drm/radeon/rv770_dpm.h @@ -266,8 +266,12 @@ int rv770_resume_smc(struct radeon_device *rdev); int rv770_set_sw_state(struct radeon_device *rdev); int rv770_set_boot_state(struct radeon_device *rdev); int rv7xx_parse_power_table(struct radeon_device *rdev); -void rv770_set_uvd_clock_before_set_eng_clock(struct radeon_device *rdev); -void rv770_set_uvd_clock_after_set_eng_clock(struct radeon_device *rdev); +void rv770_set_uvd_clock_before_set_eng_clock(struct radeon_device *rdev, + struct radeon_ps *new_ps, + struct radeon_ps *old_ps); +void rv770_set_uvd_clock_after_set_eng_clock(struct radeon_device *rdev, + struct radeon_ps *new_ps, + struct radeon_ps *old_ps);
/* smc */ int rv770_read_smc_soft_register(struct radeon_device *rdev,
From: Alex Deucher alexander.deucher@amd.com
Needed to properly handle dynamic state adjustment.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/btc_dpm.c | 13 ++++--- drivers/gpu/drm/radeon/cypress_dpm.c | 62 +++++++++++++++++---------------- drivers/gpu/drm/radeon/cypress_dpm.h | 20 ++++++++--- 3 files changed, 53 insertions(+), 42 deletions(-)
diff --git a/drivers/gpu/drm/radeon/btc_dpm.c b/drivers/gpu/drm/radeon/btc_dpm.c index 3ae4de6..73a0ba3 100644 --- a/drivers/gpu/drm/radeon/btc_dpm.c +++ b/drivers/gpu/drm/radeon/btc_dpm.c @@ -2243,26 +2243,26 @@ int btc_dpm_set_power_state(struct radeon_device *rdev) rv770_restrict_performance_levels_before_switch(rdev);
if (eg_pi->pcie_performance_request) - cypress_notify_link_speed_change_before_state_change(rdev); + cypress_notify_link_speed_change_before_state_change(rdev, new_ps, old_ps);
rv770_set_uvd_clock_before_set_eng_clock(rdev, new_ps, old_ps); rv770_halt_smc(rdev); btc_set_at_for_uvd(rdev); if (eg_pi->smu_uvd_hs) btc_notify_uvd_to_smc(rdev); - cypress_upload_sw_state(rdev); + cypress_upload_sw_state(rdev, new_ps);
if (eg_pi->dynamic_ac_timing) - cypress_upload_mc_reg_table(rdev); + cypress_upload_mc_reg_table(rdev, new_ps);
- cypress_program_memory_timing_parameters(rdev); + cypress_program_memory_timing_parameters(rdev, new_ps);
rv770_resume_smc(rdev); rv770_set_sw_state(rdev); rv770_set_uvd_clock_after_set_eng_clock(rdev, new_ps, old_ps);
if (eg_pi->pcie_performance_request) - cypress_notify_link_speed_change_after_state_change(rdev); + cypress_notify_link_speed_change_after_state_change(rdev, new_ps, old_ps);
btc_set_power_state_conditionally_enable_ulv(rdev);
@@ -2273,6 +2273,7 @@ int btc_dpm_enable(struct radeon_device *rdev) { struct rv7xx_power_info *pi = rv770_get_pi(rdev); struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct radeon_ps *boot_ps = rdev->pm.dpm.boot_ps;
if (pi->gfx_clock_gating) btc_cg_clock_gating_default(rdev); @@ -2325,7 +2326,7 @@ int btc_dpm_enable(struct radeon_device *rdev) btc_init_smc_table(rdev);
if (eg_pi->dynamic_ac_timing) - cypress_populate_mc_reg_table(rdev); + cypress_populate_mc_reg_table(rdev, boot_ps);
cypress_program_response_times(rdev); r7xx_start_smc(rdev); diff --git a/drivers/gpu/drm/radeon/cypress_dpm.c b/drivers/gpu/drm/radeon/cypress_dpm.c index c9026ed..1ce1f08 100644 --- a/drivers/gpu/drm/radeon/cypress_dpm.c +++ b/drivers/gpu/drm/radeon/cypress_dpm.c @@ -353,10 +353,10 @@ static u32 cypress_get_maximum_link_speed(struct radeon_ps *radeon_state) return 0; }
-void cypress_notify_link_speed_change_after_state_change(struct radeon_device *rdev) +void cypress_notify_link_speed_change_after_state_change(struct radeon_device *rdev, + struct radeon_ps *radeon_new_state, + struct radeon_ps *radeon_current_state) { - struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; - struct radeon_ps *radeon_current_state = rdev->pm.dpm.current_ps; u32 pcie_link_speed_target = cypress_get_maximum_link_speed(radeon_new_state); u32 pcie_link_speed_current = cypress_get_maximum_link_speed(radeon_current_state); u8 request; @@ -373,10 +373,10 @@ void cypress_notify_link_speed_change_after_state_change(struct radeon_device *r } }
-void cypress_notify_link_speed_change_before_state_change(struct radeon_device *rdev) +void cypress_notify_link_speed_change_before_state_change(struct radeon_device *rdev, + struct radeon_ps *radeon_new_state, + struct radeon_ps *radeon_current_state) { - struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; - struct radeon_ps *radeon_current_state = rdev->pm.dpm.current_ps; u32 pcie_link_speed_target = cypress_get_maximum_link_speed(radeon_new_state); u32 pcie_link_speed_current = cypress_get_maximum_link_speed(radeon_current_state); u8 request; @@ -856,10 +856,10 @@ static void cypress_convert_mc_reg_table_to_smc(struct radeon_device *rdev, &mc_reg_table->data[4]); }
-int cypress_upload_sw_state(struct radeon_device *rdev) +int cypress_upload_sw_state(struct radeon_device *rdev, + struct radeon_ps *radeon_new_state) { struct rv7xx_power_info *pi = rv770_get_pi(rdev); - struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; u16 address = pi->state_table_start + offsetof(RV770_SMC_STATETABLE, driverState); RV770_SMC_SWSTATE state = { 0 }; @@ -874,11 +874,11 @@ int cypress_upload_sw_state(struct radeon_device *rdev) pi->sram_end); }
-int cypress_upload_mc_reg_table(struct radeon_device *rdev) +int cypress_upload_mc_reg_table(struct radeon_device *rdev, + struct radeon_ps *radeon_new_state) { struct rv7xx_power_info *pi = rv770_get_pi(rdev); struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); - struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; SMC_Evergreen_MCRegisters mc_reg_table = { 0 }; u16 address;
@@ -914,9 +914,9 @@ u32 cypress_calculate_burst_time(struct radeon_device *rdev, return burst_time; }
-void cypress_program_memory_timing_parameters(struct radeon_device *rdev) +void cypress_program_memory_timing_parameters(struct radeon_device *rdev, + struct radeon_ps *radeon_new_state) { - struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; struct rv7xx_ps *new_state = rv770_get_ps(radeon_new_state); u32 mc_arb_burst_time = RREG32(MC_ARB_BURST_TIME);
@@ -1106,9 +1106,9 @@ static void cypress_wait_for_mc_sequencer(struct radeon_device *rdev, u8 value) } }
-static void cypress_force_mc_use_s1(struct radeon_device *rdev) +static void cypress_force_mc_use_s1(struct radeon_device *rdev, + struct radeon_ps *radeon_boot_state) { - struct radeon_ps *radeon_boot_state = rdev->pm.dpm.boot_ps; struct rv7xx_ps *boot_state = rv770_get_ps(radeon_boot_state); u32 strobe_mode; u32 mc_seq_cg; @@ -1167,9 +1167,9 @@ static void cypress_copy_ac_timing_from_s1_to_s0(struct radeon_device *rdev) } }
-static void cypress_force_mc_use_s0(struct radeon_device *rdev) +static void cypress_force_mc_use_s0(struct radeon_device *rdev, + struct radeon_ps *radeon_boot_state) { - struct radeon_ps *radeon_boot_state = rdev->pm.dpm.boot_ps; struct rv7xx_ps *boot_state = rv770_get_ps(radeon_boot_state); u32 strobe_mode; u32 mc_seq_cg; @@ -1601,10 +1601,10 @@ int cypress_get_mvdd_configuration(struct radeon_device *rdev) return 0; }
-static int cypress_init_smc_table(struct radeon_device *rdev) +static int cypress_init_smc_table(struct radeon_device *rdev, + struct radeon_ps *radeon_boot_state) { struct rv7xx_power_info *pi = rv770_get_pi(rdev); - struct radeon_ps *radeon_boot_state = rdev->pm.dpm.boot_ps; RV770_SMC_STATETABLE *table = &pi->smc_statetable; int ret;
@@ -1653,11 +1653,11 @@ static int cypress_init_smc_table(struct radeon_device *rdev) pi->sram_end); }
-int cypress_populate_mc_reg_table(struct radeon_device *rdev) +int cypress_populate_mc_reg_table(struct radeon_device *rdev, + struct radeon_ps *radeon_boot_state) { struct rv7xx_power_info *pi = rv770_get_pi(rdev); struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); - struct radeon_ps *radeon_boot_state = rdev->pm.dpm.boot_ps; struct rv7xx_ps *boot_state = rv770_get_ps(radeon_boot_state); SMC_Evergreen_MCRegisters mc_reg_table = { 0 };
@@ -1797,6 +1797,7 @@ int cypress_dpm_enable(struct radeon_device *rdev) { struct rv7xx_power_info *pi = rv770_get_pi(rdev); struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct radeon_ps *boot_ps = rdev->pm.dpm.boot_ps;
if (pi->gfx_clock_gating) rv770_restore_cgcg(rdev); @@ -1814,9 +1815,9 @@ int cypress_dpm_enable(struct radeon_device *rdev)
if (eg_pi->dynamic_ac_timing) { cypress_set_mc_reg_address_table(rdev); - cypress_force_mc_use_s0(rdev); + cypress_force_mc_use_s0(rdev, boot_ps); cypress_initialize_mc_reg_table(rdev); - cypress_force_mc_use_s1(rdev); + cypress_force_mc_use_s1(rdev, boot_ps); }
if (rdev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_BACKBIAS) @@ -1845,11 +1846,11 @@ int cypress_dpm_enable(struct radeon_device *rdev)
cypress_get_table_locations(rdev);
- if (cypress_init_smc_table(rdev)) + if (cypress_init_smc_table(rdev, boot_ps)) return -EINVAL;
if (eg_pi->dynamic_ac_timing) - cypress_populate_mc_reg_table(rdev); + cypress_populate_mc_reg_table(rdev, boot_ps);
cypress_program_response_times(rdev);
@@ -1892,6 +1893,7 @@ void cypress_dpm_disable(struct radeon_device *rdev) { struct rv7xx_power_info *pi = rv770_get_pi(rdev); struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct radeon_ps *boot_ps = rdev->pm.dpm.boot_ps;
if (!rv770_dpm_enabled(rdev)) return; @@ -1922,7 +1924,7 @@ void cypress_dpm_disable(struct radeon_device *rdev) cypress_enable_spread_spectrum(rdev, false);
if (eg_pi->dynamic_ac_timing) - cypress_force_mc_use_s1(rdev); + cypress_force_mc_use_s1(rdev, boot_ps);
rv770_reset_smio_status(rdev); } @@ -1936,23 +1938,23 @@ int cypress_dpm_set_power_state(struct radeon_device *rdev) rv770_restrict_performance_levels_before_switch(rdev);
if (eg_pi->pcie_performance_request) - cypress_notify_link_speed_change_before_state_change(rdev); + cypress_notify_link_speed_change_before_state_change(rdev, new_ps, old_ps);
rv770_set_uvd_clock_before_set_eng_clock(rdev, new_ps, old_ps); rv770_halt_smc(rdev); - cypress_upload_sw_state(rdev); + cypress_upload_sw_state(rdev, new_ps);
if (eg_pi->dynamic_ac_timing) - cypress_upload_mc_reg_table(rdev); + cypress_upload_mc_reg_table(rdev, new_ps);
- cypress_program_memory_timing_parameters(rdev); + cypress_program_memory_timing_parameters(rdev, new_ps);
rv770_resume_smc(rdev); rv770_set_sw_state(rdev); rv770_set_uvd_clock_after_set_eng_clock(rdev, new_ps, old_ps);
if (eg_pi->pcie_performance_request) - cypress_notify_link_speed_change_after_state_change(rdev); + cypress_notify_link_speed_change_after_state_change(rdev, new_ps, old_ps);
return 0; } diff --git a/drivers/gpu/drm/radeon/cypress_dpm.h b/drivers/gpu/drm/radeon/cypress_dpm.h index 9b6198e..5b19364 100644 --- a/drivers/gpu/drm/radeon/cypress_dpm.h +++ b/drivers/gpu/drm/radeon/cypress_dpm.h @@ -120,18 +120,26 @@ int cypress_populate_smc_initial_state(struct radeon_device *rdev, RV770_SMC_STATETABLE *table); u32 cypress_calculate_burst_time(struct radeon_device *rdev, u32 engine_clock, u32 memory_clock); -void cypress_notify_link_speed_change_before_state_change(struct radeon_device *rdev); -int cypress_upload_sw_state(struct radeon_device *rdev); -int cypress_upload_mc_reg_table(struct radeon_device *rdev); -void cypress_program_memory_timing_parameters(struct radeon_device *rdev); -void cypress_notify_link_speed_change_after_state_change(struct radeon_device *rdev); +void cypress_notify_link_speed_change_before_state_change(struct radeon_device *rdev, + struct radeon_ps *radeon_new_state, + struct radeon_ps *radeon_current_state); +int cypress_upload_sw_state(struct radeon_device *rdev, + struct radeon_ps *radeon_new_state); +int cypress_upload_mc_reg_table(struct radeon_device *rdev, + struct radeon_ps *radeon_new_state); +void cypress_program_memory_timing_parameters(struct radeon_device *rdev, + struct radeon_ps *radeon_new_state); +void cypress_notify_link_speed_change_after_state_change(struct radeon_device *rdev, + struct radeon_ps *radeon_new_state, + struct radeon_ps *radeon_current_state); int cypress_construct_voltage_tables(struct radeon_device *rdev); int cypress_get_mvdd_configuration(struct radeon_device *rdev); void cypress_enable_spread_spectrum(struct radeon_device *rdev, bool enable); void cypress_enable_display_gap(struct radeon_device *rdev); int cypress_get_table_locations(struct radeon_device *rdev); -int cypress_populate_mc_reg_table(struct radeon_device *rdev); +int cypress_populate_mc_reg_table(struct radeon_device *rdev, + struct radeon_ps *radeon_boot_state); void cypress_program_response_times(struct radeon_device *rdev); int cypress_notify_smc_display_change(struct radeon_device *rdev, bool has_display);
From: Alex Deucher alexander.deucher@amd.com
Needed to properly handle dynamic state adjustment.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/btc_dpm.c | 30 +++++++++++++++--------------- drivers/gpu/drm/radeon/btc_dpm.h | 3 ++- drivers/gpu/drm/radeon/ni_dpm.c | 3 ++- 3 files changed, 19 insertions(+), 17 deletions(-)
diff --git a/drivers/gpu/drm/radeon/btc_dpm.c b/drivers/gpu/drm/radeon/btc_dpm.c index 73a0ba3..30aed00 100644 --- a/drivers/gpu/drm/radeon/btc_dpm.c +++ b/drivers/gpu/drm/radeon/btc_dpm.c @@ -1604,11 +1604,11 @@ bool btc_dpm_enabled(struct radeon_device *rdev) return false; }
-static int btc_init_smc_table(struct radeon_device *rdev) +static int btc_init_smc_table(struct radeon_device *rdev, + struct radeon_ps *radeon_boot_state) { struct rv7xx_power_info *pi = rv770_get_pi(rdev); struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); - struct radeon_ps *radeon_boot_state = rdev->pm.dpm.boot_ps; RV770_SMC_STATETABLE *table = &pi->smc_statetable; int ret;
@@ -1668,11 +1668,11 @@ static int btc_init_smc_table(struct radeon_device *rdev) pi->sram_end); }
-static void btc_set_at_for_uvd(struct radeon_device *rdev) +static void btc_set_at_for_uvd(struct radeon_device *rdev, + struct radeon_ps *radeon_new_state) { struct rv7xx_power_info *pi = rv770_get_pi(rdev); struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); - struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; int idx = 0;
if (r600_is_uvd_state(radeon_new_state->class, radeon_new_state->class2)) @@ -1692,9 +1692,9 @@ static void btc_set_at_for_uvd(struct radeon_device *rdev)
}
-void btc_notify_uvd_to_smc(struct radeon_device *rdev) +void btc_notify_uvd_to_smc(struct radeon_device *rdev, + struct radeon_ps *radeon_new_state) { - struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev);
if (r600_is_uvd_state(radeon_new_state->class, radeon_new_state->class2)) { @@ -1814,11 +1814,11 @@ static int btc_enable_ulv(struct radeon_device *rdev) return 0; }
-static int btc_set_power_state_conditionally_enable_ulv(struct radeon_device *rdev) +static int btc_set_power_state_conditionally_enable_ulv(struct radeon_device *rdev, + struct radeon_ps *radeon_new_state) { int ret = 0; struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); - struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps;
if (eg_pi->ulv.supported) { if (btc_is_state_ulv_compatible(rdev, radeon_new_state)) { @@ -2059,10 +2059,10 @@ static void btc_init_stutter_mode(struct radeon_device *rdev) } }
-static void btc_apply_state_adjust_rules(struct radeon_device *rdev) +static void btc_apply_state_adjust_rules(struct radeon_device *rdev, + struct radeon_ps *rps) { struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); - struct radeon_ps *rps = rdev->pm.dpm.requested_ps; struct rv7xx_ps *ps = rv770_get_ps(rps); struct radeon_clock_and_voltage_limits *max_limits; bool disable_mclk_switching; @@ -2236,7 +2236,7 @@ int btc_dpm_set_power_state(struct radeon_device *rdev) struct radeon_ps *new_ps = rdev->pm.dpm.requested_ps; struct radeon_ps *old_ps = rdev->pm.dpm.current_ps;
- btc_apply_state_adjust_rules(rdev); + btc_apply_state_adjust_rules(rdev, new_ps);
btc_disable_ulv(rdev); btc_set_boot_state_timing(rdev); @@ -2247,9 +2247,9 @@ int btc_dpm_set_power_state(struct radeon_device *rdev)
rv770_set_uvd_clock_before_set_eng_clock(rdev, new_ps, old_ps); rv770_halt_smc(rdev); - btc_set_at_for_uvd(rdev); + btc_set_at_for_uvd(rdev, new_ps); if (eg_pi->smu_uvd_hs) - btc_notify_uvd_to_smc(rdev); + btc_notify_uvd_to_smc(rdev, new_ps); cypress_upload_sw_state(rdev, new_ps);
if (eg_pi->dynamic_ac_timing) @@ -2264,7 +2264,7 @@ int btc_dpm_set_power_state(struct radeon_device *rdev) if (eg_pi->pcie_performance_request) cypress_notify_link_speed_change_after_state_change(rdev, new_ps, old_ps);
- btc_set_power_state_conditionally_enable_ulv(rdev); + btc_set_power_state_conditionally_enable_ulv(rdev, new_ps);
return 0; } @@ -2323,7 +2323,7 @@ int btc_dpm_enable(struct radeon_device *rdev) return -EINVAL;
cypress_get_table_locations(rdev); - btc_init_smc_table(rdev); + btc_init_smc_table(rdev, boot_ps);
if (eg_pi->dynamic_ac_timing) cypress_populate_mc_reg_table(rdev, boot_ps); diff --git a/drivers/gpu/drm/radeon/btc_dpm.h b/drivers/gpu/drm/radeon/btc_dpm.h index c22d39f..1a15e0e 100644 --- a/drivers/gpu/drm/radeon/btc_dpm.h +++ b/drivers/gpu/drm/radeon/btc_dpm.h @@ -51,6 +51,7 @@ void btc_apply_voltage_delta_rules(struct radeon_device *rdev, u16 *vddc, u16 *vddci); bool btc_dpm_enabled(struct radeon_device *rdev); int btc_reset_to_default(struct radeon_device *rdev); -void btc_notify_uvd_to_smc(struct radeon_device *rdev); +void btc_notify_uvd_to_smc(struct radeon_device *rdev, + struct radeon_ps *radeon_new_state);
#endif diff --git a/drivers/gpu/drm/radeon/ni_dpm.c b/drivers/gpu/drm/radeon/ni_dpm.c index 44016f2..9f68115 100644 --- a/drivers/gpu/drm/radeon/ni_dpm.c +++ b/drivers/gpu/drm/radeon/ni_dpm.c @@ -3628,6 +3628,7 @@ int ni_power_control_set_level(struct radeon_device *rdev) int ni_dpm_set_power_state(struct radeon_device *rdev) { struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct radeon_ps *new_ps = rdev->pm.dpm.requested_ps; int ret;
ni_apply_state_adjust_rules(rdev); @@ -3637,7 +3638,7 @@ int ni_dpm_set_power_state(struct radeon_device *rdev) ni_enable_smc_cac(rdev, false); rv770_halt_smc(rdev); if (eg_pi->smu_uvd_hs) - btc_notify_uvd_to_smc(rdev); + btc_notify_uvd_to_smc(rdev, new_ps); ni_upload_sw_state(rdev); if (eg_pi->dynamic_ac_timing) ni_upload_mc_reg_table(rdev);
From: Alex Deucher alexander.deucher@amd.com
Needed to properly handle dynamic state adjustment.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/ni_dpm.c | 69 ++++++++++++++++++++------------------ 1 files changed, 36 insertions(+), 33 deletions(-)
diff --git a/drivers/gpu/drm/radeon/ni_dpm.c b/drivers/gpu/drm/radeon/ni_dpm.c index 9f68115..ebc9837 100644 --- a/drivers/gpu/drm/radeon/ni_dpm.c +++ b/drivers/gpu/drm/radeon/ni_dpm.c @@ -788,10 +788,10 @@ static void ni_calculate_leakage_for_v_and_t(struct radeon_device *rdev, ni_calculate_leakage_for_v_and_t_formula(coeff, v, t, i_leakage, leakage); }
-static void ni_apply_state_adjust_rules(struct radeon_device *rdev) +static void ni_apply_state_adjust_rules(struct radeon_device *rdev, + struct radeon_ps *rps) { struct ni_power_info *ni_pi = ni_get_pi(rdev); - struct radeon_ps *rps = rdev->pm.dpm.requested_ps; struct ni_ps *ps = ni_get_ps(rps); struct radeon_clock_and_voltage_limits *max_limits; bool disable_mclk_switching; @@ -1434,13 +1434,13 @@ static int ni_calculate_adjusted_tdp_limits(struct radeon_device *rdev, return 0; }
-static int ni_populate_smc_tdp_limits(struct radeon_device *rdev) +static int ni_populate_smc_tdp_limits(struct radeon_device *rdev, + struct radeon_ps *radeon_state) { struct rv7xx_power_info *pi = rv770_get_pi(rdev); struct ni_power_info *ni_pi = ni_get_pi(rdev);
if (ni_pi->enable_power_containment) { - struct radeon_ps *radeon_state = rdev->pm.dpm.requested_ps; NISLANDS_SMC_STATETABLE *smc_table = &ni_pi->smc_statetable; u32 scaling_factor = ni_get_smc_power_scaling_factor(rdev); u32 tdp_limit; @@ -1647,10 +1647,9 @@ static int ni_do_program_memory_timing_parameters(struct radeon_device *rdev, return ret; }
-static int ni_program_memory_timing_parameters(struct radeon_device *rdev) +static int ni_program_memory_timing_parameters(struct radeon_device *rdev, + struct radeon_ps *radeon_new_state) { - struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; - return ni_do_program_memory_timing_parameters(rdev, radeon_new_state, NISLANDS_DRIVER_STATE_ARB_INDEX); } @@ -2576,7 +2575,9 @@ static int ni_populate_sq_ramping_values(struct radeon_device *rdev, return 0; }
-static int ni_enable_power_containment(struct radeon_device *rdev, bool enable) +static int ni_enable_power_containment(struct radeon_device *rdev, + struct radeon_ps *radeon_new_state, + bool enable) { struct ni_power_info *ni_pi = ni_get_pi(rdev); PPSMC_Result smc_result; @@ -2584,8 +2585,6 @@ static int ni_enable_power_containment(struct radeon_device *rdev, bool enable)
if (ni_pi->enable_power_containment) { if (enable) { - struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; - if (!r600_is_uvd_state(radeon_new_state->class, radeon_new_state->class2)) { smc_result = rv770_send_msg_to_smc(rdev, PPSMC_TDPClampingActive); if (smc_result != PPSMC_Result_OK) { @@ -2665,10 +2664,10 @@ static int ni_convert_power_state_to_smc(struct radeon_device *rdev, return ni_populate_smc_t(rdev, radeon_state, smc_state); }
-static int ni_upload_sw_state(struct radeon_device *rdev) +static int ni_upload_sw_state(struct radeon_device *rdev, + struct radeon_ps *radeon_new_state) { struct rv7xx_power_info *pi = rv770_get_pi(rdev); - struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; u16 address = pi->state_table_start + offsetof(NISLANDS_SMC_STATETABLE, driverState); u16 state_size = sizeof(NISLANDS_SMC_SWSTATE) + @@ -2974,12 +2973,12 @@ static void ni_convert_mc_reg_table_to_smc(struct radeon_device *rdev, } }
-static int ni_populate_mc_reg_table(struct radeon_device *rdev) +static int ni_populate_mc_reg_table(struct radeon_device *rdev, + struct radeon_ps *radeon_boot_state) { struct rv7xx_power_info *pi = rv770_get_pi(rdev); struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); struct ni_power_info *ni_pi = ni_get_pi(rdev); - struct radeon_ps *radeon_boot_state = rdev->pm.dpm.boot_ps; struct ni_ps *boot_state = ni_get_ps(radeon_boot_state); SMC_NIslands_MCRegisters *mc_reg_table = &ni_pi->smc_mc_reg_table;
@@ -3005,12 +3004,12 @@ static int ni_populate_mc_reg_table(struct radeon_device *rdev) pi->sram_end); }
-static int ni_upload_mc_reg_table(struct radeon_device *rdev) +static int ni_upload_mc_reg_table(struct radeon_device *rdev, + struct radeon_ps *radeon_new_state) { struct rv7xx_power_info *pi = rv770_get_pi(rdev); struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); struct ni_power_info *ni_pi = ni_get_pi(rdev); - struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; struct ni_ps *ni_new_state = ni_get_ps(radeon_new_state); SMC_NIslands_MCRegisters *mc_reg_table = &ni_pi->smc_mc_reg_table; u16 address; @@ -3359,7 +3358,9 @@ static int ni_initialize_hardware_cac_manager(struct radeon_device *rdev) return 0; }
-static int ni_enable_smc_cac(struct radeon_device *rdev, bool enable) +static int ni_enable_smc_cac(struct radeon_device *rdev, + struct radeon_ps *radeon_new_state, + bool enable) { struct ni_power_info *ni_pi = ni_get_pi(rdev); int ret = 0; @@ -3367,8 +3368,6 @@ static int ni_enable_smc_cac(struct radeon_device *rdev, bool enable)
if (ni_pi->enable_cac) { if (enable) { - struct radeon_ps *radeon_new_state = rdev->pm.dpm.requested_ps; - if (!r600_is_uvd_state(radeon_new_state->class, radeon_new_state->class2)) { smc_result = rv770_send_msg_to_smc(rdev, PPSMC_MSG_CollectCAC_PowerCorreln);
@@ -3507,6 +3506,7 @@ int ni_dpm_enable(struct radeon_device *rdev) { struct rv7xx_power_info *pi = rv770_get_pi(rdev); struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct radeon_ps *boot_ps = rdev->pm.dpm.boot_ps;
if (pi->gfx_clock_gating) ni_cg_clockgating_default(rdev); @@ -3543,10 +3543,10 @@ int ni_dpm_enable(struct radeon_device *rdev) ni_init_smc_spll_table(rdev); ni_init_arb_table_index(rdev); if (eg_pi->dynamic_ac_timing) - ni_populate_mc_reg_table(rdev); + ni_populate_mc_reg_table(rdev, boot_ps); ni_initialize_smc_cac_tables(rdev); ni_initialize_hardware_cac_manager(rdev); - ni_populate_smc_tdp_limits(rdev); + ni_populate_smc_tdp_limits(rdev, boot_ps); ni_program_response_times(rdev); r7xx_start_smc(rdev); cypress_notify_smc_display_change(rdev, false); @@ -3583,14 +3583,15 @@ void ni_dpm_disable(struct radeon_device *rdev) { struct rv7xx_power_info *pi = rv770_get_pi(rdev); struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct radeon_ps *boot_ps = rdev->pm.dpm.boot_ps;
if (!btc_dpm_enabled(rdev)) return; rv770_clear_vc(rdev); if (pi->thermal_protection) rv770_enable_thermal_protection(rdev, false); - ni_enable_power_containment(rdev, false); - ni_enable_smc_cac(rdev, false); + ni_enable_power_containment(rdev, boot_ps, false); + ni_enable_smc_cac(rdev, boot_ps, false); cypress_enable_spread_spectrum(rdev, false); rv770_enable_auto_throttle_source(rdev, RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL, false); if (pi->dynamic_pcie_gen2) @@ -3616,9 +3617,11 @@ void ni_dpm_disable(struct radeon_device *rdev)
int ni_power_control_set_level(struct radeon_device *rdev) { + struct radeon_ps *new_ps = rdev->pm.dpm.requested_ps; + ni_restrict_performance_levels_before_switch(rdev); rv770_halt_smc(rdev); - ni_populate_smc_tdp_limits(rdev); + ni_populate_smc_tdp_limits(rdev, new_ps); rv770_resume_smc(rdev); rv770_set_sw_state(rdev);
@@ -3631,25 +3634,25 @@ int ni_dpm_set_power_state(struct radeon_device *rdev) struct radeon_ps *new_ps = rdev->pm.dpm.requested_ps; int ret;
- ni_apply_state_adjust_rules(rdev); + ni_apply_state_adjust_rules(rdev, new_ps);
ni_restrict_performance_levels_before_switch(rdev); - ni_enable_power_containment(rdev, false); - ni_enable_smc_cac(rdev, false); + ni_enable_power_containment(rdev, new_ps, false); + ni_enable_smc_cac(rdev, new_ps, false); rv770_halt_smc(rdev); if (eg_pi->smu_uvd_hs) btc_notify_uvd_to_smc(rdev, new_ps); - ni_upload_sw_state(rdev); + ni_upload_sw_state(rdev, new_ps); if (eg_pi->dynamic_ac_timing) - ni_upload_mc_reg_table(rdev); - ret = ni_program_memory_timing_parameters(rdev); + ni_upload_mc_reg_table(rdev, new_ps); + ret = ni_program_memory_timing_parameters(rdev, new_ps); if (ret) return ret; - ni_populate_smc_tdp_limits(rdev); + ni_populate_smc_tdp_limits(rdev, new_ps); rv770_resume_smc(rdev); rv770_set_sw_state(rdev); - ni_enable_smc_cac(rdev, true); - ni_enable_power_containment(rdev, true); + ni_enable_smc_cac(rdev, new_ps, true); + ni_enable_power_containment(rdev, new_ps, true);
return 0; }
From: Alex Deucher alexander.deucher@amd.com
Needed to properly handle dynamic state adjustment.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/sumo_dpm.c | 143 +++++++++++++++++++++---------------- 1 files changed, 81 insertions(+), 62 deletions(-)
diff --git a/drivers/gpu/drm/radeon/sumo_dpm.c b/drivers/gpu/drm/radeon/sumo_dpm.c index 3805302..f5de850 100644 --- a/drivers/gpu/drm/radeon/sumo_dpm.c +++ b/drivers/gpu/drm/radeon/sumo_dpm.c @@ -342,10 +342,11 @@ static void sumo_init_bsp(struct radeon_device *rdev) }
-static void sumo_program_bsp(struct radeon_device *rdev) +static void sumo_program_bsp(struct radeon_device *rdev, + struct radeon_ps *rps) { struct sumo_power_info *pi = sumo_get_pi(rdev); - struct sumo_ps *ps = sumo_get_ps(rdev->pm.dpm.requested_ps); + struct sumo_ps *ps = sumo_get_ps(rps); u32 i; u32 highest_engine_clock = ps->levels[ps->num_levels - 1].sclk;
@@ -384,10 +385,11 @@ static void sumo_write_at(struct radeon_device *rdev, WREG32(CG_AT_7, value); }
-static void sumo_program_at(struct radeon_device *rdev) +static void sumo_program_at(struct radeon_device *rdev, + struct radeon_ps *rps) { struct sumo_power_info *pi = sumo_get_pi(rdev); - struct sumo_ps *ps = sumo_get_ps(rdev->pm.dpm.requested_ps); + struct sumo_ps *ps = sumo_get_ps(rps); u32 asi; u32 i; u32 m_a; @@ -662,10 +664,11 @@ static void sumo_enable_power_level_0(struct radeon_device *rdev) sumo_power_level_enable(rdev, 0, true); }
-static void sumo_patch_boost_state(struct radeon_device *rdev) +static void sumo_patch_boost_state(struct radeon_device *rdev, + struct radeon_ps *rps) { struct sumo_power_info *pi = sumo_get_pi(rdev); - struct sumo_ps *new_ps = sumo_get_ps(rdev->pm.dpm.requested_ps); + struct sumo_ps *new_ps = sumo_get_ps(rps);
if (new_ps->flags & SUMO_POWERSTATE_FLAGS_BOOST_STATE) { pi->boost_pl = new_ps->levels[new_ps->num_levels - 1]; @@ -675,10 +678,12 @@ static void sumo_patch_boost_state(struct radeon_device *rdev) } }
-static void sumo_pre_notify_alt_vddnb_change(struct radeon_device *rdev) +static void sumo_pre_notify_alt_vddnb_change(struct radeon_device *rdev, + struct radeon_ps *new_rps, + struct radeon_ps *old_rps) { - struct sumo_ps *new_ps = sumo_get_ps(rdev->pm.dpm.requested_ps); - struct sumo_ps *old_ps = sumo_get_ps(rdev->pm.dpm.current_ps); + struct sumo_ps *new_ps = sumo_get_ps(new_rps); + struct sumo_ps *old_ps = sumo_get_ps(old_rps); u32 nbps1_old = 0; u32 nbps1_new = 0;
@@ -691,10 +696,12 @@ static void sumo_pre_notify_alt_vddnb_change(struct radeon_device *rdev) sumo_smu_notify_alt_vddnb_change(rdev, 0, 0); }
-static void sumo_post_notify_alt_vddnb_change(struct radeon_device *rdev) +static void sumo_post_notify_alt_vddnb_change(struct radeon_device *rdev, + struct radeon_ps *new_rps, + struct radeon_ps *old_rps) { - struct sumo_ps *new_ps = sumo_get_ps(rdev->pm.dpm.requested_ps); - struct sumo_ps *old_ps = sumo_get_ps(rdev->pm.dpm.current_ps); + struct sumo_ps *new_ps = sumo_get_ps(new_rps); + struct sumo_ps *old_ps = sumo_get_ps(old_rps); u32 nbps1_old = 0; u32 nbps1_new = 0;
@@ -707,9 +714,11 @@ static void sumo_post_notify_alt_vddnb_change(struct radeon_device *rdev) sumo_smu_notify_alt_vddnb_change(rdev, 1, 1); }
-static void sumo_enable_boost(struct radeon_device *rdev, bool enable) +static void sumo_enable_boost(struct radeon_device *rdev, + struct radeon_ps *rps, + bool enable) { - struct sumo_ps *new_ps = sumo_get_ps(rdev->pm.dpm.requested_ps); + struct sumo_ps *new_ps = sumo_get_ps(rps);
if (enable) { if (new_ps->flags & SUMO_POWERSTATE_FLAGS_BOOST_STATE) @@ -718,9 +727,10 @@ static void sumo_enable_boost(struct radeon_device *rdev, bool enable) sumo_boost_state_enable(rdev, false); }
-static void sumo_update_current_power_levels(struct radeon_device *rdev) +static void sumo_update_current_power_levels(struct radeon_device *rdev, + struct radeon_ps *rps) { - struct sumo_ps *new_ps = sumo_get_ps(rdev->pm.dpm.requested_ps); + struct sumo_ps *new_ps = sumo_get_ps(rps); struct sumo_power_info *pi = sumo_get_pi(rdev);
pi->current_ps = *new_ps; @@ -736,9 +746,10 @@ static void sumo_set_forced_level_0(struct radeon_device *rdev) sumo_set_forced_level(rdev, 0); }
-static void sumo_program_wl(struct radeon_device *rdev) +static void sumo_program_wl(struct radeon_device *rdev, + struct radeon_ps *rps) { - struct sumo_ps *new_ps = sumo_get_ps(rdev->pm.dpm.requested_ps); + struct sumo_ps *new_ps = sumo_get_ps(rps); u32 dpm_ctrl4 = RREG32(CG_SCLK_DPM_CTRL_4);
dpm_ctrl4 &= 0xFFFFFF00; @@ -750,11 +761,13 @@ static void sumo_program_wl(struct radeon_device *rdev) WREG32(CG_SCLK_DPM_CTRL_4, dpm_ctrl4); }
-static void sumo_program_power_levels_0_to_n(struct radeon_device *rdev) +static void sumo_program_power_levels_0_to_n(struct radeon_device *rdev, + struct radeon_ps *new_rps, + struct radeon_ps *old_rps) { struct sumo_power_info *pi = sumo_get_pi(rdev); - struct sumo_ps *new_ps = sumo_get_ps(rdev->pm.dpm.requested_ps); - struct sumo_ps *old_ps = sumo_get_ps(rdev->pm.dpm.current_ps); + struct sumo_ps *new_ps = sumo_get_ps(new_rps); + struct sumo_ps *old_ps = sumo_get_ps(old_rps); u32 i; u32 n_current_state_levels = (old_ps == NULL) ? 1 : old_ps->num_levels;
@@ -811,38 +824,40 @@ static void sumo_program_bootup_state(struct radeon_device *rdev) sumo_power_level_enable(rdev, i, false); }
-static void sumo_set_uvd_clock_before_set_eng_clock(struct radeon_device *rdev) +static void sumo_set_uvd_clock_before_set_eng_clock(struct radeon_device *rdev, + struct radeon_ps *new_rps, + struct radeon_ps *old_rps) { - struct sumo_ps *new_ps = sumo_get_ps(rdev->pm.dpm.requested_ps); - struct sumo_ps *current_ps = sumo_get_ps(rdev->pm.dpm.current_ps); + struct sumo_ps *new_ps = sumo_get_ps(new_rps); + struct sumo_ps *current_ps = sumo_get_ps(old_rps);
- if ((rdev->pm.dpm.requested_ps->vclk == rdev->pm.dpm.current_ps->vclk) && - (rdev->pm.dpm.requested_ps->dclk == rdev->pm.dpm.current_ps->dclk)) + if ((new_rps->vclk == old_rps->vclk) && + (new_rps->dclk == old_rps->dclk)) return;
if (new_ps->levels[new_ps->num_levels - 1].sclk >= current_ps->levels[current_ps->num_levels - 1].sclk) return;
- radeon_set_uvd_clocks(rdev, rdev->pm.dpm.requested_ps->vclk, - rdev->pm.dpm.requested_ps->dclk); + radeon_set_uvd_clocks(rdev, new_rps->vclk, new_rps->dclk); }
-static void sumo_set_uvd_clock_after_set_eng_clock(struct radeon_device *rdev) +static void sumo_set_uvd_clock_after_set_eng_clock(struct radeon_device *rdev, + struct radeon_ps *new_rps, + struct radeon_ps *old_rps) { - struct sumo_ps *new_ps = sumo_get_ps(rdev->pm.dpm.requested_ps); - struct sumo_ps *current_ps = sumo_get_ps(rdev->pm.dpm.current_ps); + struct sumo_ps *new_ps = sumo_get_ps(new_rps); + struct sumo_ps *current_ps = sumo_get_ps(old_rps);
- if ((rdev->pm.dpm.requested_ps->vclk == rdev->pm.dpm.current_ps->vclk) && - (rdev->pm.dpm.requested_ps->dclk == rdev->pm.dpm.current_ps->dclk)) + if ((new_rps->vclk == old_rps->vclk) && + (new_rps->dclk == old_rps->dclk)) return;
if (new_ps->levels[new_ps->num_levels - 1].sclk < current_ps->levels[current_ps->num_levels - 1].sclk) return;
- radeon_set_uvd_clocks(rdev, rdev->pm.dpm.requested_ps->vclk, - rdev->pm.dpm.requested_ps->dclk); + radeon_set_uvd_clocks(rdev, new_rps->vclk, new_rps->dclk); }
void sumo_take_smu_control(struct radeon_device *rdev, bool enable) @@ -960,10 +975,11 @@ static void sumo_program_dc_hto(struct radeon_device *rdev) WREG32(CG_SCLK_DPM_CTRL_4, cg_sclk_dpm_ctrl_4); }
-static void sumo_force_nbp_state(struct radeon_device *rdev) +static void sumo_force_nbp_state(struct radeon_device *rdev, + struct radeon_ps *rps) { struct sumo_power_info *pi = sumo_get_pi(rdev); - struct sumo_ps *new_ps = sumo_get_ps(rdev->pm.dpm.requested_ps); + struct sumo_ps *new_ps = sumo_get_ps(rps);
if (!pi->driver_nbps_policy_disable) { if (new_ps->flags & SUMO_POWERSTATE_FLAGS_FORCE_NBPS1_STATE) @@ -1061,11 +1077,12 @@ static void sumo_patch_thermal_state(struct radeon_device *rdev, ps->levels[0].ss_divider_index = 0; }
-static void sumo_apply_state_adjust_rules(struct radeon_device *rdev) +static void sumo_apply_state_adjust_rules(struct radeon_device *rdev, + struct radeon_ps *new_rps, + struct radeon_ps *old_rps) { - struct radeon_ps *rps = rdev->pm.dpm.requested_ps; - struct sumo_ps *ps = sumo_get_ps(rps); - struct sumo_ps *current_ps = sumo_get_ps(rdev->pm.dpm.current_ps); + struct sumo_ps *ps = sumo_get_ps(new_rps); + struct sumo_ps *current_ps = sumo_get_ps(old_rps); struct sumo_power_info *pi = sumo_get_pi(rdev); u32 min_voltage = 0; /* ??? */ u32 min_sclk = pi->sys_info.min_sclk; /* XXX check against disp reqs */ @@ -1077,17 +1094,17 @@ static void sumo_apply_state_adjust_rules(struct radeon_device *rdev) rdev->pm.dpm.hw_ps.ps_priv = &pi->hw_ps; ps = &pi->hw_ps;
- if (rps->class & ATOM_PPLIB_CLASSIFICATION_THERMAL) + if (new_rps->class & ATOM_PPLIB_CLASSIFICATION_THERMAL) return sumo_patch_thermal_state(rdev, ps, current_ps);
if (pi->enable_boost) { - if (rps->class & ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE) + if (new_rps->class & ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE) ps->flags |= SUMO_POWERSTATE_FLAGS_BOOST_STATE; }
- if ((rps->class & ATOM_PPLIB_CLASSIFICATION_UI_BATTERY) || - (rps->class & ATOM_PPLIB_CLASSIFICATION_SDSTATE) || - (rps->class & ATOM_PPLIB_CLASSIFICATION_HDSTATE)) + if ((new_rps->class & ATOM_PPLIB_CLASSIFICATION_UI_BATTERY) || + (new_rps->class & ATOM_PPLIB_CLASSIFICATION_SDSTATE) || + (new_rps->class & ATOM_PPLIB_CLASSIFICATION_HDSTATE)) ps->flags |= SUMO_POWERSTATE_FLAGS_FORCE_NBPS1_STATE;
for (i = 0; i < ps->num_levels; i++) { @@ -1120,8 +1137,8 @@ static void sumo_apply_state_adjust_rules(struct radeon_device *rdev)
if (ps->flags & SUMO_POWERSTATE_FLAGS_FORCE_NBPS1_STATE) ps->levels[i].allow_gnb_slow = 1; - else if ((rps->class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE) || - (rps->class2 & ATOM_PPLIB_CLASSIFICATION2_MVC)) + else if ((new_rps->class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE) || + (new_rps->class2 & ATOM_PPLIB_CLASSIFICATION2_MVC)) ps->levels[i].allow_gnb_slow = 0; else if (i == ps->num_levels - 1) ps->levels[i].allow_gnb_slow = 0; @@ -1240,36 +1257,38 @@ void sumo_dpm_disable(struct radeon_device *rdev) int sumo_dpm_set_power_state(struct radeon_device *rdev) { struct sumo_power_info *pi = sumo_get_pi(rdev); + struct radeon_ps *new_ps = rdev->pm.dpm.requested_ps; + struct radeon_ps *old_ps = rdev->pm.dpm.current_ps;
if (pi->enable_dynamic_patch_ps) - sumo_apply_state_adjust_rules(rdev); + sumo_apply_state_adjust_rules(rdev, new_ps, old_ps); if (pi->enable_dpm) - sumo_set_uvd_clock_before_set_eng_clock(rdev); - sumo_update_current_power_levels(rdev); + sumo_set_uvd_clock_before_set_eng_clock(rdev, new_ps, old_ps); + sumo_update_current_power_levels(rdev, new_ps); if (pi->enable_boost) { - sumo_enable_boost(rdev, false); - sumo_patch_boost_state(rdev); + sumo_enable_boost(rdev, new_ps, false); + sumo_patch_boost_state(rdev, new_ps); } if (pi->enable_dpm) { - sumo_pre_notify_alt_vddnb_change(rdev); + sumo_pre_notify_alt_vddnb_change(rdev, new_ps, old_ps); sumo_enable_power_level_0(rdev); sumo_set_forced_level_0(rdev); sumo_set_forced_mode_enabled(rdev); sumo_wait_for_level_0(rdev); - sumo_program_power_levels_0_to_n(rdev); - sumo_program_wl(rdev); - sumo_program_bsp(rdev); - sumo_program_at(rdev); - sumo_force_nbp_state(rdev); + sumo_program_power_levels_0_to_n(rdev, new_ps, old_ps); + sumo_program_wl(rdev, new_ps); + sumo_program_bsp(rdev, new_ps); + sumo_program_at(rdev, new_ps); + sumo_force_nbp_state(rdev, new_ps); sumo_set_forced_mode_disabled(rdev); sumo_set_forced_mode_enabled(rdev); sumo_set_forced_mode_disabled(rdev); - sumo_post_notify_alt_vddnb_change(rdev); + sumo_post_notify_alt_vddnb_change(rdev, new_ps, old_ps); } if (pi->enable_boost) - sumo_enable_boost(rdev, true); + sumo_enable_boost(rdev, new_ps, true); if (pi->enable_dpm) - sumo_set_uvd_clock_after_set_eng_clock(rdev); + sumo_set_uvd_clock_after_set_eng_clock(rdev, new_ps, old_ps);
return 0; }
From: Alex Deucher alexander.deucher@amd.com
Needed to properly handle dynamic state adjustment.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/trinity_dpm.c | 93 +++++++++++++++++++--------------- 1 files changed, 52 insertions(+), 41 deletions(-)
diff --git a/drivers/gpu/drm/radeon/trinity_dpm.c b/drivers/gpu/drm/radeon/trinity_dpm.c index 0c1b50a..103efbc 100644 --- a/drivers/gpu/drm/radeon/trinity_dpm.c +++ b/drivers/gpu/drm/radeon/trinity_dpm.c @@ -337,7 +337,9 @@ static const u32 trinity_override_mgpg_sequences[] = static void trinity_program_clk_gating_hw_sequence(struct radeon_device *rdev, const u32 *seq, u32 count); static void trinity_override_dynamic_mg_powergating(struct radeon_device *rdev); -static void trinity_apply_state_adjust_rules(struct radeon_device *rdev); +static void trinity_apply_state_adjust_rules(struct radeon_device *rdev, + struct radeon_ps *new_rps, + struct radeon_ps *old_rps);
struct trinity_ps *trinity_get_ps(struct radeon_ps *rps) { @@ -830,18 +832,21 @@ static void trinity_unforce_levels(struct radeon_device *rdev) trinity_dpm_no_forced_level(rdev); }
-static void trinity_update_current_power_levels(struct radeon_device *rdev) +static void trinity_update_current_power_levels(struct radeon_device *rdev, + struct radeon_ps *rps) { - struct trinity_ps *new_ps = trinity_get_ps(rdev->pm.dpm.requested_ps); + struct trinity_ps *new_ps = trinity_get_ps(rps); struct trinity_power_info *pi = trinity_get_pi(rdev);
pi->current_ps = *new_ps; }
-static void trinity_program_power_levels_0_to_n(struct radeon_device *rdev) +static void trinity_program_power_levels_0_to_n(struct radeon_device *rdev, + struct radeon_ps *new_rps, + struct radeon_ps *old_rps) { - struct trinity_ps *new_ps = trinity_get_ps(rdev->pm.dpm.requested_ps); - struct trinity_ps *old_ps = trinity_get_ps(rdev->pm.dpm.current_ps); + struct trinity_ps *new_ps = trinity_get_ps(new_rps); + struct trinity_ps *old_ps = trinity_get_ps(old_rps); u32 i; u32 n_current_state_levels = (old_ps == NULL) ? 1 : old_ps->num_levels;
@@ -919,19 +924,19 @@ static bool trinity_uvd_clocks_equal(struct radeon_ps *rps1, }
static void trinity_setup_uvd_clocks(struct radeon_device *rdev, - struct radeon_ps *current_rps, - struct radeon_ps *new_rps) + struct radeon_ps *new_rps, + struct radeon_ps *old_rps) { struct trinity_power_info *pi = trinity_get_pi(rdev);
if (pi->uvd_dpm) { if (trinity_uvd_clocks_zero(new_rps) && - !trinity_uvd_clocks_zero(current_rps)) { + !trinity_uvd_clocks_zero(old_rps)) { trinity_setup_uvd_dpm_interval(rdev, 0); } else if (!trinity_uvd_clocks_zero(new_rps)) { trinity_setup_uvd_clock_table(rdev, new_rps);
- if (trinity_uvd_clocks_zero(current_rps)) { + if (trinity_uvd_clocks_zero(old_rps)) { u32 tmp = RREG32(CG_MISC_REG); tmp &= 0xfffffffd; WREG32(CG_MISC_REG, tmp); @@ -944,37 +949,39 @@ static void trinity_setup_uvd_clocks(struct radeon_device *rdev, trinity_uvd_dpm_config(rdev); } else { if (trinity_uvd_clocks_zero(new_rps) || - trinity_uvd_clocks_equal(new_rps, current_rps)) + trinity_uvd_clocks_equal(new_rps, old_rps)) return;
radeon_set_uvd_clocks(rdev, new_rps->vclk, new_rps->dclk); } }
-static void trinity_set_uvd_clock_before_set_eng_clock(struct radeon_device *rdev) +static void trinity_set_uvd_clock_before_set_eng_clock(struct radeon_device *rdev, + struct radeon_ps *new_rps, + struct radeon_ps *old_rps) { - struct trinity_ps *new_ps = trinity_get_ps(rdev->pm.dpm.requested_ps); - struct trinity_ps *current_ps = trinity_get_ps(rdev->pm.dpm.current_ps); + struct trinity_ps *new_ps = trinity_get_ps(new_rps); + struct trinity_ps *current_ps = trinity_get_ps(new_rps);
if (new_ps->levels[new_ps->num_levels - 1].sclk >= current_ps->levels[current_ps->num_levels - 1].sclk) return;
- trinity_setup_uvd_clocks(rdev, rdev->pm.dpm.current_ps, - rdev->pm.dpm.requested_ps); + trinity_setup_uvd_clocks(rdev, new_rps, old_rps); }
-static void trinity_set_uvd_clock_after_set_eng_clock(struct radeon_device *rdev) +static void trinity_set_uvd_clock_after_set_eng_clock(struct radeon_device *rdev, + struct radeon_ps *new_rps, + struct radeon_ps *old_rps) { - struct trinity_ps *new_ps = trinity_get_ps(rdev->pm.dpm.requested_ps); - struct trinity_ps *current_ps = trinity_get_ps(rdev->pm.dpm.current_ps); + struct trinity_ps *new_ps = trinity_get_ps(new_rps); + struct trinity_ps *current_ps = trinity_get_ps(old_rps);
if (new_ps->levels[new_ps->num_levels - 1].sclk < current_ps->levels[current_ps->num_levels - 1].sclk) return;
- trinity_setup_uvd_clocks(rdev, rdev->pm.dpm.current_ps, - rdev->pm.dpm.requested_ps); + trinity_setup_uvd_clocks(rdev, new_rps, old_rps); }
static void trinity_program_ttt(struct radeon_device *rdev) @@ -1102,10 +1109,11 @@ static void trinity_get_min_sclk_divider(struct radeon_device *rdev) (RREG32_SMC(CC_SMU_MISC_FUSES) & MinSClkDid_MASK) >> MinSClkDid_SHIFT; }
-static void trinity_setup_nbp_sim(struct radeon_device *rdev) +static void trinity_setup_nbp_sim(struct radeon_device *rdev, + struct radeon_ps *rps) { struct trinity_power_info *pi = trinity_get_pi(rdev); - struct trinity_ps *new_ps = trinity_get_ps(rdev->pm.dpm.requested_ps); + struct trinity_ps *new_ps = trinity_get_ps(rps); u32 nbpsconfig;
if (pi->sys_info.nb_dpm_enable) { @@ -1122,21 +1130,23 @@ static void trinity_setup_nbp_sim(struct radeon_device *rdev) int trinity_dpm_set_power_state(struct radeon_device *rdev) { struct trinity_power_info *pi = trinity_get_pi(rdev); + struct radeon_ps *new_ps = rdev->pm.dpm.requested_ps; + struct radeon_ps *old_ps = rdev->pm.dpm.current_ps;
- trinity_apply_state_adjust_rules(rdev); - trinity_update_current_power_levels(rdev); + trinity_apply_state_adjust_rules(rdev, new_ps, old_ps); + trinity_update_current_power_levels(rdev, new_ps);
trinity_acquire_mutex(rdev); if (pi->enable_dpm) { - trinity_set_uvd_clock_before_set_eng_clock(rdev); + trinity_set_uvd_clock_before_set_eng_clock(rdev, new_ps, old_ps); trinity_enable_power_level_0(rdev); trinity_force_level_0(rdev); trinity_wait_for_level_0(rdev); - trinity_setup_nbp_sim(rdev); - trinity_program_power_levels_0_to_n(rdev); + trinity_setup_nbp_sim(rdev, new_ps); + trinity_program_power_levels_0_to_n(rdev, new_ps, old_ps); trinity_force_level_0(rdev); trinity_unforce_levels(rdev); - trinity_set_uvd_clock_after_set_eng_clock(rdev); + trinity_set_uvd_clock_after_set_eng_clock(rdev, new_ps, old_ps); } trinity_release_mutex(rdev);
@@ -1366,11 +1376,12 @@ static void trinity_adjust_uvd_state(struct radeon_device *rdev,
-static void trinity_apply_state_adjust_rules(struct radeon_device *rdev) +static void trinity_apply_state_adjust_rules(struct radeon_device *rdev, + struct radeon_ps *new_rps, + struct radeon_ps *old_rps) { - struct radeon_ps *rps = rdev->pm.dpm.requested_ps; - struct trinity_ps *ps = trinity_get_ps(rps); - struct trinity_ps *current_ps = trinity_get_ps(rdev->pm.dpm.current_ps); + struct trinity_ps *ps = trinity_get_ps(new_rps); + struct trinity_ps *current_ps = trinity_get_ps(old_rps); struct trinity_power_info *pi = trinity_get_pi(rdev); u32 min_voltage = 0; /* ??? */ u32 min_sclk = pi->sys_info.min_sclk; /* XXX check against disp reqs */ @@ -1384,10 +1395,10 @@ static void trinity_apply_state_adjust_rules(struct radeon_device *rdev) rdev->pm.dpm.hw_ps.ps_priv = &pi->hw_ps; ps = &pi->hw_ps;
- if (rps->class & ATOM_PPLIB_CLASSIFICATION_THERMAL) + if (new_rps->class & ATOM_PPLIB_CLASSIFICATION_THERMAL) return trinity_patch_thermal_state(rdev, ps, current_ps);
- trinity_adjust_uvd_state(rdev, rps); + trinity_adjust_uvd_state(rdev, new_rps);
for (i = 0; i < ps->num_levels; i++) { if (ps->levels[i].vddc_index < min_voltage) @@ -1410,8 +1421,8 @@ static void trinity_apply_state_adjust_rules(struct radeon_device *rdev) trinity_calculate_vce_wm(rdev, ps->levels[0].sclk); }
- if ((rps->class & (ATOM_PPLIB_CLASSIFICATION_HDSTATE | ATOM_PPLIB_CLASSIFICATION_SDSTATE)) || - ((rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) == ATOM_PPLIB_CLASSIFICATION_UI_BATTERY)) + if ((new_rps->class & (ATOM_PPLIB_CLASSIFICATION_HDSTATE | ATOM_PPLIB_CLASSIFICATION_SDSTATE)) || + ((new_rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) == ATOM_PPLIB_CLASSIFICATION_UI_BATTERY)) ps->bapm_flags |= TRINITY_POWERSTATE_FLAGS_BAPM_DISABLE;
if (pi->sys_info.nb_dpm_enable) { @@ -1420,10 +1431,10 @@ static void trinity_apply_state_adjust_rules(struct radeon_device *rdev) ps->DpmXNbPsLo = 0x2; ps->DpmXNbPsHi = 0x1;
- if ((rps->class & (ATOM_PPLIB_CLASSIFICATION_HDSTATE | ATOM_PPLIB_CLASSIFICATION_SDSTATE)) || - ((rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) == ATOM_PPLIB_CLASSIFICATION_UI_BATTERY)) { - force_high = ((rps->class & ATOM_PPLIB_CLASSIFICATION_HDSTATE) || - ((rps->class & ATOM_PPLIB_CLASSIFICATION_SDSTATE) && + if ((new_rps->class & (ATOM_PPLIB_CLASSIFICATION_HDSTATE | ATOM_PPLIB_CLASSIFICATION_SDSTATE)) || + ((new_rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) == ATOM_PPLIB_CLASSIFICATION_UI_BATTERY)) { + force_high = ((new_rps->class & ATOM_PPLIB_CLASSIFICATION_HDSTATE) || + ((new_rps->class & ATOM_PPLIB_CLASSIFICATION_SDSTATE) && (pi->sys_info.uma_channel_number == 1))); force_high = (num_active_displays >= 3) || force_high; ps->Dpm0PgNbPsLo = force_high ? 0x2 : 0x3;
From: Alex Deucher alexander.deucher@amd.com
Needed to properly handle dynamic state adjustment.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/radeon.h | 4 ++++ drivers/gpu/drm/radeon/radeon_pm.c | 11 +++++++++++ 2 files changed, 15 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 16c7d52..657d224 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1616,7 +1616,9 @@ struct radeon_asic { void (*setup_asic)(struct radeon_device *rdev); int (*enable)(struct radeon_device *rdev); void (*disable)(struct radeon_device *rdev); + int (*pre_set_power_state)(struct radeon_device *rdev); int (*set_power_state)(struct radeon_device *rdev); + void (*post_set_power_state)(struct radeon_device *rdev); void (*display_configuration_changed)(struct radeon_device *rdev); void (*fini)(struct radeon_device *rdev); u32 (*get_sclk)(struct radeon_device *rdev, bool low); @@ -2326,7 +2328,9 @@ void radeon_ring_write(struct radeon_ring *ring, uint32_t v); #define radeon_dpm_setup_asic(rdev) rdev->asic->dpm.setup_asic((rdev)) #define radeon_dpm_enable(rdev) rdev->asic->dpm.enable((rdev)) #define radeon_dpm_disable(rdev) rdev->asic->dpm.disable((rdev)) +#define radeon_dpm_pre_set_power_state(rdev) rdev->asic->dpm.pre_set_power_state((rdev)) #define radeon_dpm_set_power_state(rdev) rdev->asic->dpm.set_power_state((rdev)) +#define radeon_dpm_post_set_power_state(rdev) rdev->asic->dpm.post_set_power_state((rdev)) #define radeon_dpm_display_configuration_changed(rdev) rdev->asic->dpm.display_configuration_changed((rdev)) #define radeon_dpm_fini(rdev) rdev->asic->dpm.fini((rdev)) #define radeon_dpm_get_sclk(rdev, l) rdev->asic->dpm.get_sclk((rdev), (l)) diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 01ff18b..a50efb0 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -703,6 +703,7 @@ static void radeon_dpm_change_power_state_locked(struct radeon_device *rdev) int i; struct radeon_ps *ps; enum radeon_pm_state_type dpm_state; + int ret;
/* if dpm init failed */ if (!rdev->pm.dpm_enabled) @@ -769,6 +770,12 @@ static void radeon_dpm_change_power_state_locked(struct radeon_device *rdev) down_write(&rdev->pm.mclk_lock); mutex_lock(&rdev->ring_lock);
+ if (rdev->asic->dpm.pre_set_power_state) { + ret = radeon_dpm_pre_set_power_state(rdev); + if (ret) + goto done; + } + /* update display watermarks based on new power state */ radeon_bandwidth_update(rdev); /* update displays */ @@ -790,6 +797,10 @@ static void radeon_dpm_change_power_state_locked(struct radeon_device *rdev) /* update current power state */ rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps;
+ if (rdev->asic->dpm.post_set_power_state) + radeon_dpm_post_set_power_state(rdev); + +done: mutex_unlock(&rdev->ring_lock); up_write(&rdev->pm.mclk_lock); mutex_unlock(&rdev->ddev->struct_mutex);
From: Alex Deucher alexander.deucher@amd.com
For r6xx-evergreen, they are no-ops as they don't support any dynamic state adjustment.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/r600_dpm.c | 10 ++++++++++ drivers/gpu/drm/radeon/radeon_asic.c | 8 ++++++++ drivers/gpu/drm/radeon/radeon_asic.h | 2 ++ 3 files changed, 20 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/r600_dpm.c b/drivers/gpu/drm/radeon/r600_dpm.c index c9f9647..bcb1967 100644 --- a/drivers/gpu/drm/radeon/r600_dpm.c +++ b/drivers/gpu/drm/radeon/r600_dpm.c @@ -662,6 +662,16 @@ void r600_stop_dpm(struct radeon_device *rdev) r600_dynamicpm_enable(rdev, false); }
+int r600_dpm_pre_set_power_state(struct radeon_device *rdev) +{ + return 0; +} + +void r600_dpm_post_set_power_state(struct radeon_device *rdev) +{ + +} + bool r600_is_uvd_state(u32 class, u32 class2) { if (class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE) diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index e6703ef..5014bd1 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1110,7 +1110,9 @@ static struct radeon_asic rv6xx_asic = { .setup_asic = &rv6xx_setup_asic, .enable = &rv6xx_dpm_enable, .disable = &rv6xx_dpm_disable, + .pre_set_power_state = &r600_dpm_pre_set_power_state, .set_power_state = &rv6xx_dpm_set_power_state, + .post_set_power_state = &r600_dpm_post_set_power_state, .display_configuration_changed = &rv6xx_dpm_display_configuration_changed, .fini = &rv6xx_dpm_fini, .get_sclk = &rv6xx_dpm_get_sclk, @@ -1211,7 +1213,9 @@ static struct radeon_asic rs780_asic = { .setup_asic = &rs780_dpm_setup_asic, .enable = &rs780_dpm_enable, .disable = &rs780_dpm_disable, + .pre_set_power_state = &r600_dpm_pre_set_power_state, .set_power_state = &rs780_dpm_set_power_state, + .post_set_power_state = &r600_dpm_post_set_power_state, .display_configuration_changed = &rs780_dpm_display_configuration_changed, .fini = &rs780_dpm_fini, .get_sclk = &rs780_dpm_get_sclk, @@ -1322,7 +1326,9 @@ static struct radeon_asic rv770_asic = { .setup_asic = &rv770_dpm_setup_asic, .enable = &rv770_dpm_enable, .disable = &rv770_dpm_disable, + .pre_set_power_state = &r600_dpm_pre_set_power_state, .set_power_state = &rv770_dpm_set_power_state, + .post_set_power_state = &r600_dpm_post_set_power_state, .display_configuration_changed = &rv770_dpm_display_configuration_changed, .fini = &rv770_dpm_fini, .get_sclk = &rv770_dpm_get_sclk, @@ -1433,7 +1439,9 @@ static struct radeon_asic evergreen_asic = { .setup_asic = &cypress_dpm_setup_asic, .enable = &cypress_dpm_enable, .disable = &cypress_dpm_disable, + .pre_set_power_state = &r600_dpm_pre_set_power_state, .set_power_state = &cypress_dpm_set_power_state, + .post_set_power_state = &r600_dpm_post_set_power_state, .display_configuration_changed = &cypress_dpm_display_configuration_changed, .fini = &cypress_dpm_fini, .get_sclk = &rv770_dpm_get_sclk, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 2fef64d..d7d4662 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -396,6 +396,8 @@ int r600_mc_wait_for_idle(struct radeon_device *rdev); u32 r600_get_xclk(struct radeon_device *rdev); uint64_t r600_get_gpu_clock_counter(struct radeon_device *rdev); int rv6xx_get_temp(struct radeon_device *rdev); +int r600_dpm_pre_set_power_state(struct radeon_device *rdev); +void r600_dpm_post_set_power_state(struct radeon_device *rdev); /* rv6xx dpm */ int rv6xx_dpm_init(struct radeon_device *rdev); int rv6xx_dpm_enable(struct radeon_device *rdev);
From: Alex Deucher alexander.deucher@amd.com
This properly implemented dynamic state adjustment by using a working copy of the requested and current power states.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/radeon_asic.c | 2 + drivers/gpu/drm/radeon/radeon_asic.h | 2 + drivers/gpu/drm/radeon/sumo_dpm.c | 74 ++++++++++++++++++++++++--------- drivers/gpu/drm/radeon/sumo_dpm.h | 6 ++- 4 files changed, 62 insertions(+), 22 deletions(-)
diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index 5014bd1..c5d70da 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1552,7 +1552,9 @@ static struct radeon_asic sumo_asic = { .setup_asic = &sumo_dpm_setup_asic, .enable = &sumo_dpm_enable, .disable = &sumo_dpm_disable, + .pre_set_power_state = &sumo_dpm_pre_set_power_state, .set_power_state = &sumo_dpm_set_power_state, + .post_set_power_state = &sumo_dpm_post_set_power_state, .display_configuration_changed = &sumo_dpm_display_configuration_changed, .fini = &sumo_dpm_fini, .get_sclk = &sumo_dpm_get_sclk, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index d7d4662..817fc26 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -541,7 +541,9 @@ void btc_dpm_fini(struct radeon_device *rdev); int sumo_dpm_init(struct radeon_device *rdev); int sumo_dpm_enable(struct radeon_device *rdev); void sumo_dpm_disable(struct radeon_device *rdev); +int sumo_dpm_pre_set_power_state(struct radeon_device *rdev); int sumo_dpm_set_power_state(struct radeon_device *rdev); +void sumo_dpm_post_set_power_state(struct radeon_device *rdev); void sumo_dpm_setup_asic(struct radeon_device *rdev); void sumo_dpm_display_configuration_changed(struct radeon_device *rdev); void sumo_dpm_fini(struct radeon_device *rdev); diff --git a/drivers/gpu/drm/radeon/sumo_dpm.c b/drivers/gpu/drm/radeon/sumo_dpm.c index f5de850..10700e1 100644 --- a/drivers/gpu/drm/radeon/sumo_dpm.c +++ b/drivers/gpu/drm/radeon/sumo_dpm.c @@ -727,15 +727,6 @@ static void sumo_enable_boost(struct radeon_device *rdev, sumo_boost_state_enable(rdev, false); }
-static void sumo_update_current_power_levels(struct radeon_device *rdev, - struct radeon_ps *rps) -{ - struct sumo_ps *new_ps = sumo_get_ps(rps); - struct sumo_power_info *pi = sumo_get_pi(rdev); - - pi->current_ps = *new_ps; -} - static void sumo_set_forced_level(struct radeon_device *rdev, u32 index) { WREG32_P(CG_SCLK_DPM_CTRL_3, FORCE_SCLK_STATE(index), ~FORCE_SCLK_STATE_MASK); @@ -1089,11 +1080,6 @@ static void sumo_apply_state_adjust_rules(struct radeon_device *rdev, u32 sclk_in_sr = pi->sys_info.min_sclk; /* ??? */ u32 i;
- /* point to the hw copy since this function will modify the ps */ - pi->hw_ps = *ps; - rdev->pm.dpm.hw_ps.ps_priv = &pi->hw_ps; - ps = &pi->hw_ps; - if (new_rps->class & ATOM_PPLIB_CLASSIFICATION_THERMAL) return sumo_patch_thermal_state(rdev, ps, current_ps);
@@ -1192,6 +1178,28 @@ static int sumo_set_thermal_temperature_range(struct radeon_device *rdev, return 0; }
+static void sumo_update_current_ps(struct radeon_device *rdev, + struct radeon_ps *rps) +{ + struct sumo_ps *new_ps = sumo_get_ps(rps); + struct sumo_power_info *pi = sumo_get_pi(rdev); + + pi->current_rps = *rps; + pi->current_ps = *new_ps; + pi->current_rps.ps_priv = &pi->current_ps; +} + +static void sumo_update_requested_ps(struct radeon_device *rdev, + struct radeon_ps *rps) +{ + struct sumo_ps *new_ps = sumo_get_ps(rps); + struct sumo_power_info *pi = sumo_get_pi(rdev); + + pi->requested_rps = *rps; + pi->requested_ps = *new_ps; + pi->requested_rps.ps_priv = &pi->requested_ps; +} + int sumo_dpm_enable(struct radeon_device *rdev) { struct sumo_power_info *pi = sumo_get_pi(rdev); @@ -1230,6 +1238,8 @@ int sumo_dpm_enable(struct radeon_device *rdev) radeon_irq_set(rdev); }
+ sumo_update_current_ps(rdev, rdev->pm.dpm.boot_ps); + return 0; }
@@ -1252,19 +1262,34 @@ void sumo_dpm_disable(struct radeon_device *rdev) rdev->irq.dpm_thermal = false; radeon_irq_set(rdev); } + + sumo_update_current_ps(rdev, rdev->pm.dpm.boot_ps); }
-int sumo_dpm_set_power_state(struct radeon_device *rdev) +int sumo_dpm_pre_set_power_state(struct radeon_device *rdev) { struct sumo_power_info *pi = sumo_get_pi(rdev); - struct radeon_ps *new_ps = rdev->pm.dpm.requested_ps; - struct radeon_ps *old_ps = rdev->pm.dpm.current_ps; + struct radeon_ps requested_ps = *rdev->pm.dpm.requested_ps; + struct radeon_ps *new_ps = &requested_ps; + + sumo_update_requested_ps(rdev, new_ps);
if (pi->enable_dynamic_patch_ps) - sumo_apply_state_adjust_rules(rdev, new_ps, old_ps); + sumo_apply_state_adjust_rules(rdev, + &pi->requested_rps, + &pi->current_rps); + + return 0; +} + +int sumo_dpm_set_power_state(struct radeon_device *rdev) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + struct radeon_ps *new_ps = &pi->requested_rps; + struct radeon_ps *old_ps = &pi->current_rps; + if (pi->enable_dpm) sumo_set_uvd_clock_before_set_eng_clock(rdev, new_ps, old_ps); - sumo_update_current_power_levels(rdev, new_ps); if (pi->enable_boost) { sumo_enable_boost(rdev, new_ps, false); sumo_patch_boost_state(rdev, new_ps); @@ -1293,6 +1318,14 @@ int sumo_dpm_set_power_state(struct radeon_device *rdev) return 0; }
+void sumo_dpm_post_set_power_state(struct radeon_device *rdev) +{ + struct sumo_power_info *pi = sumo_get_pi(rdev); + struct radeon_ps *new_ps = &pi->requested_rps; + + sumo_update_current_ps(rdev, new_ps); +} + void sumo_dpm_reset_asic(struct radeon_device *rdev) { sumo_program_bootup_state(rdev); @@ -1751,7 +1784,8 @@ void sumo_dpm_fini(struct radeon_device *rdev)
u32 sumo_dpm_get_sclk(struct radeon_device *rdev, bool low) { - struct sumo_ps *requested_state = sumo_get_ps(rdev->pm.dpm.requested_ps); + struct sumo_power_info *pi = sumo_get_pi(rdev); + struct sumo_ps *requested_state = sumo_get_ps(&pi->requested_rps);
if (low) return requested_state->levels[0].sclk; diff --git a/drivers/gpu/drm/radeon/sumo_dpm.h b/drivers/gpu/drm/radeon/sumo_dpm.h index a40b62a..a3a7a61 100644 --- a/drivers/gpu/drm/radeon/sumo_dpm.h +++ b/drivers/gpu/drm/radeon/sumo_dpm.h @@ -116,7 +116,6 @@ struct sumo_power_info { struct sumo_pl acpi_pl; struct sumo_pl boot_pl; struct sumo_pl boost_pl; - struct sumo_ps current_ps; bool disable_gfx_power_gating_in_uvd; bool driver_nbps_policy_disable; bool enable_alt_vddnb; @@ -129,7 +128,10 @@ struct sumo_power_info { bool enable_dynamic_patch_ps; bool enable_dpm; bool enable_boost; - struct sumo_ps hw_ps; + struct radeon_ps current_rps; + struct sumo_ps current_ps; + struct radeon_ps requested_rps; + struct sumo_ps requested_ps; };
#define SUMO_UTC_DFLT_00 0x48
From: Alex Deucher alexander.deucher@amd.com
This properly implemented dynamic state adjustment by using a working copy of the requested and current power states.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/radeon_asic.c | 2 + drivers/gpu/drm/radeon/radeon_asic.h | 2 + drivers/gpu/drm/radeon/trinity_dpm.c | 73 ++++++++++++++++++++++++--------- drivers/gpu/drm/radeon/trinity_dpm.h | 6 ++- 4 files changed, 61 insertions(+), 22 deletions(-)
diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index c5d70da..c78519c 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1971,7 +1971,9 @@ static struct radeon_asic trinity_asic = { .setup_asic = &trinity_dpm_setup_asic, .enable = &trinity_dpm_enable, .disable = &trinity_dpm_disable, + .pre_set_power_state = &trinity_dpm_pre_set_power_state, .set_power_state = &trinity_dpm_set_power_state, + .post_set_power_state = &trinity_dpm_post_set_power_state, .display_configuration_changed = &trinity_dpm_display_configuration_changed, .fini = &trinity_dpm_fini, .get_sclk = &trinity_dpm_get_sclk, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 817fc26..14d992d 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -598,7 +598,9 @@ void ni_dpm_print_power_state(struct radeon_device *rdev, int trinity_dpm_init(struct radeon_device *rdev); int trinity_dpm_enable(struct radeon_device *rdev); void trinity_dpm_disable(struct radeon_device *rdev); +int trinity_dpm_pre_set_power_state(struct radeon_device *rdev); int trinity_dpm_set_power_state(struct radeon_device *rdev); +void trinity_dpm_post_set_power_state(struct radeon_device *rdev); void trinity_dpm_setup_asic(struct radeon_device *rdev); void trinity_dpm_display_configuration_changed(struct radeon_device *rdev); void trinity_dpm_fini(struct radeon_device *rdev); diff --git a/drivers/gpu/drm/radeon/trinity_dpm.c b/drivers/gpu/drm/radeon/trinity_dpm.c index 103efbc..1699e93 100644 --- a/drivers/gpu/drm/radeon/trinity_dpm.c +++ b/drivers/gpu/drm/radeon/trinity_dpm.c @@ -832,15 +832,6 @@ static void trinity_unforce_levels(struct radeon_device *rdev) trinity_dpm_no_forced_level(rdev); }
-static void trinity_update_current_power_levels(struct radeon_device *rdev, - struct radeon_ps *rps) -{ - struct trinity_ps *new_ps = trinity_get_ps(rps); - struct trinity_power_info *pi = trinity_get_pi(rdev); - - pi->current_ps = *new_ps; -} - static void trinity_program_power_levels_0_to_n(struct radeon_device *rdev, struct radeon_ps *new_rps, struct radeon_ps *old_rps) @@ -1046,6 +1037,28 @@ static int trinity_set_thermal_temperature_range(struct radeon_device *rdev, return 0; }
+static void trinity_update_current_ps(struct radeon_device *rdev, + struct radeon_ps *rps) +{ + struct trinity_ps *new_ps = trinity_get_ps(rps); + struct trinity_power_info *pi = trinity_get_pi(rdev); + + pi->current_rps = *rps; + pi->current_ps = *new_ps; + pi->current_rps.ps_priv = &pi->current_ps; +} + +static void trinity_update_requested_ps(struct radeon_device *rdev, + struct radeon_ps *rps) +{ + struct trinity_ps *new_ps = trinity_get_ps(rps); + struct trinity_power_info *pi = trinity_get_pi(rdev); + + pi->requested_rps = *rps; + pi->requested_ps = *new_ps; + pi->requested_rps.ps_priv = &pi->requested_ps; +} + int trinity_dpm_enable(struct radeon_device *rdev) { struct trinity_power_info *pi = trinity_get_pi(rdev); @@ -1077,6 +1090,8 @@ int trinity_dpm_enable(struct radeon_device *rdev) radeon_irq_set(rdev); }
+ trinity_update_current_ps(rdev, rdev->pm.dpm.boot_ps); + return 0; }
@@ -1099,6 +1114,8 @@ void trinity_dpm_disable(struct radeon_device *rdev) rdev->irq.dpm_thermal = false; radeon_irq_set(rdev); } + + trinity_update_current_ps(rdev, rdev->pm.dpm.boot_ps); }
static void trinity_get_min_sclk_divider(struct radeon_device *rdev) @@ -1127,14 +1144,26 @@ static void trinity_setup_nbp_sim(struct radeon_device *rdev, } }
-int trinity_dpm_set_power_state(struct radeon_device *rdev) +int trinity_dpm_pre_set_power_state(struct radeon_device *rdev) { struct trinity_power_info *pi = trinity_get_pi(rdev); - struct radeon_ps *new_ps = rdev->pm.dpm.requested_ps; - struct radeon_ps *old_ps = rdev->pm.dpm.current_ps; + struct radeon_ps requested_ps = *rdev->pm.dpm.requested_ps; + struct radeon_ps *new_ps = &requested_ps; + + trinity_update_requested_ps(rdev, new_ps);
- trinity_apply_state_adjust_rules(rdev, new_ps, old_ps); - trinity_update_current_power_levels(rdev, new_ps); + trinity_apply_state_adjust_rules(rdev, + &pi->requested_rps, + &pi->current_rps); + + return 0; +} + +int trinity_dpm_set_power_state(struct radeon_device *rdev) +{ + struct trinity_power_info *pi = trinity_get_pi(rdev); + struct radeon_ps *new_ps = &pi->requested_rps; + struct radeon_ps *old_ps = &pi->current_rps;
trinity_acquire_mutex(rdev); if (pi->enable_dpm) { @@ -1153,6 +1182,14 @@ int trinity_dpm_set_power_state(struct radeon_device *rdev) return 0; }
+void trinity_dpm_post_set_power_state(struct radeon_device *rdev) +{ + struct trinity_power_info *pi = trinity_get_pi(rdev); + struct radeon_ps *new_ps = &pi->requested_rps; + + trinity_update_current_ps(rdev, new_ps); +} + void trinity_dpm_setup_asic(struct radeon_device *rdev) { trinity_acquire_mutex(rdev); @@ -1390,11 +1427,6 @@ static void trinity_apply_state_adjust_rules(struct radeon_device *rdev, bool force_high; u32 num_active_displays = rdev->pm.dpm.new_active_crtc_count;
- /* point to the hw copy since this function will modify the ps */ - pi->hw_ps = *ps; - rdev->pm.dpm.hw_ps.ps_priv = &pi->hw_ps; - ps = &pi->hw_ps; - if (new_rps->class & ATOM_PPLIB_CLASSIFICATION_THERMAL) return trinity_patch_thermal_state(rdev, ps, current_ps);
@@ -1833,7 +1865,8 @@ void trinity_dpm_fini(struct radeon_device *rdev)
u32 trinity_dpm_get_sclk(struct radeon_device *rdev, bool low) { - struct trinity_ps *requested_state = trinity_get_ps(rdev->pm.dpm.requested_ps); + struct trinity_power_info *pi = trinity_get_pi(rdev); + struct trinity_ps *requested_state = trinity_get_ps(&pi->requested_rps);
if (low) return requested_state->levels[0].sclk; diff --git a/drivers/gpu/drm/radeon/trinity_dpm.h b/drivers/gpu/drm/radeon/trinity_dpm.h index c663aed..c621b84 100644 --- a/drivers/gpu/drm/radeon/trinity_dpm.h +++ b/drivers/gpu/drm/radeon/trinity_dpm.h @@ -97,7 +97,6 @@ struct trinity_power_info { u32 thermal_auto_throttling; struct trinity_sys_info sys_info; struct trinity_pl boot_pl; - struct trinity_ps current_ps; u32 min_sclk_did; bool enable_nbps_policy; bool voltage_drop_in_dce; @@ -110,7 +109,10 @@ struct trinity_power_info { bool enable_dpm; bool enable_sclk_ds; bool uvd_dpm; - struct trinity_ps hw_ps; + struct radeon_ps current_rps; + struct trinity_ps current_ps; + struct radeon_ps requested_rps; + struct trinity_ps requested_ps; };
#define TRINITY_AT_DFLT 30
From: Alex Deucher alexander.deucher@amd.com
This properly implemented dynamic state adjustment by using a working copy of the requested and current power states.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/btc_dpm.c | 81 +++++++++++++++++++++++++++++---- drivers/gpu/drm/radeon/cypress_dpm.h | 5 ++- drivers/gpu/drm/radeon/radeon_asic.c | 6 ++- drivers/gpu/drm/radeon/radeon_asic.h | 4 ++ 4 files changed, 83 insertions(+), 13 deletions(-)
diff --git a/drivers/gpu/drm/radeon/btc_dpm.c b/drivers/gpu/drm/radeon/btc_dpm.c index 30aed00..7c7d58f 100644 --- a/drivers/gpu/drm/radeon/btc_dpm.c +++ b/drivers/gpu/drm/radeon/btc_dpm.c @@ -2062,18 +2062,12 @@ static void btc_init_stutter_mode(struct radeon_device *rdev) static void btc_apply_state_adjust_rules(struct radeon_device *rdev, struct radeon_ps *rps) { - struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); struct rv7xx_ps *ps = rv770_get_ps(rps); struct radeon_clock_and_voltage_limits *max_limits; bool disable_mclk_switching; u32 mclk, sclk; u16 vddc, vddci;
- /* point to the hw copy since this function will modify the ps */ - eg_pi->hw_ps = *ps; - rdev->pm.dpm.hw_ps.ps_priv = &eg_pi->hw_ps; - ps = &eg_pi->hw_ps; - if (rdev->pm.dpm.new_active_crtc_count > 1) disable_mclk_switching = true; else @@ -2222,6 +2216,28 @@ static void btc_apply_state_adjust_rules(struct radeon_device *rdev, ps->high.flags &= ~ATOM_PPLIB_R600_FLAGS_PCIEGEN2; }
+static void btc_update_current_ps(struct radeon_device *rdev, + struct radeon_ps *rps) +{ + struct rv7xx_ps *new_ps = rv770_get_ps(rps); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + + eg_pi->current_rps = *rps; + eg_pi->current_ps = *new_ps; + eg_pi->current_rps.ps_priv = &eg_pi->current_ps; +} + +static void btc_update_requested_ps(struct radeon_device *rdev, + struct radeon_ps *rps) +{ + struct rv7xx_ps *new_ps = rv770_get_ps(rps); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + + eg_pi->requested_rps = *rps; + eg_pi->requested_ps = *new_ps; + eg_pi->requested_rps.ps_priv = &eg_pi->requested_ps; +} + void btc_dpm_reset_asic(struct radeon_device *rdev) { rv770_restrict_performance_levels_before_switch(rdev); @@ -2230,13 +2246,24 @@ void btc_dpm_reset_asic(struct radeon_device *rdev) rv770_set_boot_state(rdev); }
-int btc_dpm_set_power_state(struct radeon_device *rdev) +int btc_dpm_pre_set_power_state(struct radeon_device *rdev) { struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); - struct radeon_ps *new_ps = rdev->pm.dpm.requested_ps; - struct radeon_ps *old_ps = rdev->pm.dpm.current_ps; + struct radeon_ps requested_ps = *rdev->pm.dpm.requested_ps; + struct radeon_ps *new_ps = &requested_ps; + + btc_update_requested_ps(rdev, new_ps); + + btc_apply_state_adjust_rules(rdev, &eg_pi->requested_rps);
- btc_apply_state_adjust_rules(rdev, new_ps); + return 0; +} + +int btc_dpm_set_power_state(struct radeon_device *rdev) +{ + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct radeon_ps *new_ps = &eg_pi->requested_rps; + struct radeon_ps *old_ps = &eg_pi->current_rps;
btc_disable_ulv(rdev); btc_set_boot_state_timing(rdev); @@ -2269,6 +2296,14 @@ int btc_dpm_set_power_state(struct radeon_device *rdev) return 0; }
+void btc_dpm_post_set_power_state(struct radeon_device *rdev) +{ + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct radeon_ps *new_ps = &eg_pi->requested_rps; + + btc_update_current_ps(rdev, new_ps); +} + int btc_dpm_enable(struct radeon_device *rdev) { struct rv7xx_power_info *pi = rv770_get_pi(rdev); @@ -2364,6 +2399,8 @@ int btc_dpm_enable(struct radeon_device *rdev)
btc_init_stutter_mode(rdev);
+ btc_update_current_ps(rdev, rdev->pm.dpm.boot_ps); + return 0; };
@@ -2402,6 +2439,8 @@ void btc_dpm_disable(struct radeon_device *rdev) btc_reset_to_default(rdev); btc_stop_smc(rdev); cypress_enable_spread_spectrum(rdev, false); + + btc_update_current_ps(rdev, rdev->pm.dpm.boot_ps); }
void btc_dpm_setup_asic(struct radeon_device *rdev) @@ -2586,3 +2625,25 @@ void btc_dpm_fini(struct radeon_device *rdev) kfree(rdev->pm.dpm.priv); r600_free_extended_power_table(rdev); } + +u32 btc_dpm_get_sclk(struct radeon_device *rdev, bool low) +{ + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct rv7xx_ps *requested_state = rv770_get_ps(&eg_pi->requested_rps); + + if (low) + return requested_state->low.sclk; + else + return requested_state->high.sclk; +} + +u32 btc_dpm_get_mclk(struct radeon_device *rdev, bool low) +{ + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct rv7xx_ps *requested_state = rv770_get_ps(&eg_pi->requested_rps); + + if (low) + return requested_state->low.mclk; + else + return requested_state->high.mclk; +} diff --git a/drivers/gpu/drm/radeon/cypress_dpm.h b/drivers/gpu/drm/radeon/cypress_dpm.h index 5b19364..4c3f18c 100644 --- a/drivers/gpu/drm/radeon/cypress_dpm.h +++ b/drivers/gpu/drm/radeon/cypress_dpm.h @@ -88,7 +88,10 @@ struct evergreen_power_info { struct at ats[2]; /* smc offsets */ u16 mc_reg_table_start; - struct rv7xx_ps hw_ps; + struct radeon_ps current_rps; + struct rv7xx_ps current_ps; + struct radeon_ps requested_rps; + struct rv7xx_ps requested_ps; };
#define CYPRESS_HASI_DFLT 400000 diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index c78519c..25eeaa4 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1665,11 +1665,13 @@ static struct radeon_asic btc_asic = { .setup_asic = &btc_dpm_setup_asic, .enable = &btc_dpm_enable, .disable = &btc_dpm_disable, + .pre_set_power_state = &btc_dpm_pre_set_power_state, .set_power_state = &btc_dpm_set_power_state, + .post_set_power_state = &btc_dpm_post_set_power_state, .display_configuration_changed = &cypress_dpm_display_configuration_changed, .fini = &btc_dpm_fini, - .get_sclk = &rv770_dpm_get_sclk, - .get_mclk = &rv770_dpm_get_mclk, + .get_sclk = &btc_dpm_get_sclk, + .get_mclk = &btc_dpm_get_mclk, .print_power_state = &rv770_dpm_print_power_state, }, .pflip = { diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 14d992d..696d6e9 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -536,8 +536,12 @@ int btc_dpm_init(struct radeon_device *rdev); void btc_dpm_setup_asic(struct radeon_device *rdev); int btc_dpm_enable(struct radeon_device *rdev); void btc_dpm_disable(struct radeon_device *rdev); +int btc_dpm_pre_set_power_state(struct radeon_device *rdev); int btc_dpm_set_power_state(struct radeon_device *rdev); +void btc_dpm_post_set_power_state(struct radeon_device *rdev); void btc_dpm_fini(struct radeon_device *rdev); +u32 btc_dpm_get_sclk(struct radeon_device *rdev, bool low); +u32 btc_dpm_get_mclk(struct radeon_device *rdev, bool low); int sumo_dpm_init(struct radeon_device *rdev); int sumo_dpm_enable(struct radeon_device *rdev); void sumo_dpm_disable(struct radeon_device *rdev);
From: Alex Deucher alexander.deucher@amd.com
This properly implemented dynamic state adjustment by using a working copy of the requested and current power states.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/ni_dpm.c | 65 ++++++++++++++++++++++++++++------ drivers/gpu/drm/radeon/ni_dpm.h | 3 +- drivers/gpu/drm/radeon/radeon_asic.c | 2 + drivers/gpu/drm/radeon/radeon_asic.h | 2 + 4 files changed, 60 insertions(+), 12 deletions(-)
diff --git a/drivers/gpu/drm/radeon/ni_dpm.c b/drivers/gpu/drm/radeon/ni_dpm.c index ebc9837..01ecb80 100644 --- a/drivers/gpu/drm/radeon/ni_dpm.c +++ b/drivers/gpu/drm/radeon/ni_dpm.c @@ -791,7 +791,6 @@ static void ni_calculate_leakage_for_v_and_t(struct radeon_device *rdev, static void ni_apply_state_adjust_rules(struct radeon_device *rdev, struct radeon_ps *rps) { - struct ni_power_info *ni_pi = ni_get_pi(rdev); struct ni_ps *ps = ni_get_ps(rps); struct radeon_clock_and_voltage_limits *max_limits; bool disable_mclk_switching; @@ -799,11 +798,6 @@ static void ni_apply_state_adjust_rules(struct radeon_device *rdev, u16 vddc, vddci; int i;
- /* point to the hw copy since this function will modify the ps */ - ni_pi->hw_ps = *ps; - rdev->pm.dpm.hw_ps.ps_priv = &ni_pi->hw_ps; - ps = &ni_pi->hw_ps; - if (rdev->pm.dpm.new_active_crtc_count > 1) disable_mclk_switching = true; else @@ -3502,6 +3496,30 @@ void ni_dpm_setup_asic(struct radeon_device *rdev) rv770_enable_acpi_pm(rdev); }
+static void ni_update_current_ps(struct radeon_device *rdev, + struct radeon_ps *rps) +{ + struct ni_ps *new_ps = ni_get_ps(rps); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct ni_power_info *ni_pi = ni_get_pi(rdev); + + eg_pi->current_rps = *rps; + ni_pi->current_ps = *new_ps; + eg_pi->current_rps.ps_priv = &ni_pi->current_ps; +} + +static void ni_update_requested_ps(struct radeon_device *rdev, + struct radeon_ps *rps) +{ + struct ni_ps *new_ps = ni_get_ps(rps); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct ni_power_info *ni_pi = ni_get_pi(rdev); + + eg_pi->requested_rps = *rps; + ni_pi->requested_ps = *new_ps; + eg_pi->requested_rps.ps_priv = &ni_pi->requested_ps; +} + int ni_dpm_enable(struct radeon_device *rdev) { struct rv7xx_power_info *pi = rv770_get_pi(rdev); @@ -3576,6 +3594,8 @@ int ni_dpm_enable(struct radeon_device *rdev)
rv770_enable_auto_throttle_source(rdev, RADEON_DPM_AUTO_THROTTLE_SRC_THERMAL, true);
+ ni_update_current_ps(rdev, boot_ps); + return 0; }
@@ -3613,6 +3633,8 @@ void ni_dpm_disable(struct radeon_device *rdev) btc_reset_to_default(rdev); ni_stop_smc(rdev); ni_force_switch_to_arb_f0(rdev); + + ni_update_current_ps(rdev, boot_ps); }
int ni_power_control_set_level(struct radeon_device *rdev) @@ -3628,14 +3650,25 @@ int ni_power_control_set_level(struct radeon_device *rdev) return 0; }
+int ni_dpm_pre_set_power_state(struct radeon_device *rdev) +{ + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct radeon_ps requested_ps = *rdev->pm.dpm.requested_ps; + struct radeon_ps *new_ps = &requested_ps; + + ni_update_requested_ps(rdev, new_ps); + + ni_apply_state_adjust_rules(rdev, &eg_pi->requested_rps); + + return 0; +} + int ni_dpm_set_power_state(struct radeon_device *rdev) { struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); - struct radeon_ps *new_ps = rdev->pm.dpm.requested_ps; + struct radeon_ps *new_ps = &eg_pi->requested_rps; int ret;
- ni_apply_state_adjust_rules(rdev, new_ps); - ni_restrict_performance_levels_before_switch(rdev); ni_enable_power_containment(rdev, new_ps, false); ni_enable_smc_cac(rdev, new_ps, false); @@ -3657,6 +3690,14 @@ int ni_dpm_set_power_state(struct radeon_device *rdev) return 0; }
+void ni_dpm_post_set_power_state(struct radeon_device *rdev) +{ + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct radeon_ps *new_ps = &eg_pi->requested_rps; + + ni_update_current_ps(rdev, new_ps); +} + void ni_dpm_reset_asic(struct radeon_device *rdev) { ni_restrict_performance_levels_before_switch(rdev); @@ -4078,7 +4119,8 @@ void ni_dpm_print_power_state(struct radeon_device *rdev,
u32 ni_dpm_get_sclk(struct radeon_device *rdev, bool low) { - struct ni_ps *requested_state = ni_get_ps(rdev->pm.dpm.requested_ps); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct ni_ps *requested_state = ni_get_ps(&eg_pi->requested_rps);
if (low) return requested_state->performance_levels[0].sclk; @@ -4088,7 +4130,8 @@ u32 ni_dpm_get_sclk(struct radeon_device *rdev, bool low)
u32 ni_dpm_get_mclk(struct radeon_device *rdev, bool low) { - struct ni_ps *requested_state = ni_get_ps(rdev->pm.dpm.requested_ps); + struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); + struct ni_ps *requested_state = ni_get_ps(&eg_pi->requested_rps);
if (low) return requested_state->performance_levels[0].mclk; diff --git a/drivers/gpu/drm/radeon/ni_dpm.h b/drivers/gpu/drm/radeon/ni_dpm.h index e10f747..59c1692 100644 --- a/drivers/gpu/drm/radeon/ni_dpm.h +++ b/drivers/gpu/drm/radeon/ni_dpm.h @@ -202,7 +202,8 @@ struct ni_power_info { const struct ni_cac_weights *cac_weights; u8 lta_window_size; u8 lts_truncate; - struct ni_ps hw_ps; + struct ni_ps current_ps; + struct ni_ps requested_ps; /* scratch structs */ SMC_NIslands_MCRegisters smc_mc_reg_table; NISLANDS_SMC_STATETABLE smc_statetable; diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index 25eeaa4..e7162b1 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1821,7 +1821,9 @@ static struct radeon_asic cayman_asic = { .setup_asic = &ni_dpm_setup_asic, .enable = &ni_dpm_enable, .disable = &ni_dpm_disable, + .pre_set_power_state = &ni_dpm_pre_set_power_state, .set_power_state = &ni_dpm_set_power_state, + .post_set_power_state = &ni_dpm_post_set_power_state, .display_configuration_changed = &cypress_dpm_display_configuration_changed, .fini = &ni_dpm_fini, .get_sclk = &ni_dpm_get_sclk, diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 696d6e9..7b898bc 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -593,7 +593,9 @@ int ni_dpm_init(struct radeon_device *rdev); void ni_dpm_setup_asic(struct radeon_device *rdev); int ni_dpm_enable(struct radeon_device *rdev); void ni_dpm_disable(struct radeon_device *rdev); +int ni_dpm_pre_set_power_state(struct radeon_device *rdev); int ni_dpm_set_power_state(struct radeon_device *rdev); +void ni_dpm_post_set_power_state(struct radeon_device *rdev); void ni_dpm_fini(struct radeon_device *rdev); u32 ni_dpm_get_sclk(struct radeon_device *rdev, bool low); u32 ni_dpm_get_mclk(struct radeon_device *rdev, bool low);
From: Alex Deucher alexander.deucher@amd.com
Now that the proper fix has been implemented I can remove the last remnants of the initial implementation.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/radeon.h | 1 - drivers/gpu/drm/radeon/radeon_pm.c | 24 +++++------------------- 2 files changed, 5 insertions(+), 20 deletions(-)
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 657d224..0c6676a 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -1307,7 +1307,6 @@ struct radeon_dpm { struct radeon_ps *boot_ps; /* default uvd power state */ struct radeon_ps *uvd_ps; - struct radeon_ps hw_ps; enum radeon_pm_state_type state; enum radeon_pm_state_type user_state; u32 platform_caps; diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index a50efb0..c8688b3 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -687,17 +687,6 @@ restart_search: return NULL; }
-static void radeon_dpm_update_requested_ps(struct radeon_device *rdev, - struct radeon_ps *ps) -{ - /* copy the ps to the hw ps and point the requested ps - * at the hw state in case the driver wants to modify - * the state dynamically. - */ - rdev->pm.dpm.hw_ps = *ps; - rdev->pm.dpm.requested_ps = &rdev->pm.dpm.hw_ps; -} - static void radeon_dpm_change_power_state_locked(struct radeon_device *rdev) { int i; @@ -719,7 +708,7 @@ static void radeon_dpm_change_power_state_locked(struct radeon_device *rdev)
ps = radeon_dpm_pick_power_state(rdev, dpm_state); if (ps) - radeon_dpm_update_requested_ps(rdev, ps); + rdev->pm.dpm.requested_ps = ps; else return;
@@ -770,11 +759,9 @@ static void radeon_dpm_change_power_state_locked(struct radeon_device *rdev) down_write(&rdev->pm.mclk_lock); mutex_lock(&rdev->ring_lock);
- if (rdev->asic->dpm.pre_set_power_state) { - ret = radeon_dpm_pre_set_power_state(rdev); - if (ret) - goto done; - } + ret = radeon_dpm_pre_set_power_state(rdev); + if (ret) + goto done;
/* update display watermarks based on new power state */ radeon_bandwidth_update(rdev); @@ -797,8 +784,7 @@ static void radeon_dpm_change_power_state_locked(struct radeon_device *rdev) /* update current power state */ rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps;
- if (rdev->asic->dpm.post_set_power_state) - radeon_dpm_post_set_power_state(rdev); + radeon_dpm_post_set_power_state(rdev);
done: mutex_unlock(&rdev->ring_lock);
From: Alex Deucher alexander.deucher@amd.com
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/ni_dpm.c | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/radeon/ni_dpm.c b/drivers/gpu/drm/radeon/ni_dpm.c index 01ecb80..a88f0cb 100644 --- a/drivers/gpu/drm/radeon/ni_dpm.c +++ b/drivers/gpu/drm/radeon/ni_dpm.c @@ -3667,9 +3667,11 @@ int ni_dpm_set_power_state(struct radeon_device *rdev) { struct evergreen_power_info *eg_pi = evergreen_get_pi(rdev); struct radeon_ps *new_ps = &eg_pi->requested_rps; + struct radeon_ps *old_ps = &eg_pi->current_rps; int ret;
ni_restrict_performance_levels_before_switch(rdev); + rv770_set_uvd_clock_before_set_eng_clock(rdev, new_ps, old_ps); ni_enable_power_containment(rdev, new_ps, false); ni_enable_smc_cac(rdev, new_ps, false); rv770_halt_smc(rdev); @@ -3684,6 +3686,7 @@ int ni_dpm_set_power_state(struct radeon_device *rdev) ni_populate_smc_tdp_limits(rdev, new_ps); rv770_resume_smc(rdev); rv770_set_sw_state(rdev); + rv770_set_uvd_clock_after_set_eng_clock(rdev, new_ps, old_ps); ni_enable_smc_cac(rdev, new_ps, true); ni_enable_power_containment(rdev, new_ps, true);
From: Alex Deucher alexander.deucher@amd.com
Use the new asic callback instead.
Signed-off-by: Alex Deucher alexander.deucher@amd.com --- drivers/gpu/drm/radeon/sumo_dpm.c | 19 +++++++------------ drivers/gpu/drm/radeon/sumo_dpm.h | 1 - drivers/gpu/drm/radeon/sumo_smc.c | 2 +- drivers/gpu/drm/radeon/trinity_dpm.c | 6 +++--- 4 files changed, 11 insertions(+), 17 deletions(-)
diff --git a/drivers/gpu/drm/radeon/sumo_dpm.c b/drivers/gpu/drm/radeon/sumo_dpm.c index 10700e1..80fb3ac 100644 --- a/drivers/gpu/drm/radeon/sumo_dpm.c +++ b/drivers/gpu/drm/radeon/sumo_dpm.c @@ -84,11 +84,6 @@ struct sumo_power_info *sumo_get_pi(struct radeon_device *rdev) return pi; }
-u32 sumo_get_xclk(struct radeon_device *rdev) -{ - return rdev->clock.spll.reference_freq; -} - static void sumo_gfx_clockgating_enable(struct radeon_device *rdev, bool enable) { if (enable) @@ -124,7 +119,7 @@ static void sumo_mg_clockgating_enable(struct radeon_device *rdev, bool enable) static void sumo_program_git(struct radeon_device *rdev) { u32 p, u; - u32 xclk = sumo_get_xclk(rdev); + u32 xclk = radeon_get_xclk(rdev);
r600_calculate_u_and_p(SUMO_GICST_DFLT, xclk, 16, &p, &u); @@ -135,7 +130,7 @@ static void sumo_program_git(struct radeon_device *rdev) static void sumo_program_grsd(struct radeon_device *rdev) { u32 p, u; - u32 xclk = sumo_get_xclk(rdev); + u32 xclk = radeon_get_xclk(rdev); u32 grs = 256 * 25 / 100;
r600_calculate_u_and_p(1, xclk, 14, &p, &u); @@ -155,7 +150,7 @@ static void sumo_gfx_powergating_initialize(struct radeon_device *rdev) u32 p, u; u32 p_c, p_p, d_p; u32 r_t, i_t; - u32 xclk = sumo_get_xclk(rdev); + u32 xclk = radeon_get_xclk(rdev);
if (rdev->family == CHIP_PALM) { p_c = 4; @@ -319,7 +314,7 @@ static void sumo_calculate_bsp(struct radeon_device *rdev, u32 high_clk) { struct sumo_power_info *pi = sumo_get_pi(rdev); - u32 xclk = sumo_get_xclk(rdev); + u32 xclk = radeon_get_xclk(rdev);
pi->pasi = 65535 * 100 / high_clk; pi->asi = 65535 * 100 / high_clk; @@ -466,7 +461,7 @@ void sumo_clear_vc(struct radeon_device *rdev) void sumo_program_sstp(struct radeon_device *rdev) { u32 p, u; - u32 xclk = sumo_get_xclk(rdev); + u32 xclk = radeon_get_xclk(rdev);
r600_calculate_u_and_p(SUMO_SST_DFLT, xclk, 16, &p, &u); @@ -909,7 +904,7 @@ static void sumo_start_am(struct radeon_device *rdev)
static void sumo_program_ttp(struct radeon_device *rdev) { - u32 xclk = sumo_get_xclk(rdev); + u32 xclk = radeon_get_xclk(rdev); u32 p, u; u32 cg_sclk_dpm_ctrl_5 = RREG32(CG_SCLK_DPM_CTRL_5);
@@ -955,7 +950,7 @@ static void sumo_program_dc_hto(struct radeon_device *rdev) { u32 cg_sclk_dpm_ctrl_4 = RREG32(CG_SCLK_DPM_CTRL_4); u32 p, u; - u32 xclk = sumo_get_xclk(rdev); + u32 xclk = radeon_get_xclk(rdev);
r600_calculate_u_and_p(100000, xclk, 14, &p, &u); diff --git a/drivers/gpu/drm/radeon/sumo_dpm.h b/drivers/gpu/drm/radeon/sumo_dpm.h index a3a7a61..07dda29 100644 --- a/drivers/gpu/drm/radeon/sumo_dpm.h +++ b/drivers/gpu/drm/radeon/sumo_dpm.h @@ -188,7 +188,6 @@ struct sumo_power_info { #define SUMO_GFXPOWERGATINGT_DFLT 100
/* sumo_dpm.c */ -u32 sumo_get_xclk(struct radeon_device *rdev); void sumo_gfx_clockgating_initialize(struct radeon_device *rdev); void sumo_program_vc(struct radeon_device *rdev, u32 vrc); void sumo_clear_vc(struct radeon_device *rdev); diff --git a/drivers/gpu/drm/radeon/sumo_smc.c b/drivers/gpu/drm/radeon/sumo_smc.c index bc1a510..9cbf0b2 100644 --- a/drivers/gpu/drm/radeon/sumo_smc.c +++ b/drivers/gpu/drm/radeon/sumo_smc.c @@ -146,7 +146,7 @@ void sumo_enable_boost_timer(struct radeon_device *rdev) { struct sumo_power_info *pi = sumo_get_pi(rdev); u32 period, unit, timer_value; - u32 xclk = sumo_get_xclk(rdev); + u32 xclk = radeon_get_xclk(rdev);
unit = (RREG32_RCU(RCU_LCLK_SCALING_CNTL) & LCLK_SCALING_TIMER_PRESCALER_MASK) >> LCLK_SCALING_TIMER_PRESCALER_SHIFT; diff --git a/drivers/gpu/drm/radeon/trinity_dpm.c b/drivers/gpu/drm/radeon/trinity_dpm.c index 1699e93..b2dc905 100644 --- a/drivers/gpu/drm/radeon/trinity_dpm.c +++ b/drivers/gpu/drm/radeon/trinity_dpm.c @@ -361,7 +361,7 @@ static void trinity_gfx_powergating_initialize(struct radeon_device *rdev) u32 p, u; u32 value; struct atom_clock_dividers dividers; - u32 xclk = sumo_get_xclk(rdev); + u32 xclk = radeon_get_xclk(rdev); u32 sssd = 1; int ret; u32 hw_rev = (RREG32(HW_REV) & ATI_REV_ID_MASK) >> ATI_REV_ID_SHIFT; @@ -880,7 +880,7 @@ static void trinity_setup_uvd_dpm_interval(struct radeon_device *rdev, u32 p, u; u32 tp = RREG32_SMC(PM_TP); u32 val; - u32 xclk = sumo_get_xclk(rdev); + u32 xclk = radeon_get_xclk(rdev);
r600_calculate_u_and_p(interval, xclk, 16, &p, &u);
@@ -1000,7 +1000,7 @@ static void trinity_program_sclk_dpm(struct radeon_device *rdev) u32 p, u; u32 tp = RREG32_SMC(PM_TP); u32 ni; - u32 xclk = sumo_get_xclk(rdev); + u32 xclk = radeon_get_xclk(rdev); u32 value;
r600_calculate_u_and_p(400, xclk, 16, &p, &u);
Hi,
I just tried the DPM support out on a E-450 APU (HD6320) and it did not work like expected. In the terminal everything seemed ok but when I started a display manager, the screen showed garbage and the system basically locked up. The radeon and drm related parts of the syslog are below.
If you need further information, feel free to ask.
Best regards, Julian Wollrath
[ 14.926970] [drm] Initialized drm 1.1.0 20060810 [ 15.607011] [drm] radeon kernel modesetting enabled. [ 15.608158] [drm] initializing kernel modesetting (PALM 0x1002:0x9806 0x17AA:0x21EC). [ 15.608242] [drm] register mmio base: 0xF0300000 [ 15.608247] [drm] register mmio size: 262144 [ 15.608438] ATOM BIOS: Lenovo [ 15.608544] radeon 0000:00:01.0: VRAM: 384M 0x0000000000000000 - 0x0000000017FFFFFF (384M used) [ 15.608554] radeon 0000:00:01.0: GTT: 512M 0x0000000018000000 - 0x0000000037FFFFFF [ 15.609062] [drm] Detected VRAM RAM=384M, BAR=256M [ 15.609077] [drm] RAM width 32bits DDR [ 15.609442] [TTM] Zone kernel: Available graphics memory: 1821260 kiB [ 15.609453] [TTM] Initializing pool allocator [ 15.609469] [TTM] Initializing DMA pool allocator [ 15.609553] [drm] radeon: 384M of VRAM memory ready [ 15.609560] [drm] radeon: 512M of GTT memory ready. [ 15.785368] radeon 0000:00:01.0: ffff880119401000 unpin not necessary [ 15.911347] radeon 0000:00:01.0: fence driver on ring 5 use gpu addr 0x0000000000072118 and cpu addr 0xffffc90002ce6118 [ 15.911374] [drm] GART: num cpu pages 131072, num gpu pages 131072 [ 15.915577] [drm] Loading PALM Microcode [ 16.354967] [drm] PCIE GART of 512M enabled (table at 0x0000000000040000). [ 16.355272] radeon 0000:00:01.0: WB enabled [ 16.355287] radeon 0000:00:01.0: fence driver on ring 0 use gpu addr 0x0000000018000c00 and cpu addr 0xffff880115f44c00 [ 16.355297] radeon 0000:00:01.0: fence driver on ring 3 use gpu addr 0x0000000018000c0c and cpu addr 0xffff880115f44c0c [ 16.359306] radeon 0000:00:01.0: fence driver on ring 5 use gpu addr 0x0000000000177118 and cpu addr 0xffffc90003032118 [ 16.359326] [drm] Supports vblank timestamp caching Rev 1 (10.10.2010). [ 16.359330] [drm] Driver supports precise vblank timestamp query. [ 16.359391] radeon 0000:00:01.0: irq 46 for MSI/MSI-X [ 16.359432] radeon 0000:00:01.0: radeon: using MSI. [ 16.359528] [drm] radeon: irq initialized. [ 16.379053] [drm] ring test on 0 succeeded in 1 usecs [ 16.379120] [drm] ring test on 3 succeeded in 1 usecs [ 16.434367] [drm] ring test on 5 succeeded in 1 usecs [ 16.434384] [drm] UVD initialized successfully. [ 16.455934] [drm] ib test on ring 0 succeeded in 0 usecs [ 16.455984] [drm] ib test on ring 3 succeeded in 0 usecs [ 16.476866] [drm] ib test on ring 5 succeeded [ 16.549696] [drm] radeon atom DIG backlight initialized [ 16.549718] [drm] Radeon Display Connectors [ 16.549724] [drm] Connector 0: [ 16.549731] [drm] LVDS-1 [ 16.549736] [drm] HPD1 [ 16.549744] [drm] DDC: 0x6430 0x6430 0x6434 0x6434 0x6438 0x6438 0x643c 0x643c [ 16.549748] [drm] Encoders: [ 16.549753] [drm] LCD1: INTERNAL_UNIPHY [ 16.549757] [drm] Connector 1: [ 16.549762] [drm] HDMI-A-1 [ 16.549766] [drm] HPD2 [ 16.549773] [drm] DDC: 0x6440 0x6440 0x6444 0x6444 0x6448 0x6448 0x644c 0x644c [ 16.549776] [drm] Encoders: [ 16.549780] [drm] DFP1: INTERNAL_UNIPHY [ 16.549784] [drm] Connector 2: [ 16.549788] [drm] VGA-1 [ 16.549795] [drm] DDC: 0x64d8 0x64d8 0x64dc 0x64dc 0x64e0 0x64e0 0x64e4 0x64e4 [ 16.549799] [drm] Encoders: [ 16.549803] [drm] CRT1: INTERNAL_KLDSCP_DAC1 [ 16.549900] [drm] Internal thermal controller without fan control [ 16.550073] == power state 0 == [ 16.550079] ui class: none [ 16.550085] internal class: uvd_hd [ 16.550091] caps: [ 16.550096] uvd vclk: 38824 dclk: 30000 [ 16.550102] power level 0 sclk: 27500 vddc: 875 [ 16.550106] status: [ 16.550110] == power state 1 == [ 16.550113] ui class: none [ 16.550117] internal class: uvd [ 16.550122] caps: [ 16.550127] uvd vclk: 55000 dclk: 41250 [ 16.550132] power level 0 sclk: 50770 vddc: 950 [ 16.550135] status: [ 16.550139] == power state 2 == [ 16.550142] ui class: battery [ 16.550146] internal class: none [ 16.550151] caps: [ 16.550155] uvd vclk: 0 dclk: 0 [ 16.550160] power level 0 sclk: 27500 vddc: 875 [ 16.550163] status: [ 16.550167] == power state 3 == [ 16.550170] ui class: performance [ 16.550174] internal class: none [ 16.550179] caps: [ 16.550184] uvd vclk: 0 dclk: 0 [ 16.550188] power level 0 sclk: 27500 vddc: 875 [ 16.550193] power level 1 sclk: 50770 vddc: 950 [ 16.550196] status: [ 16.550200] == power state 4 == [ 16.550203] ui class: none [ 16.550207] internal class: boot [ 16.550212] caps: [ 16.550216] uvd vclk: 0 dclk: 0 [ 16.550221] power level 0 sclk: 20000 vddc: 975 [ 16.550224] status: c r b [ 16.550232] == power state 5 == [ 16.550235] ui class: none [ 16.550239] internal class: thermal [ 16.550243] caps: [ 16.550248] uvd vclk: 0 dclk: 0 [ 16.550252] power level 0 sclk: 17369 vddc: 875 [ 16.550255] status: [ 16.550263] [drm] Found smc ucode version: 0x00010601 [ 16.576523] switching from power state: [ 16.576539] ui class: none [ 16.576545] internal class: boot [ 16.576551] caps: [ 16.576557] uvd vclk: 0 dclk: 0 [ 16.576565] power level 0 sclk: 20000 vddc: 975 [ 16.576569] status: c b [ 16.576575] switching to power state: [ 16.576578] ui class: performance [ 16.576582] internal class: none [ 16.576588] caps: [ 16.576592] uvd vclk: 0 dclk: 0 [ 16.576598] power level 0 sclk: 27500 vddc: 875 [ 16.576603] power level 1 sclk: 50770 vddc: 950 [ 16.576606] status: r [ 16.576735] [drm] radeon: dpm initialized [ 17.000309] [drm] fb mappable at 0xE0378000 [ 17.000317] [drm] vram apper at 0xE0000000 [ 17.000319] [drm] size 4325376 [ 17.000322] [drm] fb depth is 24 [ 17.000324] [drm] pitch is 5632 [ 17.000438] fbcon: radeondrmfb (fb0) is primary device [ 17.563999] Console: switching to colour frame buffer device 170x48 [ 17.577133] radeon 0000:00:01.0: fb0: radeondrmfb frame buffer device [ 17.577141] radeon 0000:00:01.0: registered panic notifier [ 17.577820] [drm] Initialized radeon 2.34.0 20080528 for 0000:00:01.0 on minor 0
[ 159.318231] radeon 0000:00:01.0: GPU lockup CP stall for more than 10000msec [ 159.318252] radeon 0000:00:01.0: GPU lockup (waiting for 0x0000000000000004 last fence id 0x0000000000000001) [ 159.369796] radeon 0000:00:01.0: fence driver on ring 5 use gpu addr 0x0000000000177118 and cpu addr 0xffffc9000301b118 [ 159.370844] radeon 0000:00:01.0: Saved 87 dwords of commands on ring 0. [ 159.370856] radeon 0000:00:01.0: GPU softreset: 0x00000009 [ 159.370861] radeon 0000:00:01.0: GRBM_STATUS = 0xB2433828 [ 159.370865] radeon 0000:00:01.0: GRBM_STATUS_SE0 = 0x08000007 [ 159.370869] radeon 0000:00:01.0: GRBM_STATUS_SE1 = 0x00000007 [ 159.370872] radeon 0000:00:01.0: SRBM_STATUS = 0x20000040 [ 159.370876] radeon 0000:00:01.0: SRBM_STATUS2 = 0x00000000 [ 159.370880] radeon 0000:00:01.0: R_008674_CP_STALLED_STAT1 = 0x00000000 [ 159.370884] radeon 0000:00:01.0: R_008678_CP_STALLED_STAT2 = 0x40000000 [ 159.370888] radeon 0000:00:01.0: R_00867C_CP_BUSY_STAT = 0x00008000 [ 159.370891] radeon 0000:00:01.0: R_008680_CP_STAT = 0x80228643 [ 159.370895] radeon 0000:00:01.0: R_00D034_DMA_STATUS_REG = 0x44C83D57 [ 159.386016] radeon 0000:00:01.0: GRBM_SOFT_RESET=0x00007F6B [ 159.386093] radeon 0000:00:01.0: SRBM_SOFT_RESET=0x00000100 [ 159.387279] radeon 0000:00:01.0: GRBM_STATUS = 0x00003828 [ 159.387282] radeon 0000:00:01.0: GRBM_STATUS_SE0 = 0x00000007 [ 159.387286] radeon 0000:00:01.0: GRBM_STATUS_SE1 = 0x00000007 [ 159.387289] radeon 0000:00:01.0: SRBM_STATUS = 0x20000040 [ 159.387293] radeon 0000:00:01.0: SRBM_STATUS2 = 0x00000000 [ 159.387296] radeon 0000:00:01.0: R_008674_CP_STALLED_STAT1 = 0x00000000 [ 159.387300] radeon 0000:00:01.0: R_008678_CP_STALLED_STAT2 = 0x00000000 [ 159.387303] radeon 0000:00:01.0: R_00867C_CP_BUSY_STAT = 0x00000000 [ 159.387307] radeon 0000:00:01.0: R_008680_CP_STAT = 0x00000000 [ 159.387310] radeon 0000:00:01.0: R_00D034_DMA_STATUS_REG = 0x44C83D57 [ 159.387316] radeon 0000:00:01.0: GPU reset succeeded, trying to resume [ 159.407287] [drm] PCIE GART of 512M enabled (table at 0x0000000000040000). [ 159.407562] radeon 0000:00:01.0: WB enabled [ 159.407576] radeon 0000:00:01.0: fence driver on ring 0 use gpu addr 0x0000000018000c00 and cpu addr 0xffff880115f44c00 [ 159.407587] radeon 0000:00:01.0: fence driver on ring 3 use gpu addr 0x0000000018000c0c and cpu addr 0xffff880115f44c0c [ 159.410849] radeon 0000:00:01.0: fence driver on ring 5 use gpu addr 0x0000000000177118 and cpu addr 0xffffc90003eb2118 [ 159.427563] [drm] ring test on 0 succeeded in 1 usecs [ 159.427634] [drm] ring test on 3 succeeded in 1 usecs [ 159.472928] [drm] ring test on 5 succeeded in 1 usecs [ 159.472958] [drm] UVD initialized successfully. [ 164.422044] SysRq : Keyboard mode set to system default [ 164.646822] SysRq : Terminate All Tasks
[ 14.301299] [drm] Initialized drm 1.1.0 20060810 [ 14.906930] [drm] radeon kernel modesetting enabled. [ 14.975218] [drm] initializing kernel modesetting (PALM 0x1002:0x9806 0x17AA:0x21EC). [ 14.975271] [drm] register mmio base: 0xF0300000 [ 14.975273] [drm] register mmio size: 262144 [ 14.975377] ATOM BIOS: Lenovo [ 14.975453] radeon 0000:00:01.0: VRAM: 384M 0x0000000000000000 - 0x0000000017FFFFFF (384M used) [ 14.975459] radeon 0000:00:01.0: GTT: 512M 0x0000000018000000 - 0x0000000037FFFFFF [ 14.975657] [drm] Detected VRAM RAM=384M, BAR=256M [ 14.975667] [drm] RAM width 32bits DDR [ 14.976352] [TTM] Zone kernel: Available graphics memory: 1821260 kiB [ 14.976356] [TTM] Initializing pool allocator [ 14.976364] [TTM] Initializing DMA pool allocator [ 14.976404] [drm] radeon: 384M of VRAM memory ready [ 14.976408] [drm] radeon: 512M of GTT memory ready. [ 15.719064] radeon 0000:00:01.0: ffff8801196f1800 unpin not necessary [ 15.856057] radeon 0000:00:01.0: fence driver on ring 5 use gpu addr 0x0000000000072118 and cpu addr 0xffffc90002ce6118 [ 15.856075] [drm] GART: num cpu pages 131072, num gpu pages 131072 [ 15.858815] [drm] Loading PALM Microcode [ 16.363243] [drm] PCIE GART of 512M enabled (table at 0x0000000000040000). [ 16.363537] radeon 0000:00:01.0: WB enabled [ 16.363545] radeon 0000:00:01.0: fence driver on ring 0 use gpu addr 0x0000000018000c00 and cpu addr 0xffff880115f8bc00 [ 16.363550] radeon 0000:00:01.0: fence driver on ring 3 use gpu addr 0x0000000018000c0c and cpu addr 0xffff880115f8bc0c [ 16.365611] radeon 0000:00:01.0: fence driver on ring 5 use gpu addr 0x0000000000177118 and cpu addr 0xffffc90002ab2118 [ 16.365623] [drm] Supports vblank timestamp caching Rev 1 (10.10.2010). [ 16.365626] [drm] Driver supports precise vblank timestamp query. [ 16.365666] radeon 0000:00:01.0: irq 46 for MSI/MSI-X [ 16.365692] radeon 0000:00:01.0: radeon: using MSI. [ 16.365733] [drm] radeon: irq initialized. [ 16.383123] [drm] ring test on 0 succeeded in 1 usecs [ 16.383199] [drm] ring test on 3 succeeded in 1 usecs [ 16.438468] [drm] ring test on 5 succeeded in 1 usecs [ 16.438479] [drm] UVD initialized successfully. [ 16.459644] [drm] ib test on ring 0 succeeded in 0 usecs [ 16.459693] [drm] ib test on ring 3 succeeded in 0 usecs [ 16.480537] [drm] ib test on ring 5 succeeded [ 16.556037] [drm] radeon atom DIG backlight initialized [ 16.556058] [drm] Radeon Display Connectors [ 16.556064] [drm] Connector 0: [ 16.556072] [drm] LVDS-1 [ 16.556077] [drm] HPD1 [ 16.556085] [drm] DDC: 0x6430 0x6430 0x6434 0x6434 0x6438 0x6438 0x643c 0x643c [ 16.556089] [drm] Encoders: [ 16.556094] [drm] LCD1: INTERNAL_UNIPHY [ 16.556099] [drm] Connector 1: [ 16.556104] [drm] HDMI-A-1 [ 16.556107] [drm] HPD2 [ 16.556114] [drm] DDC: 0x6440 0x6440 0x6444 0x6444 0x6448 0x6448 0x644c 0x644c [ 16.556118] [drm] Encoders: [ 16.556122] [drm] DFP1: INTERNAL_UNIPHY [ 16.556126] [drm] Connector 2: [ 16.556131] [drm] VGA-1 [ 16.556137] [drm] DDC: 0x64d8 0x64d8 0x64dc 0x64dc 0x64e0 0x64e0 0x64e4 0x64e4 [ 16.556141] [drm] Encoders: [ 16.556145] [drm] CRT1: INTERNAL_KLDSCP_DAC1 [ 16.556242] [drm] Internal thermal controller without fan control [ 16.556392] == power state 0 == [ 16.556398] ui class: none [ 16.556404] internal class: uvd_hd [ 16.556410] caps: [ 16.556416] uvd vclk: 38824 dclk: 30000 [ 16.556422] power level 0 sclk: 27500 vddc: 875 [ 16.556425] status: [ 16.556430] == power state 1 == [ 16.556433] ui class: none [ 16.556437] internal class: uvd [ 16.556442] caps: [ 16.556447] uvd vclk: 55000 dclk: 41250 [ 16.556452] power level 0 sclk: 50770 vddc: 950 [ 16.556455] status: [ 16.556459] == power state 2 == [ 16.556462] ui class: battery [ 16.556466] internal class: none [ 16.556471] caps: [ 16.556475] uvd vclk: 0 dclk: 0 [ 16.556480] power level 0 sclk: 27500 vddc: 875 [ 16.556483] status: [ 16.556487] == power state 3 == [ 16.556491] ui class: performance [ 16.556494] internal class: none [ 16.556499] caps: [ 16.556504] uvd vclk: 0 dclk: 0 [ 16.556509] power level 0 sclk: 27500 vddc: 875 [ 16.556514] power level 1 sclk: 50770 vddc: 950 [ 16.556517] status: [ 16.556521] == power state 4 == [ 16.556524] ui class: none [ 16.556528] internal class: boot [ 16.556533] caps: [ 16.556537] uvd vclk: 0 dclk: 0 [ 16.556542] power level 0 sclk: 20000 vddc: 975 [ 16.556545] status: c r b [ 16.556553] == power state 5 == [ 16.556556] ui class: none [ 16.556560] internal class: thermal [ 16.556564] caps: [ 16.556569] uvd vclk: 0 dclk: 0 [ 16.556574] power level 0 sclk: 17369 vddc: 875 [ 16.556577] status: [ 16.556584] [drm] Found smc ucode version: 0x00010601 [ 16.577725] switching from power state: [ 16.577737] ui class: none [ 16.577740] internal class: boot [ 16.577744] caps: [ 16.577747] uvd vclk: 0 dclk: 0 [ 16.577751] power level 0 sclk: 20000 vddc: 975 [ 16.577753] status: c b [ 16.577757] switching to power state: [ 16.577758] ui class: performance [ 16.577760] internal class: none [ 16.577763] caps: [ 16.577765] uvd vclk: 0 dclk: 0 [ 16.577768] power level 0 sclk: 27500 vddc: 875 [ 16.577771] power level 1 sclk: 50770 vddc: 950 [ 16.577772] status: r [ 16.577868] [drm] radeon: dpm initialized [ 16.694706] ieee80211 phy0: Selected rate control algorithm 'iwl-agn-rs' [ 16.997044] [drm] fb mappable at 0xE0378000 [ 16.997057] [drm] vram apper at 0xE0000000 [ 16.997062] [drm] size 4325376 [ 16.997067] [drm] fb depth is 24 [ 16.997072] [drm] pitch is 5632 [ 16.997267] fbcon: radeondrmfb (fb0) is primary device [ 17.565597] Console: switching to colour frame buffer device 170x48 [ 17.578692] radeon 0000:00:01.0: fb0: radeondrmfb frame buffer device [ 17.578701] radeon 0000:00:01.0: registered panic notifier [ 17.579385] [drm] Initialized radeon 2.34.0 20080528 for 0000:00:01.0 on minor 0
[ 195.823063] radeon 0000:00:01.0: GPU lockup CP stall for more than 10000msec [ 195.827260] radeon 0000:00:01.0: GPU lockup (waiting for 0x0000000000000004 last fence id 0x0000000000000001) [ 195.879857] radeon 0000:00:01.0: fence driver on ring 5 use gpu addr 0x0000000000177118 and cpu addr 0xffffc90002832118 [ 195.880922] radeon 0000:00:01.0: Saved 87 dwords of commands on ring 0. [ 195.880935] radeon 0000:00:01.0: GPU softreset: 0x00000009 [ 195.880941] radeon 0000:00:01.0: GRBM_STATUS = 0xB2433828 [ 195.880946] radeon 0000:00:01.0: GRBM_STATUS_SE0 = 0x08000007 [ 195.880951] radeon 0000:00:01.0: GRBM_STATUS_SE1 = 0x00000007 [ 195.880956] radeon 0000:00:01.0: SRBM_STATUS = 0x20000040 [ 195.880960] radeon 0000:00:01.0: SRBM_STATUS2 = 0x00000000 [ 195.880965] radeon 0000:00:01.0: R_008674_CP_STALLED_STAT1 = 0x00000000 [ 195.880970] radeon 0000:00:01.0: R_008678_CP_STALLED_STAT2 = 0x40000000 [ 195.880974] radeon 0000:00:01.0: R_00867C_CP_BUSY_STAT = 0x00008000 [ 195.880979] radeon 0000:00:01.0: R_008680_CP_STAT = 0x80228643 [ 195.880983] radeon 0000:00:01.0: R_00D034_DMA_STATUS_REG = 0x44C83D57 [ 195.895865] radeon 0000:00:01.0: GRBM_SOFT_RESET=0x00007F6B [ 195.895921] radeon 0000:00:01.0: SRBM_SOFT_RESET=0x00000100 [ 195.897108] radeon 0000:00:01.0: GRBM_STATUS = 0x00003828 [ 195.897113] radeon 0000:00:01.0: GRBM_STATUS_SE0 = 0x00000007 [ 195.897117] radeon 0000:00:01.0: GRBM_STATUS_SE1 = 0x00000007 [ 195.897121] radeon 0000:00:01.0: SRBM_STATUS = 0x20000040 [ 195.897126] radeon 0000:00:01.0: SRBM_STATUS2 = 0x00000000 [ 195.897130] radeon 0000:00:01.0: R_008674_CP_STALLED_STAT1 = 0x00000000 [ 195.897134] radeon 0000:00:01.0: R_008678_CP_STALLED_STAT2 = 0x00000000 [ 195.897138] radeon 0000:00:01.0: R_00867C_CP_BUSY_STAT = 0x00000000 [ 195.897143] radeon 0000:00:01.0: R_008680_CP_STAT = 0x00000000 [ 195.897147] radeon 0000:00:01.0: R_00D034_DMA_STATUS_REG = 0x44C83D57 [ 195.897154] radeon 0000:00:01.0: GPU reset succeeded, trying to resume [ 195.917323] [drm] PCIE GART of 512M enabled (table at 0x0000000000040000). [ 195.917553] radeon 0000:00:01.0: WB enabled [ 195.917567] radeon 0000:00:01.0: fence driver on ring 0 use gpu addr 0x0000000018000c00 and cpu addr 0xffff880115f8bc00 [ 195.917578] radeon 0000:00:01.0: fence driver on ring 3 use gpu addr 0x0000000018000c0c and cpu addr 0xffff880115f8bc0c [ 195.920890] radeon 0000:00:01.0: fence driver on ring 5 use gpu addr 0x0000000000177118 and cpu addr 0xffffc90003c32118 [ 195.937779] [drm] ring test on 0 succeeded in 1 usecs [ 195.937858] [drm] ring test on 3 succeeded in 1 usecs [ 195.983131] [drm] ring test on 5 succeeded in 1 usecs [ 195.983148] [drm] UVD initialized successfully. [ 206.009333] radeon 0000:00:01.0: GPU lockup CP stall for more than 10000msec [ 206.013681] radeon 0000:00:01.0: GPU lockup (waiting for 0x0000000000000005 last fence id 0x0000000000000001) [ 206.013692] [drm:r600_ib_test] *ERROR* radeon: fence wait failed (-35). [ 206.018028] [drm:radeon_ib_ring_tests] *ERROR* radeon: failed testing IB on GFX ring (-35). [ 206.022364] radeon 0000:00:01.0: ib ring test failed (-35). [ 206.075249] radeon 0000:00:01.0: fence driver on ring 5 use gpu addr 0x0000000000177118 and cpu addr 0xffffc90002832118 [ 206.076352] radeon 0000:00:01.0: GPU softreset: 0x00000009 [ 206.076362] radeon 0000:00:01.0: GRBM_STATUS = 0xB3533828 [ 206.076370] radeon 0000:00:01.0: GRBM_STATUS_SE0 = 0x2C000007 [ 206.076378] radeon 0000:00:01.0: GRBM_STATUS_SE1 = 0x00000007 [ 206.076386] radeon 0000:00:01.0: SRBM_STATUS = 0x20000040 [ 206.076393] radeon 0000:00:01.0: SRBM_STATUS2 = 0x00000000 [ 206.076401] radeon 0000:00:01.0: R_008674_CP_STALLED_STAT1 = 0x00000000 [ 206.076408] radeon 0000:00:01.0: R_008678_CP_STALLED_STAT2 = 0x400C0000 [ 206.076415] radeon 0000:00:01.0: R_00867C_CP_BUSY_STAT = 0x00048000 [ 206.076422] radeon 0000:00:01.0: R_008680_CP_STAT = 0x80268643 [ 206.076429] radeon 0000:00:01.0: R_00D034_DMA_STATUS_REG = 0x44C83D57 [ 206.076620] radeon 0000:00:01.0: GRBM_SOFT_RESET=0x00007F6B [ 206.076678] radeon 0000:00:01.0: SRBM_SOFT_RESET=0x00000100 [ 206.077871] radeon 0000:00:01.0: GRBM_STATUS = 0x00003828 [ 206.077878] radeon 0000:00:01.0: GRBM_STATUS_SE0 = 0x00000007 [ 206.077885] radeon 0000:00:01.0: GRBM_STATUS_SE1 = 0x00000007 [ 206.077892] radeon 0000:00:01.0: SRBM_STATUS = 0x20000040 [ 206.077899] radeon 0000:00:01.0: SRBM_STATUS2 = 0x00000000 [ 206.077905] radeon 0000:00:01.0: R_008674_CP_STALLED_STAT1 = 0x00000000 [ 206.077912] radeon 0000:00:01.0: R_008678_CP_STALLED_STAT2 = 0x00000000 [ 206.077919] radeon 0000:00:01.0: R_00867C_CP_BUSY_STAT = 0x00000000 [ 206.077926] radeon 0000:00:01.0: R_008680_CP_STAT = 0x00000000 [ 206.077932] radeon 0000:00:01.0: R_00D034_DMA_STATUS_REG = 0x44C83D57 [ 206.077943] radeon 0000:00:01.0: GPU reset succeeded, trying to resume [ 206.082896] [drm] PCIE GART of 512M enabled (table at 0x0000000000040000). [ 206.083120] radeon 0000:00:01.0: WB enabled [ 206.083134] radeon 0000:00:01.0: fence driver on ring 0 use gpu addr 0x0000000018000c00 and cpu addr 0xffff880115f8bc00 [ 206.083143] radeon 0000:00:01.0: fence driver on ring 3 use gpu addr 0x0000000018000c0c and cpu addr 0xffff880115f8bc0c [ 206.086489] radeon 0000:00:01.0: fence driver on ring 5 use gpu addr 0x0000000000177118 and cpu addr 0xffffc90003c32118 [ 206.103118] [drm] ring test on 0 succeeded in 1 usecs [ 206.103188] [drm] ring test on 3 succeeded in 1 usecs [ 206.148514] [drm] ring test on 5 succeeded in 1 usecs [ 206.148531] [drm] UVD initialized successfully. [ 206.867862] SysRq : Keyboard mode set to system default [ 207.151608] SysRq : Terminate All Tasks
Hi,
I just tried the DPM support out on a E-450 APU (HD6320) and it did not work like expected. In the terminal everything seemed ok but when I started a display manager, the screen showed garbage and the system basically locked up. The radeon and drm related parts of the syslog are below.
I saw, that you updated the patches, therefore I tried out the branch drm-next-3.11-wip-2 and I got, additionally to the problem mentioned in my first e-mail, the following warning:
[ 14.274349] ------------[ cut here ]------------ [ 14.274369] WARNING: at kernel/workqueue.c:1365 __queue_work+0x260/0x2c0() [ 14.274374] Modules linked in: bluetooth crc16 acpi_cpufreq snd_hda_codec_hdmi iwlwifi uvcvideo mperf radeon(+) videobuf2_vmalloc cfg80211 snd_hda_intel(+) battery processor button thinkpad_acpi video nvram rfkill videobuf2_memops videobuf2_core videodev snd_hda_codec kvm_amd i2c_algo_bit kvm snd_hwdep snd_pcm snd_timer snd_page_alloc ac drm_kms_helper ttm snd drm soundcore wmi i2c_piix4 agpgart pcspkr i2c_core evdev k10temp psmouse serio_raw sha256_ssse3 sha256_generic twofish_x86_64_3way glue_helper xts lrw gf128mul twofish_x86_64 twofish_common cbc dm_crypt dm_mod sd_mod crc_t10dif rtsx_pci_sdmmc mmc_core microcode thermal thermal_sys ahci libahci ohci_hcd ehci_pci ehci_hcd rtsx_pci mfd_core usbcore usb_common libata scsi_mod [ 14.274517] CPU: 1 PID: 497 Comm: modprobe Not tainted 3.10.0-rc7-wl+ #3 [ 14.274524] Hardware name: LENOVO 30515YG/30515YG, BIOS 8RET52WW (1.15 ) 11/15/2011 [ 14.274530] ffffffff813fc83e ffffffff81037a7a ffff88011ec12540 ffff88011ed15d00 [ 14.274539] ffff8801164a5bc8 0000000000000002 ffff88011e060a00 ffffffff81053590 [ 14.274548] 0000000000000002 ffff8801164a5e9c 0000000000000001 0000000000000010 [ 14.274558] Call Trace: [ 14.274563] <IRQ> [<ffffffff813fc83e>] ? dump_stack+0xc/0x15 [ 14.274582] [<ffffffff81037a7a>] ? warn_slowpath_common+0x6a/0xa0 [ 14.274594] [<ffffffff81053590>] ? __queue_work+0x260/0x2c0 [ 14.274605] [<ffffffff8105360d>] ? queue_work_on+0x1d/0x30 [ 14.274703] [<ffffffffa045ac26>] ? evergreen_irq_process+0x906/0xd20 [radeon] [ 14.274715] [<ffffffff810a0a2d>] ? handle_irq_event_percpu+0x2d/0x1a0 [ 14.274724] [<ffffffff810a0bd6>] ? handle_irq_event+0x36/0x60 [ 14.274734] [<ffffffff810a3487>] ? handle_edge_irq+0x67/0x110 [ 14.274743] [<ffffffff810045d5>] ? handle_irq+0x15/0x20 [ 14.274751] [<ffffffff81004261>] ? do_IRQ+0x51/0xd0 [ 14.274761] [<ffffffff814009ea>] ? common_interrupt+0x6a/0x6a [ 14.274765] <EOI> [<ffffffff812369a9>] ? delay_tsc+0x29/0x60 [ 14.274850] [<ffffffffa0455cda>] ? sumo_set_uvd_clock+0x8a/0xb0 [radeon] [ 14.274926] [<ffffffffa0455dfd>] ? sumo_set_uvd_clocks+0x4d/0xe0 [radeon] [ 14.274999] [<ffffffffa043f775>] ? r600_uvd_init+0x25/0x390 [radeon] [ 14.275073] [<ffffffffa045d652>] ? evergreen_startup+0x1952/0x1960 [radeon] [ 14.275146] [<ffffffffa045d85b>] ? evergreen_init+0x17b/0x2c0 [radeon] [ 14.275208] [<ffffffffa03f96a5>] ? radeon_device_init+0x5b5/0x6d0 [radeon] [ 14.275219] [<ffffffff8124d975>] ? pci_find_capability+0x45/0x60 [ 14.275279] [<ffffffffa03fb234>] ? radeon_driver_load_kms+0x84/0x140 [radeon] [ 14.275303] [<ffffffffa01d84ad>] ? drm_get_pci_dev+0x17d/0x290 [drm] [ 14.275314] [<ffffffff812521b8>] ? pci_device_probe+0x98/0xe0 [ 14.275325] [<ffffffff812d9ed8>] ? driver_probe_device+0x68/0x210 [ 14.275333] [<ffffffff812da173>] ? __driver_attach+0x93/0xa0 [ 14.275341] [<ffffffff812da0e0>] ? __device_attach+0x60/0x60 [ 14.275351] [<ffffffff812d81b3>] ? bus_for_each_dev+0x53/0x90 [ 14.275359] [<ffffffff812d96a0>] ? bus_add_driver+0x1c0/0x250 [ 14.275367] [<ffffffff812da709>] ? driver_register+0x69/0x140 [ 14.275380] [<ffffffffa050b000>] ? 0xffffffffa050afff [ 14.275390] [<ffffffffa050b000>] ? 0xffffffffa050afff [ 14.275399] [<ffffffff810002da>] ? do_one_initcall+0x10a/0x160 [ 14.275408] [<ffffffff8108e508>] ? load_module+0x1cd8/0x2390 [ 14.275416] [<ffffffff8108a540>] ? store_uevent+0x50/0x50 [ 14.275427] [<ffffffff8108ec78>] ? SyS_init_module+0xb8/0xe0 [ 14.275438] [<ffffffff81401112>] ? system_call_fastpath+0x16/0x1b [ 14.275444] ---[ end trace 1f1cbf85b14e84a1 ]---
With best regards, Julian Wollrath
On Wed, Jun 26, 2013 at 6:51 PM, Julian Wollrath jwollrath@web.de wrote:
Hi,
I just tried the DPM support out on a E-450 APU (HD6320) and it did not work like expected. In the terminal everything seemed ok but when I started a display manager, the screen showed garbage and the system basically locked up. The radeon and drm related parts of the syslog are below.
I saw, that you updated the patches, therefore I tried out the branch drm-next-3.11-wip-2 and I got, additionally to the problem mentioned in my first e-mail, the following warning:
[ 14.274349] ------------[ cut here ]------------ [ 14.274369] WARNING: at kernel/workqueue.c:1365 __queue_work+0x260/0x2c0() [ 14.274374] Modules linked in: bluetooth crc16 acpi_cpufreq snd_hda_codec_hdmi iwlwifi uvcvideo mperf radeon(+) videobuf2_vmalloc cfg80211 snd_hda_intel(+) battery processor button thinkpad_acpi video nvram rfkill videobuf2_memops videobuf2_core videodev snd_hda_codec kvm_amd i2c_algo_bit kvm snd_hwdep snd_pcm snd_timer snd_page_alloc ac drm_kms_helper ttm snd drm soundcore wmi i2c_piix4 agpgart pcspkr i2c_core evdev k10temp psmouse serio_raw sha256_ssse3 sha256_generic twofish_x86_64_3way glue_helper xts lrw gf128mul twofish_x86_64 twofish_common cbc dm_crypt dm_mod sd_mod crc_t10dif rtsx_pci_sdmmc mmc_core microcode thermal thermal_sys ahci libahci ohci_hcd ehci_pci ehci_hcd rtsx_pci mfd_core usbcore usb_common libata scsi_mod [ 14.274517] CPU: 1 PID: 497 Comm: modprobe Not tainted 3.10.0-rc7-wl+ #3 [ 14.274524] Hardware name: LENOVO 30515YG/30515YG, BIOS 8RET52WW (1.15 ) 11/15/2011 [ 14.274530] ffffffff813fc83e ffffffff81037a7a ffff88011ec12540 ffff88011ed15d00 [ 14.274539] ffff8801164a5bc8 0000000000000002 ffff88011e060a00 ffffffff81053590 [ 14.274548] 0000000000000002 ffff8801164a5e9c 0000000000000001 0000000000000010 [ 14.274558] Call Trace: [ 14.274563] <IRQ> [<ffffffff813fc83e>] ? dump_stack+0xc/0x15 [ 14.274582] [<ffffffff81037a7a>] ? warn_slowpath_common+0x6a/0xa0 [ 14.274594] [<ffffffff81053590>] ? __queue_work+0x260/0x2c0 [ 14.274605] [<ffffffff8105360d>] ? queue_work_on+0x1d/0x30 [ 14.274703] [<ffffffffa045ac26>] ? evergreen_irq_process+0x906/0xd20 [radeon] [ 14.274715] [<ffffffff810a0a2d>] ? handle_irq_event_percpu+0x2d/0x1a0 [ 14.274724] [<ffffffff810a0bd6>] ? handle_irq_event+0x36/0x60 [ 14.274734] [<ffffffff810a3487>] ? handle_edge_irq+0x67/0x110 [ 14.274743] [<ffffffff810045d5>] ? handle_irq+0x15/0x20 [ 14.274751] [<ffffffff81004261>] ? do_IRQ+0x51/0xd0 [ 14.274761] [<ffffffff814009ea>] ? common_interrupt+0x6a/0x6a [ 14.274765] <EOI> [<ffffffff812369a9>] ? delay_tsc+0x29/0x60 [ 14.274850] [<ffffffffa0455cda>] ? sumo_set_uvd_clock+0x8a/0xb0 [radeon] [ 14.274926] [<ffffffffa0455dfd>] ? sumo_set_uvd_clocks+0x4d/0xe0 [radeon] [ 14.274999] [<ffffffffa043f775>] ? r600_uvd_init+0x25/0x390 [radeon] [ 14.275073] [<ffffffffa045d652>] ? evergreen_startup+0x1952/0x1960 [radeon] [ 14.275146] [<ffffffffa045d85b>] ? evergreen_init+0x17b/0x2c0 [radeon] [ 14.275208] [<ffffffffa03f96a5>] ? radeon_device_init+0x5b5/0x6d0 [radeon] [ 14.275219] [<ffffffff8124d975>] ? pci_find_capability+0x45/0x60 [ 14.275279] [<ffffffffa03fb234>] ? radeon_driver_load_kms+0x84/0x140 [radeon] [ 14.275303] [<ffffffffa01d84ad>] ? drm_get_pci_dev+0x17d/0x290 [drm] [ 14.275314] [<ffffffff812521b8>] ? pci_device_probe+0x98/0xe0 [ 14.275325] [<ffffffff812d9ed8>] ? driver_probe_device+0x68/0x210 [ 14.275333] [<ffffffff812da173>] ? __driver_attach+0x93/0xa0 [ 14.275341] [<ffffffff812da0e0>] ? __device_attach+0x60/0x60 [ 14.275351] [<ffffffff812d81b3>] ? bus_for_each_dev+0x53/0x90 [ 14.275359] [<ffffffff812d96a0>] ? bus_add_driver+0x1c0/0x250 [ 14.275367] [<ffffffff812da709>] ? driver_register+0x69/0x140 [ 14.275380] [<ffffffffa050b000>] ? 0xffffffffa050afff [ 14.275390] [<ffffffffa050b000>] ? 0xffffffffa050afff [ 14.275399] [<ffffffff810002da>] ? do_one_initcall+0x10a/0x160 [ 14.275408] [<ffffffff8108e508>] ? load_module+0x1cd8/0x2390 [ 14.275416] [<ffffffff8108a540>] ? store_uevent+0x50/0x50 [ 14.275427] [<ffffffff8108ec78>] ? SyS_init_module+0xb8/0xe0 [ 14.275438] [<ffffffff81401112>] ? system_call_fastpath+0x16/0x1b [ 14.275444] ---[ end trace 1f1cbf85b14e84a1 ]---
With best regards, Julian Wollrath
This patch should fix your issue :
http://people.freedesktop.org/~glisse/0001-radeon-do-no-schedule-thermal-wor...
Cheers, Jerome
Hi,
This patch should fix your issue :
http://people.freedesktop.org/~glisse/0001-radeon-do-no-schedule-thermal-wor...
yes, that seems to do the trick (but I can not be 100% sure, since I only saw the warning once).
Cheers, Jerome
Best regards, Julian
On 26.06.2013 23:57, Julian Wollrath wrote:
Hi,
I just tried the DPM support out on a E-450 APU (HD6320) and it did not work like expected. In the terminal everything seemed ok but when I started a display manager, the screen showed garbage and the system basically locked up. The radeon and drm related parts of the syslog are below.
If you need further information, feel free to ask.
I have the same problem with the same APU. Disabling power gating (set enable_gfx_power_gating to false in sumo_dpm.c:sumo_dpm_init) fixes it. So this problem may related to power gating.
The difference in power consumption seems to be small, but I'd still prefer it working.
By the way, the "disable_gfx_power_gating_in_uvd" quirk is not actually used, seems to be some leftover from debugging or such.
Best regards Grigori
On Wed, Jun 26, 2013 at 9:21 AM, alexdeucher@gmail.com wrote:
From: Alex Deucher alexander.deucher@amd.com
These are the radeon patches for 3.11. Some of these patches are huge so, it might be easier to review things here: http://cgit.freedesktop.org/~agd5f/linux/log/?h=drm-next-3.11-wip
Updated branch: http://cgit.freedesktop.org/~agd5f/linux/log/?h=drm-next-3.11-wip-2 Takes into account comments from Jerome a Christian and contains a few DPM fixes.
Alex
Alex Deucher wrote:
On Wed, Jun 26, 2013 at 9:21 AM, alexdeucher@gmail.com wrote:
From: Alex Deucher alexander.deucher@amd.com
These are the radeon patches for 3.11. Some of these patches are huge so, it might be easier to review things here: http://cgit.freedesktop.org/~agd5f/linux/log/?h=drm-next-3.11-wip
Updated branch: http://cgit.freedesktop.org/~agd5f/linux/log/?h=drm-next-3.11-wip-2 Takes into account comments from Jerome a Christian and contains a few DPM fixes.
I see there's a 3 now - I tested that (I guess currently it's the same as 2 anyway).
On my rv790 there are no regressions so far, but whatever I do it stays low.
echo profile > /sys/class/drm/card0/device/power_method = write error, is there a way to go back to manual setting with dpm?
I haven't tried rv670 yet - I couldn't see a new firmware for that, does it just not need one?
On Thu, Jun 27, 2013 at 9:12 AM, Andy Furniss adf.lists@gmail.com wrote:
Alex Deucher wrote:
On Wed, Jun 26, 2013 at 9:21 AM, alexdeucher@gmail.com wrote:
From: Alex Deucher alexander.deucher@amd.com
These are the radeon patches for 3.11. Some of these patches are huge so, it might be easier to review things here: http://cgit.freedesktop.org/~agd5f/linux/log/?h=drm-next-3.11-wip
Updated branch: http://cgit.freedesktop.org/~agd5f/linux/log/?h=drm-next-3.11-wip-2 Takes into account comments from Jerome a Christian and contains a few DPM fixes.
I see there's a 3 now - I tested that (I guess currently it's the same as 2 anyway).
On my rv790 there are no regressions so far, but whatever I do it stays low.
We may need to tweak the scaling parameters a bit to force higher states more readily. They are pretty conservative right now.
echo profile > /sys/class/drm/card0/device/power_method = write error, is there a way to go back to manual setting with dpm?
Not yet. I'm working on an infrastructure to force DPM modes, but it's not ready yet.
I haven't tried rv670 yet - I couldn't see a new firmware for that, does it just not need one?
Nope. 6xx and APUs do not require ucode. only 7xx+ dGPUs.
Alex
"AD" == Alex Deucher alexdeucher@gmail.com writes:
AD> Nope. 6xx and APUs do not require ucode. only 7xx+ dGPUs.
Does that mean that APUs do not require *any* ucode blobs?
Or just that they do not require updated or additional blobs for the new functionality like DPM?
-JimC
On Thu, Jun 27, 2013 at 6:19 PM, James Cloos cloos@jhcloos.com wrote:
"AD" == Alex Deucher alexdeucher@gmail.com writes:
AD> Nope. 6xx and APUs do not require ucode. only 7xx+ dGPUs.
Does that mean that APUs do not require *any* ucode blobs?
Or just that they do not require updated or additional blobs for the new functionality like DPM?
-JimC
They don't require any ucode blob for the power management. They do require ucode blob for 3d acceleration, compute, well anything that want to use the gpu really.
Cheers, Jerome
On Thu, Jun 27, 2013 at 9:12 AM, Andy Furniss adf.lists@gmail.com wrote:
Alex Deucher wrote:
On Wed, Jun 26, 2013 at 9:21 AM, alexdeucher@gmail.com wrote:
From: Alex Deucher alexander.deucher@amd.com
These are the radeon patches for 3.11. Some of these patches are huge so, it might be easier to review things here: http://cgit.freedesktop.org/~agd5f/linux/log/?h=drm-next-3.11-wip
Updated branch: http://cgit.freedesktop.org/~agd5f/linux/log/?h=drm-next-3.11-wip-2 Takes into account comments from Jerome a Christian and contains a few DPM fixes.
I see there's a 3 now - I tested that (I guess currently it's the same as 2 anyway).
On my rv790 there are no regressions so far, but whatever I do it stays low.
This should work much better with with wip-5.
Alex
Le 28/06/2013 01:55, Alex Deucher a écrit :
On Thu, Jun 27, 2013 at 9:12 AM, Andy Furniss adf.lists@gmail.com wrote:
Alex Deucher wrote:
On Wed, Jun 26, 2013 at 9:21 AM, alexdeucher@gmail.com wrote:
From: Alex Deucher alexander.deucher@amd.com
These are the radeon patches for 3.11. Some of these patches are huge so, it might be easier to review things here: http://cgit.freedesktop.org/~agd5f/linux/log/?h=drm-next-3.11-wip
Updated branch: http://cgit.freedesktop.org/~agd5f/linux/log/?h=drm-next-3.11-wip-2 Takes into account comments from Jerome a Christian and contains a few DPM fixes.
I see there's a 3 now - I tested that (I guess currently it's the same as 2 anyway).
On my rv790 there are no regressions so far, but whatever I do it stays low.
This should work much better with with wip-5.
Alex _______________________________________________ dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
Just tested, and my card (HD6870) is still stuck at low.
(tested with HL2 and wine/furmark)
++
Alex Deucher wrote:
On Thu, Jun 27, 2013 at 9:12 AM, Andy Furniss adf.lists@gmail.com wrote:
Alex Deucher wrote:
On Wed, Jun 26, 2013 at 9:21 AM, alexdeucher@gmail.com wrote:
From: Alex Deucher alexander.deucher@amd.com
These are the radeon patches for 3.11. Some of these patches are huge so, it might be easier to review things here: http://cgit.freedesktop.org/~agd5f/linux/log/?h=drm-next-3.11-wip
Updated branch: http://cgit.freedesktop.org/~agd5f/linux/log/?h=drm-next-3.11-wip-2 Takes into account comments from Jerome a Christian and contains a few DPM fixes.
I see there's a 3 now - I tested that (I guess currently it's the same as 2 anyway).
On my rv790 there are no regressions so far, but whatever I do it stays low.
This should work much better with with wip-5.
Yes, this works on my rv790 games get high and using vdpau/gl for video stays low (which is nice as the fan on my card is too noisy on high).
One thing which I guess 99.999% of people won't notice is that doing any thing with plain X + fluxbox (so no compositing) very briefly ramps up the speed.
As my fan is just audible on low but very quick to respond I can hear every time the screen gets updated eg. switching desktops, browsing+scrolling or switching tabs, even typing dmesg in an xterm which time shows as taking < 0.1 sec results in a fan change.
Hi,
Yes, this works on my rv790 games get high and using vdpau/gl for video
stays low (which is nice as the fan on my card is too noisy on high).
One thing which I guess 99.999% of people won't notice is that doing any
thing with plain X + fluxbox (so no compositing) very briefly ramps up the speed.
As my fan is just audible on low but very quick to respond I can hear
every time the screen gets updated eg. switching desktops, browsing+scrolling or switching tabs, even typing dmesg in an xterm which time shows as taking < 0.1 sec results in a fan change.
Probably a low-pass filter is needed/has to be configured in front of the freq. scaling engine?
Ilyes
dri-devel mailing list dri-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/dri-devel
dri-devel@lists.freedesktop.org